Сделайте потоки Javascript быстрыми - PullRequest
0 голосов
/ 21 декабря 2018

Недавно я пытался использовать интерфейс Web-работники для экспериментов с потоками в JavaScript.

При попытке сделать содержащим с веб-работниками, выполнив следующие действия:

  • Разделить начальный массив на куски одинакового размера
  • Создать веб-работника для каждого кусочка, который запускает .contains на этом кусочке
  • Когда и если значение найдено в какой-либо части, оно возвращает значение true, не дожидаясь завершения всех рабочих.

Вот что я пытался:

var MAX_VALUE = 100000000;
var integerArray = Array.from({length: 40000000}, () => Math.floor(Math.random() * MAX_VALUE));

var t0 = performance.now();
console.log(integerArray.includes(1));
var t1 = performance.now();
console.log("Call to doSomething took " + (t1 - t0) + " milliseconds.");

var promises = [];
var chunks = [];
while(integerArray.length) {
    chunks.push(integerArray.splice(0,10000000));
}

t0 = performance.now();
chunks.forEach(function(element) {
    promises.push(createWorker(element));
});

function createWorker(arrayChunk) {
    return new Promise(function(resolve) {
        var v = new Worker(getScriptPath(function(){
            self.addEventListener('message', function(e) {
                var value = e.data.includes(1);
                self.postMessage(value);
            }, false);
        }));
        v.postMessage(arrayChunk);
        v.onmessage = function(event){
            resolve(event.data);
        };
    });
}

firstTrue(promises).then(function(data) {
    // `data` has the results, compute the final solution
    var t1 = performance.now();
    console.log("Call to doSomething took " + (t1 - t0) + " milliseconds.");
});

function firstTrue(promises) {
    const newPromises = promises.map(p => new Promise(
        (resolve, reject) => p.then(v => v && resolve(true), reject)
));
   newPromises.push(Promise.all(promises).then(() => false));
    return Promise.race(newPromises);
}

//As a worker normally take another JavaScript file to execute we convert the function in an URL: http://stackoverflow.com/a/16799132/2576706
function getScriptPath(foo){ return window.URL.createObjectURL(new Blob([foo.toString().match(/^\s*function\s*\(\s*\)\s*\{(([\s\S](?!\}$))*[\s\S])/)[1]],{type:'text/javascript'})); }

Любой браузер и процессор пытались работать очень медленно по сравнению с простым , содержащим в исходном массиве.

Почемуэто так медленно ?Что не так с кодом выше?

Ссылки

Редактировать: Проблема не в конкретных .contains (), а в других функциях массива, например .indexOf (), .map (), forEach () и т. д. Почему разделение работы между веб-работниками занимает гораздо больше времени ...

Ответы [ 2 ]

0 голосов
/ 29 декабря 2018

Это немного надуманный пример, поэтому трудно помочь оптимизировать то, что вы пытаетесь сделать конкретно, но один легко пропускаемый и исправляемый медленный путь - это копирование данных на веб-работника.Если возможно, вы можете использовать ArrayBuffers и SharedArrayBuffers для быстрой передачи данных веб-работникам и от них.

Вы можете использовать второй аргумент функции postMessage для передачи владения arrayBuffer веб-работнику.Важно отметить, что этот буфер больше не будет использоваться основным потоком, пока он не будет передан обратно веб-работником.SharedArrayBuffers не имеют этого ограничения и могут быть прочитаны многими работниками одновременно, но не обязательно поддерживаются во всех браузерах из-за проблем безопасности (см. mdn для более подробной информации )

Например

const arr = new Float64Array(new ArrayBuffer(40000000 * 8));

console.time('posting');
ww.postMessage(arr, [ arr.buffer ]);
console.timeEnd('posting');

требует ~ 0,1 мс для запуска, в то время как

const arr = new Array(40000000).fill(0);

console.time('posting');
ww.postMessage(arr, [ arr ]);
console.timeEnd('posting');

требует ~ 10000 мс для запуска.Это просто для передачи данных в сообщении, а не для запуска самой рабочей логики.

Подробнее об аргументе postMessage TransferList здесь и переносимых типах здесь * 1020 можно прочитать подробнее*.Важно отметить, что способ, которым ваш пример выполняет сравнение времени, также включает время создания веб-работника, но, надеюсь, это даст лучшее представление о том, куда уходит много этого времени и как его можно лучше обойти.

0 голосов
/ 21 декабря 2018

Вы делаете намного больше работы между t0 и t1 по сравнению с простым contains.Эти дополнительные шаги включают в себя:

  1. функция преобразования -> строка -> регулярное выражение -> blob -> URL объекта
  2. вызов нового работника -> анализ URL объекта -> JS-механизм интерпретирует код
  3. отправка обработанных веб-данных -> сериализовано в главном потоке -> десериализовано в работнике (вероятно, в структуре памяти, которая на самом деле копируется, поэтому не супер медленная)

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

например, массив ['apple', 'coconut', 'kiwi'] будет преобразован в { apple: 1, coconut: 2, kiwi:3 } поиск по карте будет происходить в обычном амортизированном времени (быстро), в то время как массив будет линейным (медленно, как ад) для больших наборов).

...