Как написать правильный микро-бенчмарк на Java?



Как вы пишете (и запускаете) правильный микро-бенчмарк в Java?



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



пример: должен ли эталон измерять время / итерацию или итерации / время и почему?



по теме: является ли бенчмаркинг секундомера приемлемым?

842   11  

11 ответов:

советы о написании микро тестов от создателей Java HotSpot:

правило 0: прочитайте авторитетный документ по JVMs и микро-бенчмаркингу. Хороший - это Brian Goetz, 2005. Не ожидайте слишком многого от микро-бенчмарков; они измеряют только ограниченный диапазон характеристик производительности JVM.

Правило 1: всегда включайте фазу прогрева, которая запускает ваше тестовое ядро полностью, достаточно, чтобы запуск всех инициализаций и компиляций перед фазой(фазами) синхронизации. (Меньшее количество итераций нормально на этапе прогрева. Эмпирическое правило-это несколько десятков тысяч итераций внутреннего цикла.)

Правило 2: всегда запускать с -XX:+PrintCompilation,-verbose:gc и т. д. таким образом, вы можете убедиться, что компилятор и другие части JVM не выполняют неожиданную работу во время фазы синхронизации.

правило 2.1: печать сообщений в начале и конце синхронизации и фазы прогрева, так что вы можете проверить, что нет никакого выхода из правила 2 во время фазы синхронизации.

Правило 3: имейте в виду разницу между-клиентом и-сервером, а также OSR и регулярными компиляциями. Элемент -XX:+PrintCompilation флаг сообщает компиляции OSR со знаком at для обозначения не начальной точки входа, например:Trouble::run @ 2 (41 bytes). Предпочитайте сервер клиенту, а обычный OSR, если вы после лучшей производительности.

Правило 4: будьте в курсе эффекты инициализации. Не печатайте в первый раз во время фазы синхронизации, так как печать загружает и инициализирует классы. Не загружайте новые классы вне фазы прогрева (или заключительной фазы отчетности), если вы не тестируете загрузку класса специально (и в этом случае загружайте только тестовые классы). Правило 2 - это ваша первая линия защиты от таких эффектов.

Правило 5: имейте в виду эффекты деоптимизации и перекомпиляции. Не принимайте никакого пути кода для первый раз на этапе синхронизации, потому что компилятор может отказаться и перекомпилировать код, основанный на более раннем оптимистическом предположении, что путь вообще не будет использоваться. Правило 2 - это ваша первая линия защиты от таких эффектов.

Правило 6: используйте соответствующие инструменты, чтобы читать мысли компилятора и ожидать, что он будет удивлен кодом, который он производит. Проверьте код самостоятельно, прежде чем формировать теории о том, что делает что-то быстрее или замедлившийся.

Правило 7: уменьшить шум в ваших измерениях. Запустите свой бенчмарк на тихой машине и запустите его несколько раз, отбрасывая выбросы. Используйте -Xbatch чтобы сериализовать компилятор с приложением, и рассмотреть возможность установки -XX:CICompilerCount=1 чтобы компилятор не работал параллельно с самим собой. Попробуйте лучше сократить затраты на сборку мусора, установить Xmx(достаточно большие) равна Xms и использовать UseEpsilonGC если он доступен.

правила 8: используйте библиотеку для вашего бенчмарка, поскольку она, вероятно, более эффективна и уже отлажена для этой единственной цели. Например,JMH,регулировка или отличные тесты UCSD Билла и пола для Java.

Я знаю, что этот вопрос был отмечен как ответ, но я хотел бы упомянуть две библиотеки, которые позволяют нам писать микро-тесты

суппорт от Google

учебники для начинающих

  1. http://codingjunkie.net/micro-benchmarking-with-caliper/
  2. http://vertexlabs.co.uk/blog/caliper

JMH от Теперь

учебники для начинающих

  1. избегая провалов бенчмаркинга на JVM
  2. http://nitschinger.at/Using-JMH-for-Java-Microbenchmarking
  3. http://java-performance.info/jmh/

важными вещами для тестов Java являются:

  • сначала разогрейте JIT, запустив код несколько раз, прежде чем синхронизировать его
  • убедитесь, что вы запустите его достаточно долго, чтобы иметь возможность измерить результаты в секундах или (лучше) десятки секунд
  • пока вы не можете назвать System.gc() между итерациями, это хорошая идея, чтобы запустить его между тестами, так что каждый тест, мы надеемся получить "чистое" пространство памяти для работы. (Да,gc() - это больше намек, чем гарантия, но это очень скорее что это действительно будет мусор собирать в моем опыте.)
  • мне нравится отображать итерации и время, а также счет времени/итерации, который может быть масштабирован таким образом, что "лучший" алгоритм получает оценку 1.0, а другие оцениваются относительным образом. Это означает, что вы можете запустить все алгоритмы в течение длительного времени, варьируя как количество итераций, так и время, но все же получая сопоставимые результаты.

Я просто в процессе ведения блога о разработке бенчмаркинг-фреймворка в. NET. i've got a пара на ранее должности

jmh является недавним дополнением к OpenJDK и был написан некоторыми инженерами производительности из Oracle. Конечно, стоит посмотреть.

JMH-это Java-жгут для построения, запуска и анализа тестов nano/micro/macro, написанных на Java и других языках, предназначенных для JVM.

очень интересные куски информации похоронены в тесты комментарии.

посмотреть также:

должен ли тест измерять время / итерацию или итерации / время и почему?

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

убедитесь, что вы каким-то образом использовать результаты, которые вычисляются в сравнении код. В противном случае ваш код может быть оптимизирован.

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

for(i=1..n)
  alg1();
for(i=1..n)
  alg2();
for(i=1..n)
  alg2();
for(i=1..n)
  alg1();

Я нашел некоторые заметные различия (иногда 5-10%) во время выполнения одного и того же алгоритма в разных проходах..

кроме того, убедитесь, что n очень большой, так что время выполнения каждого цикла составляет не менее 10 секунд или около того. Чем больше итераций, тем более значимые цифры в вашем тестовом времени и тем больше достоверных данных.

есть много возможных подводных камней для написания микро-тестов в Java.

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

во-вторых: вы не можете доверять точности измеренных времен для очень коротких интервалов.

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

мои рекомендации: Сделайте ваш тест выполняется несколько секунд, что является более надежным, чем время выполнения в течение миллисекунд. Разогрейте JVM (означает запуск бенчмарка хотя бы один раз без измерения, что JVM может запускать оптимизации). И запустите свой бенчмарк несколько раз (возможно, 5 раз) и возьмите медианное значение. Запустите каждый микро-бенчмарк в новом JVM-экземпляре (вызовите для каждого бенчмарка новую Java) в противном случае эффекты оптимизации JVM могут повлиять позже ходовые испытания. Не выполняйте вещи, которые не выполняются в фазе прогрева (так как это может вызвать загрузку класса и перекомпиляцию).

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

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

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

http://opt.sourceforge.net/ Java Micro Benchmark - задачи управления, необходимые для определения сравнительных характеристик производительности компьютерной системы на разных платформах. Может использоваться для руководства решениями оптимизации и сравнения различных реализаций Java.

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

для некоторых процессоров (например, Intel Core i5 диапазон с TurboBoost), температура (и количество ядер в настоящее время используется, а также их процент использования) влияет на тактовую частоту. Поскольку процессоры динамически синхронизируются, это может повлиять на ваши результаты. Например, если у вас есть однопоточное приложение, максимальная тактовая частота (с TurboBoost) выше, чем для приложения, использующего все ядра. Таким образом, это может помешать сравнениям однопоточной и многопоточной производительности в некоторых системах. Имейте в виду, что температура и летучесть также влияют на то, как долго поддерживается частота турбонаддува.

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

long startTime = System.nanoTime();
//code here...
System.out.println("Code took "+(System.nanoTime()-startTime)+"nano seconds");

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

final long endTime, startTime = System.nanoTime();
//code here...
endTime = System.nanoTime();
System.out.println("Code took "+(endTime-startTime)+"nano seconds");

Comments

    Ничего не найдено.