调整Java 7以匹配Java 6的性能



作为性能测试套件的一部分,我们有一个简单的单元测试,用于在开始测试代码之前验证基本系统是否正常并执行。通过这种方式,我们通常会验证机器是否适合运行实际性能测试。

当我们使用这个测试来比较Java6和Java7时,Java7的执行时间要长得多!我们看到Java 6平均需要22秒,Java 7平均需要24秒。测试只计算fibonacci,所以只有在单个线程中执行字节码才应该与此相关,而不是I/O或其他任何东西。

目前,我们在Windows上使用默认设置运行它,无论是否使用"-server",使用32位和64位JVM,所有运行都表明Java 7的性能有类似的下降。

在这里,哪些调优选项可能适合尝试将Java 7与Java 6相匹配?

public class BaseLinePerformance {
    @Before
    public void setup() throws Exception{
        fib(46);
    }
    @Test
    public void testBaseLine() throws Exception {
        long start = System.currentTimeMillis();
        fib(46);
        fib(46);
        System.out.println("Time: " + (System.currentTimeMillis() - start));
    }
    public static void fib(final int n) throws Exception {
        for (int i = 0; i < n; i++) {
            System.out.println("fib(" + i + ") = " + fib2(i));
        }
    }
    public static int fib2(final int n) {
        if (n == 0)
            return 0;
        else if (n == 1)
            return 1;
        else
            return fib2(n - 2) + fib2(n - 1);
    }
}

更新:我已经将测试减少到不进行任何睡眠,并遵循了"如何用Java编写正确的微基准测试?"中的其他建议?,我仍然看到Java7和Java6之间的相同区别,用于打印编译和GC的额外JVM选项在实际测试期间不显示任何输出,只打印最初的编译信息。

我的一位同事在进一步挖掘后发现了原因:

JVM标志XX:MaxRecursiveInlineLevel的默认值为1。在以前的版本中,这个设置的处理似乎有点不正确,所以Sun/Oracle在Java 7中"修复"了这个问题,但它的副作用是,现在的内联有时不那么激进,因此递归代码的纯运行时/CPU时间可能比以前更长。

我们正在测试将它设置为2,以获得与Java6中相同的行为,至少对于所讨论的测试是这样。

这不是一个简单的答案,有很多事情可以解释这两秒钟。

对于您的评论,我假设您已经熟悉微基准测试,并且您的基准测试是在JVM预热后运行的,使您的代码达到优化的JIT状态,并且没有GC发生,还假设您的硬件设置没有改变。

我建议您对基准测试进行CPU分析,这将帮助您确定这两秒钟的时间,并可能采取相应的行动。

如果你对字节码很好奇,你可以看看它

要做到这一点,你可以编译你的类,并在两台机器上执行javap-c ClassName,这将反汇编类文件字节码并将其显示给你,在这里你肯定会看到两个编译的类之间的变化。

总之,在查看数据后的22秒内,对应用程序进行相应的评测和调优,无论如何,对字节码实现都无能为力。

最新更新