我想在Java应用程序上进行一些计时测试.这就是我目前正在做的事情:
long startTime = System.currentTimeMillis(); doSomething(); long finishTime = System.currentTimeMillis(); System.out.println("That took: " + (finishTime - startTime) + " ms");
像这样的性能测试有什么"错误"吗?什么是更好的方法?
重复:秒表基准可以接受吗?
这种方法的一个缺陷是doSomething()
执行的"实际"时间可能会有很大差异,具体取决于系统上运行的其他程序及其负载.这使得性能测量有些不精确.
假设代码是单线程的,跟踪执行代码所花费的时间的一种更准确的方法是查看线程在调用期间消耗的CPU时间.您可以使用JMX类执行此操作; 尤其是ThreadMXBean
.您可以检索ThreadMXBean
from 的实例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!)
这种策略并不完美,但它不受系统负载变化的影响.
问题中显示的代码不是一个好的性能测量代码:
编译器可能会选择通过重新排序语句来优化代码.是的,它可以做到这一点.这意味着您的整个测试可能会失败.它甚至可以选择内联测试中的方法,并将测量语句重新排序为现在内联的代码.
热点可能会选择重新排序语句,内联代码,缓存结果,延迟执行......
即使假设编译器/热点没有欺骗你,你测量的是"壁挂时间".您应该测量的是CPU时间(除非您使用OS资源并希望包含这些资源,或者您在多线程环境中测量锁定争用).
解决方案?使用真实的探查器.周围有很多,包括免费的个人资料以及商业广告的演示/时间锁定试验.