Вопрос

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

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

Пример:Должен ли бенчмарк измерять время / итерацию или итерации / time и почему?

Похожие: Допустим ли сравнительный анализ по секундомеру?

Это было полезно?

Решение

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

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

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

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

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

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

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

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

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

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

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

Другие советы

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

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

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

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

JMH из OpenJDK

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

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

Важными вещами для Java-бенчмарков являются:

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

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

jmh это недавнее дополнение к OpenJDK, написанное некоторыми инженерами по производительности из Oracle.Безусловно, стоит посмотреть.

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

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

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

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

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

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

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

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

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.

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

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

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

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

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

Это происходит потому, что реализация 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");
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top