Где я могу поставить вызовы performance.now()?

Я написал небольшой скрипт для сравнения реализаций функций по времени выполнения. Он выполняет functon, например, 1** раз и суммирует время выполнения для каждого вызова. Я озадачен тем, где именно я должен разместить вызовы performance.now(). Первый вариант заключается в том, чтобы помещать их в цикл for прямо перед и после вызова функции, а затем просто подводить итоги после каждой итерации. Второй вариант - поставить вызовы performance.now() прямо до и после самого цикла. Первый сценарий кажется более точным, но на моей машине он дает конечный результат, который на 30% больше, чем второй вариант стабильно. Может ли кто-нибудь объяснить мне, почему? Какое правильное место для performance.now()?

Первый вариант - performance.now() вызывает внутри цикла. Он дает около 1300 мс для вычисления факториала 150 1** раз на моей машине:

function factorial(x) {
 return x === 1 ? 1 : x * factorial(x - 1); 
}

function measure(func, arg, times) {
 let timeSpent = 0;
 for(var i = times;i--;) {
 let t1 = performance.now(); 
 func(arg);
 let t2 = performance.now();
 timeSpent += t2 - t1;
 }
 console.log('Tested function ${func.name} has been executed ${times} times with argument ${arg} and it has taken %d ms. in total.', timeSpent);
}

measure(factorial, 150, 1**);

Второй вариант - performance.now() вызывает вне цикла. Он дает около 1000 мс для вычисления факториала 150 1** раз на моей машине:

function factorial(x) {
 return x === 1 ? 1 : x * factorial(x - 1); 
 }

 function measure(func, arg, times) {
 let t1 = performance.now();
 for(var i = times;i--;) func(arg);
 let t2 = performance.now();
 console.log('Tested function ${func.name} has been executed ${times} times with argument ${arg} and it has taken %d ms. in total.', t2 - t1);
 }

 measure(factorial, 150, 1**);
1 ответ

Когда я выполняю измерения производительности, я считаю несколько вещей:

  • какова точность обслуживания времени, которое я использую, и возвращает ли оно "время настенных часов" или "время процессора",

  • какую степень точности мне нужно получить от измерения (1%,.1%, 10%?)

  • сколько времени я хочу потратить на выполнение работы?

  • мне нужно "время прогрева" до того, как я сделаю измерение (например, если я измеряю, сколько времени занимает транзакция для запуска, и я использую JIT-язык, такой как Java или javascript, тогда я, возможно, захочу выбросить тайминги для первых N результатов. С другой стороны, если я измеряю "пакетную" программу - что-то, что только запускается один раз - тогда мне не нужно это делать).

Как правило, службы времени имеют где-то между сотнями микросекунд до нескольких миллисекунд. Итак, вы хотите, чтобы вы делали достаточно циклов вокруг того, что вы измеряете, чтобы вы не беспокоились о своей временной функции, вызывающей у вас горе. Итак, если бы у меня был таймер, который был бы точным (скажем) 100us, то я бы удостоверился, что я провел достаточно циклов, чтобы взять как минимум 1000us (1 мс) и, возможно, 10 мс.

Есть также много вещей, которые вызывают дрожание при выполнении измерений производительности. джиттер - неизбежная изменчивость, которую вы получаете на практике при выполнении измерений производительности. Итак, я бы также повторил, что ваша производительность работает несколько раз, и посмотрите на среднее, std отклонение прогонов, чтобы увидеть, насколько согласованы ваши результаты (это также может помочь осветить такие вещи, как JIT-компиляторы, так как вы получите некоторые действительно резкие выбросы, когда будете выглядеть по вашим результатам).

Чтобы ответить на ваш вопрос, я бы пошел с вашим вторым вариантом. Вы хотите, чтобы у вас было достаточно работы, чтобы точность вашего обслуживания времени не была проблемой.

licensed under cc by-sa 3.0 with attribution.