Тестирование производительности Java
Я хочу сделать некоторые тесты времени в приложении Java. Это то, что я сейчас делаю:
long startTime = System.currentTimeMillis();
doSomething();
long finishTime = System.currentTimeMillis();
System.out.println("That took: " + (finishTime - startTime) + " ms");
Есть ли что-то "неправильное" при тестировании производительности? Что лучше?
Дубликат: Допустимы ли тесты в секундомере?
Ответы
Ответ 1
Единственным недостатком этого подхода является то, что "реальное" время doSomething()
, которое требуется выполнить, может сильно различаться в зависимости от того, какие другие программы работают в системе и какова его нагрузка. Это делает измерение производительности несколько неточным.
Еще один точный способ отслеживания времени, затрачиваемого на выполнение кода, при условии, что код является однопоточным, - это смотреть на время процессора, потребляемое потоком во время вызова. Вы можете сделать это с помощью классов JMX; в частности, с ThreadMXBean
. Вы можете получить экземпляр ThreadMXBean
из java.lang.management.ManagementFactory
, и если ваша платформа поддерживает его (большинство из них), используйте getCurrentThreadCpuTime
вместо System.currentTimeMillis
выполнить аналогичный тест. Имейте в виду, что getCurrentThreadCpuTime
сообщает время в наносекундах, а не миллисекундах.
Здесь пример (Scala), который можно использовать для выполнения измерения:
def measureCpuTime(f: => Unit): java.time.Duration = {
import java.lang.management.ManagementFactory.getThreadMXBean
if (!getThreadMXBean.isThreadCpuTimeSupported)
throw new UnsupportedOperationException(
"JVM does not support measuring thread CPU-time")
var finalCpuTime: Option[Long] = None
val thread = new Thread {
override def run(): Unit = {
f
finalCpuTime = Some(getThreadMXBean.getThreadCpuTime(
Thread.currentThread.getId))
}
}
thread.start()
while (finalCpuTime.isEmpty && thread.isAlive) {
Thread.sleep(100)
}
java.time.Duration.ofNanos(finalCpuTime.getOrElse {
throw new Exception("Operation never returned, and the thread is dead " +
"(perhaps an unhandled exception occurred)")
})
}
(Не стесняйтесь переводить вышесказанное на Java!)
Эта стратегия не идеальна, но она менее подвержена изменениям в загрузке системы.
Ответ 2
Код, показанный в вопросе, не является хорошим кодом измерения производительности:
-
Компилятор может выбрать оптимизацию кода путем переопределения операторов. Да, он может это сделать. Это означает, что весь ваш тест может потерпеть неудачу. Он может даже выбрать встроенный метод проверки и изменить порядок измерений в теперь встроенный код.
-
Точка доступа может выбрать изменение порядка операторов, встроенный код, результаты кеша, выполнение задержки...
-
Даже если предположить, что компилятор/точка доступа не обманывали вас, то, что вы измеряете, - это "время на стене". То, что вы должны измерять, - это время процессора (если вы не используете ресурсы ОС и не хотите включать их, или вы измеряете блокировку в многопоточной среде).
Решение? Используйте реальный профилировщик. Существует множество возможностей, как для бесплатных профилировщиков, так и для демонстрационных/задержек с проверкой прочности рекламных роликов.
Ответ 3
Использование Java Profiler - лучший вариант, и он даст вам все понимание, которое вам нужно в коде. viz Response Times, Thread CallTraces, Утилиты памяти и т.д.
Я предлагаю вам JENSOR, открытый Java-профайлер с открытым исходным кодом, для простоты использования и нет накладных расходов на процессор. Вы можете загрузить его, использовать код и получить всю необходимую информацию о своем коде.
Вы можете скачать его из: http://jensor.sourceforge.net/
Ответ 4
Помните, что разрешение System.currentTimeMillis()
зависит от разных операционных систем. Я считаю, что Windows составляет около 15 мс. Поэтому, если ваш doSomething()
работает быстрее разрешения по времени, вы получите дельта 0. Вы можете запустить doSomething()
в цикле несколько раз, но тогда JVM может оптимизировать его.
Ответ 5
Я бы предположил, что вы захотите сделать что-то() перед тем, как начать синхронизацию, чтобы код был JITted и "разогрет".
Ответ 6
Ну, это всего лишь одна часть тестирования производительности. В зависимости от того, что вы тестируете, вам, возможно, придется посмотреть размер кучи, количество потоков, сетевой трафик или целый ряд других вещей. В противном случае я использую эту технику для простых вещей, которые я просто хочу посмотреть, как долго они будут выполняться.
Ответ 7
Хорошо, когда вы сравниваете одну реализацию с другой или пытаетесь найти медленную часть своего кода (хотя это может быть утомительно). Это действительно хорошая техника, чтобы знать, и вы, вероятно, будете использовать ее больше, чем любой другой, но также знакомы с инструментом профилирования.
Ответ 8
Вы просмотрели инструменты профилирования в netbeans и eclipse. Эти инструменты помогут вам лучше справиться с тем, что ДЕЙСТВИТЕЛЬНО занимает все время в вашем коде. Я нашел проблемы, которые я не понимал, используя эти инструменты.
Ответ 9
Japex может быть вам полезен, как способ быстрого создания контрольных показателей, или как способ изучения проблем с бенчмаркингом в Java через исходный код.