Deno + многопоточность: сложение чисел с помощью рабочих машин

Недавно я начал изучать и немного экспериментировать с «новой» средой выполнения JavaScript / Typescript, Deno.

Дисклеймер/офф: и, кстати, я влюблена. Не только за то, что талисман — маленький динозавр, но и за всю скорость и легкость, которую мы получаем по сравнению с узлом. 🤪

Оценивая варианты выполнения задачи, предложенной Zanfranceschi, я наткнулся на Worker API. Я думал использовать его для вызова, но почувствовал, что это не лучший вариант решения проблемы.

В тот же день, когда я закончил hexchange, уже была начата новая задача: обработка содержимого файла распределенным способом. Это был бы «идеальный» сценарий для тестирования рабочих — хотя не обязательно распределенная обработка, но многопоточная.

Имея это в виду, я попытался придумать «простую» реализацию. Поехали! Но прежде чем я начну…

Как работники работают в Deno?

В двух словах, рабочие машины позволяют выполнять реализацию в нескольких различных потоках. Каждый экземпляр рабочего выполняется в выделенном для него потоке.

Для его выполнения необходимо иметь родителя и файл, который будет выполнять логику обработки, называемый рабочим.

Родитель

Родитель будет отвечать за создание логики для инстанцирования рабочих, а также за получение результатов их обработки.

Чтобы запросить выполнение рабочего, мы отправляем сообщение через метод postMessage, которое должно содержать все данные, подлежащие обработке.

В случае с предложенной задачей мне нужно было запросить сумму строки с 200 числами, поэтому реализация была примерно такой:

const createWorkerURL = (filename: string): string => new URL(filename, import.meta.url).href;

export function sumDistributedFileNumbers(line: string, name: string): Promise<number> {
    return new Promise((resolve) => {
        const worker = new Worker(createWorkerURL('./workers.ts'), { type: 'module' });
        worker.addEventListener('message', (message) => {
            console.log(`Total of ${message.data.sum} returned from ${message.data.name}`);
            resolve(message.data.sum);
        });
        worker.postMessage({ line, name });
    });
}
Войдите в полноэкранный режим Выход из полноэкранного режима

В данном случае, помимо обычной реализации, я использовал еще две вещи: Promises и метод addEventListener из самого Worker.

В случае с обещанием мне нужно было использовать его, чтобы убедиться, что когда моя функция вернет что-то, она уже вернет сумму всех чисел в этом ряду. Метод addEventListener, с другой стороны, я использовал для получения итоговой суммы строки — которая отправлялась через рабочий в родительский.

Обратите внимание, что для создания рабочих нам нужно передать ссылку на файл, в котором находится логика.

Рабочие

Следуя рассуждениям предложенной задачи, внутри рабочих я должен буду прочитать целую строку, содержащую в общей сложности 200 чисел. Чтобы добиться такого результата, такова была логика рабочего дела:

type MessageContent = {
    line: string;
    name: string;
}; 

self.onmessage = ({ data: { line, name } }: MessageEvent<MessageContent>): void => {
    const sum = line
        .split(' ')
        .map((num) => Number(num))
        .reduce((a, b) => {
            return a + b;
        });

    self.postMessage({ sum, name });
    self.close();
};
Войдите в полноэкранный режим Выход из полноэкранного режима

Таким образом, выполняется суммирование и, наконец, результат суммирования отправляется обратно родителю.

Окончательный результат

Для консолидации конечного результата, то есть всех частичных сумм, созданных рабочими, файл main.js получает серию обещаний и выполняет их через метод Promise.all. При успешном выполнении мы получим что-то вроде:

Total of 52499 returned from worker-1298
Total of 53023 returned from worker-1427
Total of 46830 returned from worker-1428
Total of 48380 returned from worker-1557
Total of 44707 returned from worker-1558
Total of 53450 returned from worker-1687
Total of 53031 returned from worker-1688
Total of 50385 returned from worker-1817
Total of 52455 returned from worker-1818
Total of 50101 returned from worker-1947
Total of 51728 returned from worker-1948
File total sum: 102367758!
Processing finished with 17589.7198 ms.
Войдите в полноэкранный режим Выход из полноэкранного режима

При сложности O(n²) приложению требуется в среднем ~18 секунд для выполнения суммы 20 000 строк файла, предполагая, что в каждой строке находится 200 чисел.

Следующим шагом будет выполнение ~de facto~ упражнения в соответствии с предпосылкой распределенной обработки и, наконец, сравнение его с этим упражнением.

Отказ от ответственности

Я хотел бы поблагодарить @zanfranceschi за то, что он вдохновил меня сделать первый шаг в создании контента с помощью своих задач! Большое спасибо! 😁

До следующего раза! 🤗

Оцените статью
devanswers.ru
Добавить комментарий