18

У меня есть два сценария, каждый из которых вычисляет факториал числа. Я хотел бы знать, что быстрее. Команда 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 , но я думаю, что есть лучшее решение?

5 ответов5

34

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

time for i in {1..10}; do sleep 1; done
real    0m10.052s
user    0m0.005s
sys 0m0.018s
9
python -m timeit -n 1 -r 100 -s 'import os' 'os.system("ruby fac1.rb")'

-n , -r и другие параметры см. Https://docs.python.org/2/library/timeit.html#command-line-interface.

7

есть инструмент под названием 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 
6

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

perf stat -r 10 -B sleep 1

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

1.002248382 seconds time elapsed                   ( +-  0.01% )
4

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

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

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

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

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

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

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

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

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .