Как написать правильный микро-бенчмарк на Java?
Как вы пишете (и запускаете) правильный микро-бенчмарк в Java?
Я смотрю здесь примеры кода и комментарии, иллюстрирующие различные вещи, чтобы думать.
пример: должен ли эталон измерять время / итерацию или итерации / время и почему?
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.
Я знаю, что этот вопрос был отмечен как ответ, но я хотел бы упомянуть две библиотеки, которые позволяют нам писать микро-тесты
учебники для начинающих
учебники для начинающих
важными вещами для тестов 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