Подтвердить что ты не робот

Как получить согласованные критерии критерия или интерпретировать результаты в разных сериях?

Я пытаюсь оптимизировать некоторый код, используя критерий, чтобы попытаться сравнить, например, эффект добавления INLINE прагмы в функцию. Но я считаю, что результаты несовместимы между повторными компиляторами/прогонами.

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

Подробная информация о моем конкретном случае

Это ортогонально моим основным вопросам выше, но пара вещей может вызвать непоследовательность в моем конкретном случае:

  • Я пытаюсь сравнить действия IO с помощью whnfIO, потому что метод, использующий whnf в этот пример не работает.

  • В моем коде используется concurrency

  • У меня есть много вкладок и дерьмо.

Пример вывода

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

ghc --make -fforce-recomp -threaded -O2 Benchmark.hs

Первый запуск:

estimating clock resolution...                                      
mean is 16.97297 us (40001 iterations)                              
found 6222 outliers among 39999 samples (15.6%)                     
  6055 (15.1%) high severe                                          
estimating cost of a clock call...                                  
mean is 1.838749 us (49 iterations)                                 
found 8 outliers among 49 samples (16.3%)                           
  3 (6.1%) high mild                                                
  5 (10.2%) high severe                                             

benchmarking actors/insert 1000, query 1000                         
collecting 100 samples, 1 iterations each, in estimated 12.66122 s  
mean: 110.8566 ms, lb 108.4353 ms, ub 113.6627 ms, ci 0.950         
std dev: 13.41726 ms, lb 11.58487 ms, ub 16.25262 ms, ci 0.950      
found 2 outliers among 100 samples (2.0%)                           
  2 (2.0%) high mild                                                
variance introduced by outliers: 85.211%                            
variance is severely inflated by outliers                           

benchmarking actors/insert 1000, query 100000                       
collecting 100 samples, 1 iterations each, in estimated 945.5325 s  
mean: 9.319406 s, lb 9.152310 s, ub 9.412688 s, ci 0.950            
std dev: 624.8493 ms, lb 385.4364 ms, ub 956.7049 ms, ci 0.950      
found 6 outliers among 100 samples (6.0%)                           
  3 (3.0%) low severe                                               
  1 (1.0%) high severe                                              
variance introduced by outliers: 62.576%                            
variance is severely inflated by outliers

Второй запуск, ~ 3 раза медленнее:

estimating clock resolution...
mean is 51.46815 us (10001 iterations)
found 203 outliers among 9999 samples (2.0%)
  117 (1.2%) high severe
estimating cost of a clock call...
mean is 4.615408 us (18 iterations)
found 4 outliers among 18 samples (22.2%)
  4 (22.2%) high severe

benchmarking actors/insert 1000, query 1000
collecting 100 samples, 1 iterations each, in estimated 38.39478 s
mean: 302.4651 ms, lb 295.9046 ms, ub 309.5958 ms, ci 0.950
std dev: 35.12913 ms, lb 31.35431 ms, ub 42.20590 ms, ci 0.950
found 1 outliers among 100 samples (1.0%)
variance introduced by outliers: 84.163%
variance is severely inflated by outliers

benchmarking actors/insert 1000, query 100000
collecting 100 samples, 1 iterations each, in estimated 2644.987 s
mean: 27.71277 s, lb 26.95914 s, ub 28.97871 s, ci 0.950
std dev: 4.893489 s, lb 3.373838 s, ub 7.302145 s, ci 0.950
found 21 outliers among 100 samples (21.0%)
  4 (4.0%) low severe
  3 (3.0%) low mild
  3 (3.0%) high mild
  11 (11.0%) high severe
variance introduced by outliers: 92.567%
variance is severely inflated by outliers

Я замечаю, что если я масштабируюсь по "оценочной стоимости часового звонка", два теста довольно близки. Это то, что я должен сделать, чтобы получить реальное число для сравнения?

4b9b3361

Ответ 1

Хотя здесь определенно недостаточно информации, чтобы определить каждую проблему, у меня есть несколько советов, которые могут помочь.

Результаты критерия интерпретации

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

Вам может быть интересно прочитать BOS объявление критерия, в котором объясняется, как он работает довольно подробно и проходит через некоторые примеры как влияет системная нагрузка на процесс бенчмаркинга.

Я очень подозрительно отношусь к разнице в "оценочной стоимости часового звонка". Обратите внимание, что существует высокая доля выбросов (в обоих прогонах), и эти выбросы оказывают "очень сильное" воздействие. Я бы это интерпретировал как означающий, что критерий тайминга часов, который был выбран, является нежелательным (вероятно, в обоих прогонах), что делает все остальное ненадежным. Как предлагает @DanielFischer, закрытие других приложений может помочь в решении этой проблемы. В худшем случае может возникнуть проблема с оборудованием. Если вы закрываете все другие приложения, а тайминги часов по-прежнему ненадежны, вы можете протестировать другую систему.

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

Кроме того, вот две случайные идеи, которые могут быть фактором.

Загрузка процессора

Среда выполнения с потоком может быть чувствительной к загрузке процессора. Значения RTS по умолчанию хорошо работают для многих приложений, если ваша система не находится под большой нагрузкой. Проблема в том, что в сборщике мусора есть несколько критических разделов, поэтому, если время выполнения Haskell ресурс голоден (потому что он конкурирует за процессор или память с другими приложениями), весь прогресс может быть заблокирован в ожидании этих разделов. Я видел, что это влияет на производительность в 2,5 раза, что более или менее соответствует трехкратной разнице, которую вы видите.

Даже если у вас нет проблем с сборщиком мусора, высокая загрузка процессора из других приложений будет искажать ваши результаты и должна быть устранена, если это возможно.

как диагностировать

  • Используйте top или другие системные утилиты для проверки загрузки ЦП.
  • Выполнить с +RTS -s. В нижней части статики найдите эти строки.

-RTS -s output

gc_alloc_block_sync: 0
whitehole_spin: 0
gen[0].sync: 0
gen[1].sync: 0

ненулевые значения указывают на конфликт ресурсов в сборщике мусора. Большие значения здесь указывают на серьезную проблему.

как исправить

  • закрыть другие приложения
  • укажите, что ваш исполняемый файл должен использовать меньше всех ядер (например, +RTS -N6 или +RTS -N7 в 8-ядерном ящике)
  • отключить параллельную сборку мусора (с помощью +RTS -qg). У меня обычно были лучшие результаты, оставив свободное ядро, чем отключить параллельный коллектор, но YMMV.

I/O

Если функции, которые вы сравниваете, выполняют какие-либо операции ввода-вывода (диск, сеть и т.д.), вам нужно быть очень осторожным в том, как вы интерпретируете результаты. Дисковый ввод-вывод может привести к огромным отклонениям в производительности. Если вы запускаете одну и ту же функцию для 100 образцов, после первого запуска любой I/O может быть кэширован контроллером. Или вам, возможно, придется обратиться к руководителю, если к нему будет доступен другой файл. Другие операции ввода-вывода обычно не лучше.

как диагностировать

  • вы, вероятно, уже знаете, выполняет ли ваша функция I/O.
  • инструменты, такие как lsof, могут помочь отслеживать таинственную производительность ввода/вывода.

как исправить

  • mock I/O. Создайте ramdisk. Все, кроме фактического перехода на жесткий диск и т.д.
  • Если вы действительно должны тестировать реальные операции ввода-вывода, минимизируйте помехи от других приложений. Возможно, используйте специальный диск. Закройте другие приложения. Определенно собирайте несколько выборок и обратите внимание на дисперсию между ними.

Ответ 2

В Linux > 2.6 есть удобная функция под названием "cpusets", которая может использоваться для резервирования ядер процессора для определенных процессов. Конечно, это может только помочь уменьшить отклонения от использования центрального процессора, но я считаю, что это довольно эффективно.

Здесь мой текущий рабочий процесс (требуется пакет cpuset):

$ # Reserve virtual cores 0 and 1, corresponding to a full physical CPU core
$ sudo cset shield -k on -c 0-1
$ # Run benchmarks with my usual user
$ sudo cset shield --user=$USER -e -- stack bench 
$ # Release the CPU cores
$ sudo cset shield --reset

Здесь учебник для команды cset.