Бенчмарки JS — как правильно
Большинство домашних замеров неверны. Виноваты: непрогретый JIT, dead code elimination, аллокация в одной итерации, шум GC, неверная мера времени.
5 правил адекватного бенчмарка
- Прогрей JIT — несколько тысяч итераций до начала замера, чтобы TurboFan успел.
- Не давай V8 выкинуть результат — сохраняй в переменную/массив, иначе dead code elimination оптимизирует всё в
noop. - Большие данные не аллоцируй в той же итерации, что и замер — это будет тест аллокатора, а не алгоритма.
- Меряй много раз и смотри распределение, не среднее. Используй
console.timeилиperformance.now(), лучше Benchmark.js. - Изолируй deopt через
%ClearFunctionFeedback/прогрев.
Минимальный шаблон
function bench(fn, iters = 1e6) {
// warmup
for (let i = 0; i < 1000; i++) fn;
const t = performance.now();
let sink;
for (let i = 0; i < iters; i++) sink = fn;
return { ms: performance.now() - t, sink };
}
Чужие бенчмарки врут
«Прежде чем мы перейдем к замерам, я хотел бы продемонстрировать пруф того, о чем я рассказывал.»
Спикер AsForJS неоднократно показывал, что код в тестах подписчиков измеряет не то, что они думают:
- Замеряют создание массива вместо обхода
- Сравнивают
MapvsObjectбез прогрева - Меряют 1 итерацию в наносекундах
Подводные камни
console.timeточен до миллисекунд.performance.now()— до микросекунд, но не наносекунд.- Между прогонами очищай
--isolates(новый процесс Node). - Один и тот же код в Chrome / d8 / Node даёт разные числа.
Источники
- JS Performance: Как правильно оценить эффективность JS кода (perf 05) · AsForJS · 2023-10-22
- JavaScript Tools: Performance Evaluation (devices04) · AsForJS · 2023-12-04
- Введение в производительность JavaScript кода · AsForJS · 2023-06-07
- Измерение производительности кода и оптимизация в JavaScript и Node.js · 2018-10-24