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

2022-08-31 11:53:50

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

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");
    }
}

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


答案 1

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

调查:

  1. 使用VM选项运行java表明这两种方法都经过编译和内联-XX:+UnlockDiagnosticVMOptions -XX:+PrintCompilation -XX:+PrintInlining
  2. 查看方法本身的生成程序集,发现没有显著差异
  3. 然而,一旦它们被内联,其中生成的程序集就非常不同,实例方法得到了更积极的优化,特别是在循环展开方面。main

然后,我再次运行了您的测试,但使用不同的循环展开设置来确认上面的怀疑。我用以下命令运行了您的代码:

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

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


答案 2

只是一个未经证实的猜测,基于阿西利亚斯的答案。

JVM 使用阈值进行循环展开,大约为 70。无论出于何种原因,静态调用都略大,不会展开。

更新结果

  • 在低于52的情况下,两个版本都很慢。LoopUnrollLimit
  • 在 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

对于那些愿意尝试的人来说,我的版本可能会有所帮助。