高效的绩效衡量

时间:2012-05-11 10:06:58

标签: java performance

在这个问题中,我想问一下如何测试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启动时的跳转。

在大多数情况下,这种方法不会(更好)吗?

  1. 运行代码以经常测量以强制JIT编译
  2. 循环运行代码并测量执行时间。记住最小值并在此值稳定时中止循环。
  3. 我的理由是我通常想知道一些代码的速度有多快(下限)。外部事件(鼠标移动,显卡中断,因为你在桌面上有模拟时钟,交换,网络数据包......)任何代码都可能变得任意慢,但大多数时候,我只想知道有多快我的代码可以在完美的情况下。

    它还可以使性能测量更快,因为我不必在几秒钟或几分钟内运行代码(以消除不需要的效果)。

    有人可以确认/揭穿这个吗?

3 个答案:

答案 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成本。

关于你的建议:所有定时器的精度都有限,因此短时间例程可能会在零时钟周期内完成。这意味着您的算法会发现例程在零时间内运行。这显然是不正确的。