Почему обратный вызов Array.from намного медленнее, чем функция карты?

Выполняя сегодня некоторые тесты производительности, я обнаружил, что обратный вызов Array.from работает довольно медленнее, чем независимый запуск функции Array.map.

Тесты проводились на длинном массиве из 32000000 элементов следующим образом.

let t1;
const arr = Array.from(new Array(32000000), (v,i) => {
    if (i === 0) t1 = performance.now();
    return i;
});
let t2 = performance.now();

t2 - t1; // ~4500ms


let arr2 = Array.from(new Array(32000000));
arr2 = arr2.map((v,i) => {
    if (i === 0) t1 = performance.now();
    return i;
});
t2 = performance.now();

t2 - t1; // ~500ms

Я всегда думал, что Array.from просто запускает функцию map при создании массива. Код полифиллов тоже выглядит одинаково. Есть идеи, почему такая разница в производительности?

Протестировано в Google Chrome 74.0.3729.157, macOS

Совет: в случаях измерения производительности всегда старайтесь запускать тесты в разном порядке, поскольку на результат могут повлиять факторы, находящиеся вне вашего контроля (например, текущее состояние кеша). В вашем примере попробуйте запустить второй блок кода перед первым блоком кода.

goodvibration 21.05.2019 22:04

Спасибо, что указали на это. Конечно, я сделал это, просто забыл упомянуть об этом. Разницы в результатах нет.

Dawid Zbiński 21.05.2019 22:05

Я не понимаю, почему вы изменили свои тесты, включая ненужный if. Вы можете безопасно установить таймер перед запуском теста. Кроме того, ваш вопрос связан с движком javascript вашего браузера. Вы должны запустить его в нескольких браузерах (и версиях, если они доступны) и на нескольких платформах. В текущей форме ваш вопрос плохо помечен и не имеет ответа.

tao 21.05.2019 22:08

вы можете попробовать добавить третий формальный параметр к обратному вызову from(). я знаю, что map() долгое время оптимизировалась, чтобы работать быстрее только тогда, когда переменные аргументы соответствовали формальным параметрам. возможно, они не/не могут оптимизировать from() таким же образом, как в конечном итоге подправили [].map().

dandavis 21.05.2019 22:33
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
4
480
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Медленная не функция отображения, это сам вызов Array.from.

Если мы удалим все ненужные переменные и условные операторы, мы получим следующий упрощенный тест, который возвращает:

Хром 74:

Array.from with map: 4735.970ms
Array.map: 166.405ms
Array.map with from: 5101.585ms
Array.from: 4999.910ms

Фаерфокс 67:


Array.from with map: 729.000ms
Array.map: 41.000ms
Array.map with from: 1150.000ms
Array.from: 619.000ms

Таким образом, мы видим, что сопоставление на самом деле почти не занимает времени, все, что занимает основное время, — это вызов Array.from.

Я предполагаю, что Array.from намного медленнее выделяет память и создает новый массив, чем тот, который возвращает Array.map, просто потому, что он намного более общий и сложный, чем функция карты. Просто сравните их спецификации: Array.prototype.map против Массив.от, Array.from кажется, намного сложнее оптимизировать компилятор.

const arr = new Array(32000000);

console.time('Array.from with map');
const arr1 = Array.from(arr, (v,i) => {
    return i;
});
console.timeEnd('Array.from with map');


console.time('Array.map');
const arr2 = arr.map((v,i) => {
    return i;
});
console.timeEnd('Array.map');



console.time('Array.map with from');
const arr3 = Array.from(arr).map((v,i) => {
    return i;
});
console.timeEnd('Array.map with from');

console.time('Array.from');
const arr4 = Array.from(arr);
console.timeEnd('Array.from');
Array.from вызывает функцию сопоставления при создании каждого элемента, тогда как функция сопоставления повторяет каждый элемент после создания массива. Итак, никакой разницы в обратных вызовах нет вообще, единственное, что Array.from вызывает его во время создания массива (что занимает некоторое время), а map просто повторяет уже существующий.
Dawid Zbiński 22.05.2019 11:12

Другие вопросы по теме