在这个问题中,我想问一下如何测试Java代码的性能。通常的方法是这样做的:
long start = System.nanoTime();
for( int i=0; i<SOME_VERY_LARGE_NUMBER; i++) {
...do something...
}
long duration = System.nanoTime() - start;
System.out.println( "Performance: "
+ new BigDecimal( duration ).divide(
new BigDecimal( SOME_VERY_LARGE_NUMBER, 3, RoundingMode.HALF_UP ) ) );
“优化”版本将对System.nanoTime()
的调用移动到循环中,增加了错误余量,因为System.nanoTime()
比i ++
需要更长的时间(并且在运行时行为中更难以预测)和比较。
我的批评是:
这给了我平均运行时间,但这个值取决于我并不感兴趣的因素:就像测试循环运行时的系统负载或JIT / GC启动时的跳转。
在大多数情况下,这种方法不会(更好)吗?
我的理由是我通常想知道一些代码的速度有多快(下限)。外部事件(鼠标移动,显卡中断,因为你在桌面上有模拟时钟,交换,网络数据包......)任何代码都可能变得任意慢,但大多数时候,我只想知道有多快我的代码可以在完美的情况下。
它还可以使性能测量更快,因为我不必在几秒钟或几分钟内运行代码(以消除不需要的效果)。
有人可以确认/揭穿这个吗?
答案 0 :(得分:3)
我认为你提出的建议非常合理,并进行了一些调整:
1)我会报告中位数 - 或一堆百分位数 - 而不是最小值。如果你的代码给垃圾收集器带来了很大的压力,那么简单地采用最小化就很容易就无法接受(只需要在两次连续的GC暂停之间进行一次迭代)。
2)在许多情况下,测量CPU时间而不是挂钟时间是有意义的。这样可以解决在同一个盒子上运行其他代码的一些影响。
3)一些基准测试工具使用两级循环:内循环重复执行操作,外循环查看内循环前后的时钟。然后在外循环的迭代中聚合观察结果。
最后,以下内容概述了需要注意的JVM特定问题:How do I write a correct micro-benchmark in Java?
答案 1 :(得分:2)
您可以使用-XX:CompileThreshold JVM选项指定JIT何时启动。然后您可以通过在运行定时循环之前运行大于CompileThreshold的循环来“预热”测试。
答案 2 :(得分:1)
我将运行SOME_VERY_LARGE_NUMBER循环50次并计算表现最佳的循环的平均值。这通常是在其他基准测试中完成的,而不仅仅是代码微基准测试。
我还认为GC引起的性能问题通常是代码的一部分。您可能不应该将GC排除在等式之外,因为分配大量内存的例程应该必须为此支付一定的基准价格。如果您选择足够大的SOME_VERY_LARGE_NUMBER,则建议的方法会计算每次调用的平均GC成本。
关于你的建议:所有定时器的精度都有限,因此短时间例程可能会在零时钟周期内完成。这意味着您的算法会发现例程在零时间内运行。这显然是不正确的。