Как лучше писать микро-тесты/тесты на производительность в Java? Какие основные пункты, на которые стоит обратить внимание?
Ответ
Ключевые моменты:
Правило 0: правильно сформулируйте задачу и что именно вы хотите проверить.
Плохо поставленная задача:
Я хочу проверить, что быстрее ArrayList или LinkedList?
Лучше:
В моём приложении вставка чаще всего происходит в середину списка. Что для моей задачи будет лучше ArrayList или LinkedList?
Ещё лучше:
В моём приложении вставка чаще всего происходит в середину списка, а при чтении мне нужно, как правило, за раз просматривать несколько элементов подряд. Что для моей задачи будет лучше ArrayList или LinkedList?
Правило 1: почитайте авторитетные статьи про различные JVM и микро-бенчмарки. Например, Brian Goetz, 2005. Но не ожидайте чудес от замеров. Все бенчмарки замеряют ограниченный набор метрик в рамках контекста.
Правило 2: всегда включайте фазу прогрева jvm, прогоняющую ваши тесты для полной инициализации и компиляции перед фазой самих замеров. (По-хорошему, нужно провести несколько десятков тысяч итераций).
Правило 3: Запускайте всегда с флагами -XX:+PrintCompilation, -verbose:gc и т.п., чтобы быть уверенными, что компилятор и другие части jvm не будут выполнять незапланированных операций во время фазы замеров.
Правило 4: имейте в виду разницу между -client и -server, а также OSR (On Stack Replacement) и регулярной компиляцией. Предпочитайте сервер клиенту и регулярную компиляцию вместо OSR.
Правило 5: помните об эффекте инициализации. Не выводите ничего в консоль впервые в фазе замеров, так как вывод инициализирует классы. Не загружайте новый классы вне фазы прогрева, если, конечно, вы не тестируете саму загрузку классов.
Правило 6: помните об эффектах деоптимизации и перекомпиляции. Не допускайте, чтобы какие-то ветки коды впервые использовались во время фазы замеров, потому что компилятор может перекомпилировать код на основе более раннего оптимистического предположения о том, что этот путь не будет использоваться вообще. Правило 2 - это ваша первая линия защиты от таких эффектов.
Правило 7: используйте соответствующие инструменты, чтобы читать кишки компилятора, и ожидайте, что будете удивлены от произведённого кода. Осмотрите код самостоятельно, прежде чем формировать теории о том, что же замедляет его.
Правило 8: уменьшите шум в ваших измерениях. Запустите свой тест на машине в спокойном состоянии. Запустите его несколько раз, отбросив выбросы. Используйте -Xbatch для сериализации компилятора с приложением и рассмотрите возможность установки -XX:CICompilerCount=1 для предотвращения параллельной работы компилятора с самим собой.
Правило 9: используйте уже готовые библиотеки для замеров. Например: JMH, Caliper или Bill and Paul's Excellent UCSD Benchmarks for Java.
Правило 10: если пишите, всё же, сами, то используйте System.nanoTime(), т.к. с System.currentTimeMillis() могут быть проблемы. На Linux системах при использовании NTP демона разность двух последовательных System.currentTimeMillis() может быть отрицательной. Или можно воспользоваться StopWatch классами из тех же Apache commons или Guava
Правило 11: будет полезно вызывать System.gc() между тестами (но не между итерациями!).
Полезные статьи:
Dynamic compilation and performance measurement от IBM.
5 things you didn't know about Java performance monitoring от IBM.
Avoiding Benchmarking Pitfalls on the JVM
Комментариев нет:
Отправить комментарий