Выполняя сегодня некоторые тесты производительности, я обнаружил, что обратный вызов 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
Спасибо, что указали на это. Конечно, я сделал это, просто забыл упомянуть об этом. Разницы в результатах нет.
Я не понимаю, почему вы изменили свои тесты, включая ненужный if
. Вы можете безопасно установить таймер перед запуском теста. Кроме того, ваш вопрос связан с движком javascript вашего браузера. Вы должны запустить его в нескольких браузерах (и версиях, если они доступны) и на нескольких платформах. В текущей форме ваш вопрос плохо помечен и не имеет ответа.
вы можете попробовать добавить третий формальный параметр к обратному вызову from(). я знаю, что map() долгое время оптимизировалась, чтобы работать быстрее только тогда, когда переменные аргументы соответствовали формальным параметрам. возможно, они не/не могут оптимизировать from() таким же образом, как в конечном итоге подправили [].map().
Медленная не функция отображения, это сам вызов 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 просто повторяет уже существующий.
Совет: в случаях измерения производительности всегда старайтесь запускать тесты в разном порядке, поскольку на результат могут повлиять факторы, находящиеся вне вашего контроля (например, текущее состояние кеша). В вашем примере попробуйте запустить второй блок кода перед первым блоком кода.