Почему функция asyn c выполняется больше времени, чем функция syn c? - PullRequest
0 голосов
/ 21 февраля 2020

Я написал две рекурсивные функции, которые суммируют числа из массива. Они делают то же самое, один асинхронно, а другой синхронно. Функция asyn c заняла около 9x времени, которое выполняла функция syn c.

Разве функция asyn c не должна использовать тот факт, что одновременно выполняется больше задач?

Функции

// Asynchronously sum the numbers in array
async function sumAsync(arr){

    if(arr.length == 1) return arr[0];

    const half = arr.length/2;

    // Numbers on the left half
    const left = arr.filter((e, i) => {
        return i < half;
    });

    // Numbers on the right half
    const right = arr.filter((e, i) => {
        return i >= half;
    });

    // Recursive call
    const leftR = sumAsync(left);
    const rightR = sumAsync(right);

    // Wait for resolves
    await Promise.all([leftR, rightR]);

    return await leftR + await rightR;
}


// Synchronously sum the numbers in array
function sumSync(arr){

    if(arr.length == 1) return arr[0];

    const half = arr.length/2;

    // Numbers on the left half
    const left = arr.filter((e, i) => {
        return i < half;
    });

    // Numbers on the right half
    const right = arr.filter((e, i) => {
        return i >= half;
    });

    // Recursive call
    const leftR = sumSync(left);
    const rightR = sumSync(right);

    return leftR + rightR;
}

Проверка их

(async () => {

    const LENGTH = 1048576; // 1024^2
    const arr = Array.from(Array(LENGTH), num => Math.random()*10 | 0);
    //  arr[1048576] <- random (0 ~ 9)

    // Async sum
    console.log('ASYNC');
    before = Date.now();
    console.log(`SUM: ${await sumAsync(arr)}`);
    after = Date.now();
    console.log(`TIME: ${after - before} ms`);

    // Sync sum
    console.log('SYNC');
    before = Date.now();
    console.log(`SUM: ${sumSync(arr)}`);
    after = Date.now();
    console.log(`TIME: ${after - before} ms`);

})();

Результаты

// ASYNC
// SUM: 4720832
// TIME: 5554 ms

// SYNC
// SUM: 4720832
// TIME: 613 ms

Ответы [ 2 ]

3 голосов
/ 21 февраля 2020

Возвращаемым значением функции async всегда является Обещание, даже если функция выполняет только синхронные операции, а await (или .then) Обещания будет выполняться только во время выполнения микрозадачи ( после завершения текущего синхронного кода). С большим массивом это приведет к большому количеству ненужных микрозадач, обертывающих синхронный код.

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

Разве функция asyn c не должна использовать тот факт, что одновременно выполняется больше задач?

Javascript однопоточный, даже с функциями async. Если одновременно вызывается несколько асин c функций, только один путь через код может быть «активным» в любой момент времени. Если общее время обработки, требуемое для всех задач, скажем, 1000 мс, в стандартном Javascript, нет смысла тратить хотя бы 1000 мс.

На самом деле больше задач не выполняется одновременно - вы просто оборачиваете задачи в Promises (без необходимости), выполняя ту же работу.

Для действительно параллельных действий вам придется использовать что-то, предоставленное вашей текущей средой, например child_process в узле или веб-работник .

2 голосов
/ 21 февраля 2020

Короткая версия: asyn c не делает больше, чем одну вещь одновременно. Он переключается между задачами (с дополнительными затратами для каждого коммутатора) в очереди, и когда одна задача блокируется, он передает управление другому (с дополнительными затратами для коммутатора и перезаписывает заблокированную задачу при разблокировании).

Длинная версия: Asyn c не означает параллельная обработка, это означает чередующаяся (одновременная, совместная) обработка. JavaScript по-прежнему однопоточен даже при использовании асинхронного c, и вся выполняемая вами работа связана исключительно с ЦП. Фактически, ваш единственный реальный параллелизм состоит в том, что асиновый код c будет планировать, приостанавливать и возобновлять ваши рекурсивные вызовы несколько раз (но все равно будет выполнять работу только по одному за раз), в то время как код syn c будет их выполнять. чтобы как можно быстрее, без участия l oop.

Преимущество асин c кода состоит в том, что при блокировке ввода / вывода (включая такие вещи, как ожидание при вводе данных пользователем), эта задача может быть приостановлена ​​до тех пор, пока она не будет заблокирована каким-либо внеполосным сигналом (ввод / вывод выполнен, пользователь щелкнул мышью и т. д.) и другие задачи не могут быть запущены. Преимущество состоит в том, чтобы пожинать преимущества параллельной (но не параллельной) обработки в ситуациях, когда большинство задач большую часть времени чего-то ждут, поэтому те немногие, которые готовы к запуску, могут начать работать немедленно (и, поскольку они обычно не работает, накладные расходы на переключение задач не имеют большого значения, в большинстве случаев переключаться не на что, большая часть накладных расходов оплачивается, когда вам нечего делать). Но это определенно выше, чем просто перехват номера без паузы.

...