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

Feb 03 2009

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

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

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

Связано: приемлемо ли тестирование секундомера?

Ответы

802 12revs,12users61%EugeneKuleshov Feb 05 2009 at 03:49

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

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

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

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

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

Правило 3. Помните о разнице между -clientи -server, OSR и обычными компиляциями. -XX:+PrintCompilationФлаг сообщает ЛРН компиляций с при-знаком для обозначения без начальной точки входа, например: Trouble$1::run @ 2 (41 bytes). Предпочитайте сервер клиенту и регулярный OSR, если вам нужна максимальная производительность.

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

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

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

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

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

244 AravindYarram Dec 19 2010 at 06:35

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

Штангенциркуль от Google

Учебники по началу работы

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

JMH из OpenJDK

Учебники по началу работы

  1. Как избежать ошибок при сравнительном анализе на JVM
  2. Использование JMH для микробенчмаркинга Java
  3. Введение в JMH
88 JonSkeet Feb 03 2009 at 00:46

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

  • Сначала разогрейте JIT, запустив код несколько раз, прежде чем рассчитывать время.
  • Убедитесь, что вы запускаете его достаточно долго, чтобы можно было измерить результаты за секунды или (лучше) за десятки секунд.
  • Хотя вы не можете вызывать System.gc()между итерациями, рекомендуется запускать его между тестами, чтобы каждый тест мог получить «чистое» пространство памяти для работы. (Да, gc()это скорее намек, чем гарантия, но очень вероятно, что, по моему опыту, это действительно будет сборщиком мусора.)
  • Мне нравится отображать итерации и время, а также оценку времени / итерации, которую можно масштабировать так, чтобы «лучший» алгоритм получил оценку 1,0, а другие оценивались относительным образом. Это означает, что вы можете запускать все алгоритмы в течение длительного времени, варьируя количество итераций и время, но при этом получать сопоставимые результаты.

Я сейчас веду блог о разработке платформы для тестирования производительности в .NET. У меня есть несколько из предыдущих постов , которые могут быть в состоянии дать вам некоторые идеи - не все будет уместно, конечно, но некоторые из них могут быть.

48 assylias Apr 03 2013 at 19:32

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

Jmh - это средство Java для создания, запуска и анализа тестов нано / микро / макросов, написанных на Java и других языках, ориентированных на JVM.

Очень интересные фрагменты информации похоронены в комментариях к образцам тестов .

Смотрите также:

  • Как избежать ошибок при сравнительном анализе на JVM
  • Обсуждение основных сильных сторон jmh .
23 PeterLawrey Feb 03 2009 at 02:54

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

Это зависит от того, что вы пытаетесь проверить.

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

16 Kip Feb 03 2009 at 00:57

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

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

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

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

15 PeterŠtibraný Feb 03 2009 at 01:00

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

13 Mnementh Feb 03 2009 at 00:46

При написании микротестов на Java существует множество возможных ошибок.

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

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

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

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

8 SpaceTrucker Jan 21 2013 at 21:04

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

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

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

8 SinaMadani Mar 20 2017 at 02:21

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

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

Возможно, это более фундаментально важный аспект, который вы можете напрямую контролировать: убедитесь, что вы измеряете правильные вещи! Например, если вы используете 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");
7 Yuriy Dec 19 2010 at 06:22

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