Как измерить среднее время выполнения скрипта?

23

У меня есть два сценария, каждый из которых вычисляет факториал числа. Я хотел бы знать, что быстрее. Команда timeдает мне миллисекунды, и время от времени результат отличается:

piousbox@piousbox-laptop:~/projects/trash$ time ruby fac2.rb
30414093201713378043612608166064768844377641568960512000000000000

real    0m0.089s
user    0m0.052s
sys 0m0.028s
piousbox@piousbox-laptop:~/projects/trash$ time ruby fac1.rb
30414093201713378043612608166064768844377641568960512000000000000

real    0m0.091s
user    0m0.048s
sys 0m0.036s
piousbox@piousbox-laptop:~/projects/trash$ time ruby fac1.rb
30414093201713378043612608166064768844377641568960512000000000000

real    0m0.088s
user    0m0.048s
sys 0m0.040s
piousbox@piousbox-laptop:~/projects/trash$ time ruby fac2.rb
30414093201713378043612608166064768844377641568960512000000000000

real    0m0.088s
user    0m0.048s
sys 0m0.028s
piousbox@piousbox-laptop:~/projects/trash$ time ruby fac1.rb
30414093201713378043612608166064768844377641568960512000000000000

real    0m0.087s
user    0m0.064s
sys 0m0.028s
piousbox@piousbox-laptop:~/projects/trash$ time ruby fac2.rb
30414093201713378043612608166064768844377641568960512000000000000

real    0m0.089s
user    0m0.068s
sys 0m0.016s
piousbox@piousbox-laptop:~/projects/trash$ 

Как мне взять среднее время, необходимое для запуска скрипта? Я мог бы проанализировать и усреднить результат 100 time, но я думаю, что есть лучшее решение?

Виктор Piousbox
источник
1
stackoverflow.com/questions/8215482/…
Сиро Сантилли 新疆 改造 中心 法轮功 六四 事件

Ответы:

4

Нет, ваша идея усреднения верна.

Выполнение сценария зависит от множества факторов, и, тем не менее, оно должно быть разделено между временем установки (загрузка интерпретатора в память, настройка и, возможно, компиляция кода в байт-код или машинный код) и истинным временем выполнения.

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

Чтобы сосредоточиться на общем времени, вы выполняете скрипт сто раз и усредняете результаты. В идеале, вы должны разделить эти исполнения настолько, чтобы система каждый раз возвращалась в «эталонное состояние» (или состояние, не связанное со сценарием). Например, сам интерпретатор будет кэшироваться в памяти, так что самое первое выполнение скрипта будет заметно медленнее, чем последующие.

Чтобы лучше понять алгоритм, я думаю, что лучший способ - это что-то вроде этого (на другом простаивающем компьютере):

  • обернуть алгоритм в одну функцию.
  • в управляющем приложении:
    • вызвать функцию один раз
    • получить системное («настенные часы») время и добавить 10 (или разумное N) секунд
    • войти в цикл и начать считать итерации
    • после каждого вызова функции увеличивайте счетчик
    • если системное время ниже сохраненного времени, сделайте еще один цикл
    • получить точное N, возможно, с плавающей точкой, из текущего времени настенных часов
    • отобразить счетчик, деленный на N: это количество итераций в секунду.

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

Если функция получает входные данные, вы должны предоставить случайную последовательность входных данных, используя PRNG, засеянный с фиксированным значением, чтобы обе версии тестируемой функции получали одинаковые значения. Это позволяет избежать одной функции, выполняющей, по- видимому, лучше из-за «счастливых чисел» (например, я помню вариант алгоритма Хиллсорта, который работал заметно лучше, если число элементов, подлежащих сортировке, было в форме 2 k -1 с малыми k s).

LSerni
источник
Хорошо, спасибо. Я заметил, что последующие звонки становятся короче. Теперь я запустил цикл внутри скриптов и обнаружил, что один алгоритм определенно быстрее другого.
Виктор Piousbox
39

Вы можете запускать итерации программы в цикле; и разделите общее время на количество итераций:

time for i in {1..10}; do sleep 1; done
real    0m10.052s
user    0m0.005s
sys 0m0.018s
Кент
источник
2
Супер просто, люблю это. Я также никогда не видел {1..10}прежде, и сбит с толку, что это работает, не могу найти это в руководстве по bash. Печально только то, что вы не знаете, как распределяются ваши результаты (минимальное и максимальное время).
w00t
@ w00t:man -P 'less +/Brace\ Expansion' bash
user2683246 10.09.15
Спасибо @ user2683246! Затем я также нашел его по адресу gnu.org/software/bash/manual/bash.html#Brace-Expansion - хорошее использование less между прочим. Теперь мне также любопытно, когда это появилось в bash…
w00t
1
Ага, версия 3, через 10 лет после того, как я начал использовать bash :) tldp.org/LDP/abs/html/bashver3.html
w00t
2
Если это не работает для прибывающих Googlers, это может быть потому, что вы не работаете bash. Попробуйте запустить /bin/bashдо этого.
Кори Кляйн
14

есть инструмент под названием multitime, который делает именно это: несколько раз запускает команду, измеряя, сколько времени это займет (реальное / пользователь / система с автоматически вычисленными средним, минимальным / максимальным и средним временем)

Например, для измерения аналогичного сценария 100 раз:

multitime -q -n 100 "fact1.sh"
===> multitime results
1: -q fact1.sh
            Mean        Std.Dev.    Min         Median      Max
real        0.122       0.032       0.086       0.116       0.171       
user        0.148       0.044       0.096       0.137       0.223       
sys         0.023       0.019       0.000       0.014       0.061 
Кирилл Шабойсо
источник
12

Это старый, но он поднялся так высоко в Google, когда я искал команду, которую использовал ранее, но не смог найти. В любом случае, мой предпочтительный способ сделать это:

perf stat -r 10 -B sleep 1

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

1.002248382 seconds time elapsed                   ( +-  0.01% )
Zaahid
источник
1

Hyperfine это еще один вариант.

Пример использования:

hyperfine --warmup 3 'ruby fac1.rb'
Луис Бьянчин
источник
1
Сверхтонкий настолько лучше, чем другие альтернативы, что это смешно. Обнаружение необходимых прогонов, прогрев, прекрасный вывод, отчеты об уценке, написанные на ржавчине и т. Д.
Клас Меллборн