Получение точного измерения времени с помощью `perf-stat` - PullRequest
0 голосов
/ 29 апреля 2019

Я пытаюсь сравнить простые программы "Здравствуй, мир", написанные на нескольких языках.Я n00b в этой области, и руководство perf-stat не облегчает задачу из-за отсутствия примеров.

Для этого я запускаю perf-stat с высоким приоритетом, чтобы избежать переключения контекста.Поэтому я придумываю что-то вроде этого:

sudo chrt -f 99 perf stat -e cs -e cpu-clock ./hello_c

Однако результаты, полученные для одной и той же программы, существенно различаются.Например, результат для того же самого скомпилированного исполняемого файла C может быть:

     0      cs                        #    0.000 K/sec                  
  0.42 msec cpu-clock                 #    0.612 CPUs utilized          

0.000694107 seconds time elapsed
0.000713000 seconds user
0.000000000 seconds sys

или

     0      cs                        #    0.000 K/sec                  
  0.58 msec cpu-clock                 #    0.620 CPUs utilized          

0.000936635 seconds time elapsed
0.000000000 seconds user
0.000940000 seconds sys

В этом конкретном примере существует 0.242528 msec несовместимости, даже если переключатели контекста находятся в обоихпримеры равны 0.

Что-то мне не хватает, какие вычисления мне нужно сделать?Или невозможно приблизиться к результатам?Есть ли другие варианты решения этой проблемы, кроме получения среднего числа n выполнений?

1 Ответ

3 голосов
/ 29 апреля 2019

Существует множество причин, по которым вы можете увидеть различия, когда вы неоднократно сравниваете то, что кажется одним и тем же кодом. Я рассмотрел некоторые причины в другом ответе , и было бы целесообразно помнить об этом.

Однако, основываясь на опыте и использовании вероятностей, мы можем устранить многие из них заранее. То, что осталось, - это наиболее вероятные причины ваших относительно больших отклонений для коротких программ с холодного старта:

  1. Функции энергосбережения ЦП и масштабирования частоты.
  2. Фактические различия в поведении во время выполнения, т. Е. Разный код, выполняемый в библиотеке времени выполнения, ВМ, ОС или другой вспомогательной инфраструктуре при каждом запуске вашей программы.
  3. Некоторый эффект кэширования, или эффект выравнивания кода или данных, который варьируется от запуска к запуску.

Вероятно, вы можете разделить эти три эффекта простым perf stat без переопределения списка событий, например:

$ perf stat true

 Performance counter stats for 'true':

          0.258367      task-clock (msec)         #    0.427 CPUs utilized          
                 0      context-switches          #    0.000 K/sec                  
                 0      cpu-migrations            #    0.000 K/sec                  
                41      page-faults               #    0.159 M/sec                  
           664,570      cycles                    #    2.572 GHz                    
           486,817      instructions              #    0.73  insn per cycle         
            92,503      branches                  #  358.029 M/sec                  
             3,978      branch-misses             #    4.30% of all branches        

       0.000605076 seconds time elapsed

Сначала посмотрите на строку 2.572 GHz. Показывает эффективную частоту ЦП, вычисляя путем деления истинного количества циклов ЦП на значение task-clock (время ЦП, затраченное программой). Если это изменяется от запуска к запуску, отклонение производительности настенного времени частично или полностью объясняется этим изменением, и наиболее вероятной причиной является (1) выше, т.е. масштабирование частоты ЦП, включая оба масштабирования ниже номинальной частоты (энергосбережение) ) и выше (турбонаддув или аналогичные функции).

Детали отключения масштабирования частоты зависят от аппаратного обеспечения, но наиболее распространенным, который работает в большинстве современных дистрибутивов Linux, является cpupower -c all frequency-set -g performance, чтобы запретить масштабирование ниже номинального.

Отключение Turbo Boost является более сложным и может зависеть от аппаратной платформы и даже конкретного процессора, но для последних версий x86 некоторые параметры включают:

  • Запись 0 в /sys/devices/system/cpu/intel_pstate/no_turbo (только для Intel)
  • Выполнение wrmsr -p${core} 0x1a0 0x4000850089 для каждого ${core} в вашей системе (хотя одного на каждый сокет, вероятно, достаточно для некоторых / большинства / всех чипов?). (Только для Intel)
  • Отрегулируйте значение /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq, чтобы установить максимальную частоту.
  • Используйте регулятор userspace и /sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed для установки фиксированной частоты.

Другой вариант - просто несколько раз запустить тест и надеяться, что процессор быстро достигнет устойчивого состояния. perf stat имеет встроенную поддержку для этого с опцией --repeat=N:

   -r, --repeat=<n>
       repeat command and print average + stddev (max: 100). 0 means forever.

Допустим, вы заметили, что частота всегда одинакова (в пределах 1% или около того), или вы исправили проблемы с частотой, но некоторая разница остается.

Далее проверьте строку instructions. Это приблизительный показатель того, сколько работы в целом выполняет ваша программа. Если он изменяется в одном и том же направлении и аналогичен относительной дисперсии к вашей дисперсии времени выполнения, у вас есть проблема типа (2): некоторые прогоны выполняют больше работы, чем другие. Не зная, что ваша программа, было бы трудно сказать больше, но вы можете использовать такие инструменты, как strace, perf record + perf annotate, чтобы отследить это.

Если instructions не меняется, а частота фиксирована, но время выполнения меняется, у вас проблема типа (3) или «другое». Вы захотите взглянуть на большее количество счетчиков производительности, чтобы увидеть, какие из них коррелируют с более медленными запусками: у вас больше ошибок в кеше? Еще контекстные переключатели? Больше отраслевых заблуждений? Список можно продолжить. Как только вы узнаете, что вас тормозит, вы можете попытаться изолировать код, который его вызывает. Вы также можете пойти в другом направлении: используя традиционное профилирование, чтобы определить, какая часть кода замедляется при медленном запуске.

Удачи!

...