等效静态和非静态方法的速度差异很大

Sta*_*bbz 86 java methods performance static object

在此代码中,当我在main方法中创建一个Object 然后调用该对象方法:( ff.twentyDivCount(i)运行在16010毫秒)时,它运行速度比使用此注释调用它快得多:( twentyDivCount(i)运行在59516毫秒).当然,当我在不创建对象的情况下运行它时,我将方法设为静态,因此可以在main中调用它.

public class ProblemFive {

    // Counts the number of numbers that the entry is evenly divisible by, as max is 20
    int twentyDivCount(int a) {    // Change to static int.... when using it directly
        int count = 0;
        for (int i = 1; i<21; i++) {

            if (a % i == 0) {
                count++;
            }
        }
        return count;
    }

    public static void main(String[] args) {
        long startT = System.currentTimeMillis();;
        int start = 500000000;
        int result = start;

        ProblemFive ff = new ProblemFive();

        for (int i = start; i > 0; i--) {

            int temp = ff.twentyDivCount(i); // Faster way
                       // twentyDivCount(i) - slower

            if (temp == 20) {
                result = i;
                System.out.println(result);
            }
        }

        System.out.println(result);

        long end = System.currentTimeMillis();;
        System.out.println((end - startT) + " ms");
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑:到目前为止,似乎不同的机器产生不同的结果,但使用JRE 1.8.*是原始结果似乎一致再现的地方.

ass*_*ias 72

使用JRE 1.8.0_45我得到了类似的结果.

调查:

  1. 使用-XX:+UnlockDiagnosticVMOptions -XX:+PrintCompilation -XX:+PrintInliningVM选项运行java 显示两个方法都被编译和内联
  2. 查看方法本身生成的程序集显示没有显着差异
  3. 然而,一旦它们被内联,生成的组件内部main就会大不相同,实例方法会得到更积极的优化,特别是在循环展开方面

然后我再次运行你的测试,但是使用不同的循环展开设置来确认上面的怀疑.我运行你的代码:

  • -XX:LoopUnrollLimit=0 并且两种方法都运行缓慢(类似于使用默认选项的静态方法).
  • -XX:LoopUnrollLimit=100 并且两种方法都运行得很快(类似于带有默认选项的实例方法).

作为结论,似乎使用默认设置,当方法是静态时,热点1.8.0_45的JIT 无法展开循环(尽管我不确定它为什么会这样).其他JVM可能会产生不同的结果.

  • @maaartinus我甚至不确定那个数字究竟代表什么 - 文档是相当规避的:"*展开循环体,服务器编译器中间表示节点计数小于此值.服务器编译器使用的限制是**的函数此值,而不是实际值**.默认值因运行JVM的平台而异.*"... (3认同)

maa*_*nus 33

只是一个未经证实的猜测基于assylias的答案.

JVM使用一个阈值进行循环展开,这类似于70.无论出于何种原因,静态调用稍微大一点并且不会展开.

更新结果

  • LoopUnrollLimit52以下,两个版本都很慢.
  • 在52到71之间,只有静态版本很慢.
  • 71以上,两个版本都很快.

这很奇怪,因为我的猜测是静态调用在内部表示中略大,而OP遇到了一个奇怪的情况.但差异似乎大约是20,这没有任何意义.

 

-XX:LoopUnrollLimit=51
5400 ms NON_STATIC
5310 ms STATIC
-XX:LoopUnrollLimit=52
1456 ms NON_STATIC
5305 ms STATIC
-XX:LoopUnrollLimit=71
1459 ms NON_STATIC
5309 ms STATIC
-XX:LoopUnrollLimit=72
1457 ms NON_STATIC
1488 ms STATIC
Run Code Online (Sandbox Code Playgroud)

对于那些愿意尝试的人来说,我的版本可能很有用.