用java创建快速/可靠的基准测试?

我正在尝试用java创建基准测试。 目前我有以下简单的方法:

public static long runTest(int times){ long start = System.nanoTime(); String str = "str"; for(int i=0; i<times; i++){ str = "str"+i; } return System.nanoTime()-start; } 

我目前在另一个循环中多次进行此循环,该循环多次发生并获得运行此方法所需的最小/最大/平均时间。 然后我在另一个线程上开始一些活动并再次测试。 基本上我只是想获得一致的结果……如果我有1000万次runTest循环,它似乎非常一致:

 Number of times ran: 5 The max time was: 1231419504 (102.85% of the average) The min time was: 1177508466 (98.35% of the average) The average time was: 1197291937 The difference between the max and min is: 4.58% Activated thread activity. Number of times ran: 5 The max time was: 3872724739 (100.82% of the average) The min time was: 3804827995 (99.05% of the average) The average time was: 3841216849 The difference between the max and min is: 1.78% Running with thread activity took 320.83% as much time as running without. 

但这似乎有点多,并且需要一些时间…如果我在runTest循环中尝试较低的数字(100000)…它开始变得非常不一致:

  Number of times ran: 5 The max time was: 34726168 (143.01% of the average) The min time was: 20889055 (86.02% of the average) The average time was: 24283026 The difference between the max and min is: 66.24% Activated thread activity. Number of times ran: 5 The max time was: 143950627 (148.83% of the average) The min time was: 64780554 (66.98% of the average) The average time was: 96719589 The difference between the max and min is: 122.21% Running with thread activity took 398.3% as much time as running without. 

有没有办法可以像这样做一个既稳定又高效/快速的基准?

顺便说一句,我没有测试开始和结束时间之间的代码。 我正在以某种方式测试CPU负载(看看我是如何开始一些线程活动并重新测试的)。 所以我认为我正在寻找的东西替代我在“runTest”中的代码,它将产生更快,更一致的结果。

谢谢

简而言之:

(微)基准测试非常复杂,因此使用像Benchmarking框架http://www.ellipticgroup.com/misc/projectLibrary.zip这样的工具 – 仍然对结果持怀疑态度(“将微信任放在微观上 -基准“,Cliff博士点击)。

详细:

有很多因素可以强烈影响结果:

  • System.nanoTime的准确性和精确度:在最坏的情况下,它与System.currentTimeMillis一样糟糕。
  • 代码预热和类加载
  • 混合模式:JVMs JIT编译(参见Edwin Buck的答案)仅在经常调用代码块(1500或1000次)之后
  • 动态优化:去优化,堆栈替换,死代码消除(你应该使用你在循环中计算的结果,例如打印它)
  • 资源回收:garbace集合(参见Michael Borgwardt的回答)和对象最终确定
  • 缓存:I / O和CPU
  • 您的操作系统整体:屏幕保护程序,电源管理,其他进程(索引器,病毒扫描……)

Brent Boyer的文章“健壮的Java基准测试,第1部分:问题”( http://www.ibm.com/developerworks/java/library/j-benchmark1/index.html )很好地描述了所有这些问题以及是否/什么你可以对付它们(例如使用JVM选项或事先调用ProcessIdleTask)。

你将无法消除所有这些因素,因此做统计数据是一个好主意。 但:

  • 你应该努力计算标准差,而不是计算最大值和最小值之间的差值(结果{1,1000乘2,3}不同于{501乘以1,501乘3})。
  • 通过产生置信区间(例如通过自举)来考虑可靠性。

上面提到的Benchmark框架( http://www.ellipticgroup.com/misc/projectLibrary.zip )使用了这些技术。 您可以在Brent Boyer的文章“Robust Java benchmarkinging,Part 2:Statistics and solutions”( https://www.ibm.com/developerworks/java/library/j-benchmark2/ )中阅读它们。

您的代码最终主要测试垃圾收集性能,因为在循环中附加到String最终会创建并立即丢弃大量越来越大的String对象。

这本身就会导致测量结果发生巨大变化,并受到multithreading活动的影响。

我建议你在循环中做一些具有更可预测性能的东西,比如数学计算。

在1000万次运行中,HotSpot编译器检测到“大量使用”的代码并将其编译为机器本机代码的可能性很大。

解释了JVM字节码,这导致它容易受到JVM中发生的其他后台进程的更多中断(如垃圾收集)。

一般来说,这些类型的基准充满了不成立的假设。 如果没有大量证据certificate初始测量(时间)实际上并未测量您的任务以及可能的其他后台任务,您无法相信微观基准确实certificate了它的目的。 如果您不尝试控制后台任务,那么测量就不那么有用了。