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

时间:2015-05-26 09:40:33

标签: java performance object methods static

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

twentyDivCount(i)

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

5 个答案:

答案 0 :(得分:72)

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

调查:

  1. 使用-XX:+UnlockDiagnosticVMOptions -XX:+PrintCompilation -XX:+PrintInlining VM选项运行java表明两种方法都已编译并内联
  2. 查看方法本身生成的程序集显示没有显着差异
  3. 然而,一旦它们被内联,main内生成的程序集就会大不相同,实例方法会更加积极地进行优化,特别是在循环展开方面
  4. 然后我再次运行你的测试但是使用不同的循环展开设置来确认上面的怀疑。我用以下代码运行你的代码:

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

    作为结论,似乎使用默认设置,当方法是静态时,热点1.8.0_45 的JIT 无法展开循环(虽然我不知道为什么它表现那样)。其他JVM可能会产生不同的结果。

答案 1 :(得分:33)

根据assylias的答案,这是一个未经证实的猜测。

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

对于愿意进行实验的人,my version可能会有用。

答案 2 :(得分:0)

在调试模式下执行此操作时,实例和静态情况的数字相同。这进一步意味着JIT在静态情况下将代码编译为本机代码犹豫不决,就像在实例方法案例中一样。

为什么会这样做?这很难说;如果这是一个更大的应用程序,它可能会做正确的事情......

答案 3 :(得分:0)

我只是略微调整了测试,得到了以下结果:

输出:

Dynamic Test:
465585120
232792560
232792560
51350 ms
Static Test:
465585120
232792560
232792560
52062 ms

注意

当我单独测试时,动态约为52秒,静态约为200秒。

这是该计划:

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

    static int twentyDivCount2(int a) {
         int count = 0;
         for (int i = 1; i<21; i++) {

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

    public static void main(String[] args) {
        System.out.println("Dynamic Test: " );
        dynamicTest();
        System.out.println("Static Test: " );
        staticTest();
    }

    private static void staticTest() {
        long startT = System.currentTimeMillis();;
        int start = 500000000;
        int result = start;

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

            int temp = twentyDivCount2(i);

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

        System.out.println(result);

        long end = System.currentTimeMillis();;
        System.out.println((end - startT) + " ms");
    }

    private static void dynamicTest() {
        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

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

        System.out.println(result);

        long end = System.currentTimeMillis();;
        System.out.println((end - startT) + " ms");
    }
}

我还将测试顺序更改为:

public static void main(String[] args) {
    System.out.println("Static Test: " );
    staticTest();
    System.out.println("Dynamic Test: " );
    dynamicTest();
}

我得到了这个:

Static Test:
465585120
232792560
232792560
188945 ms
Dynamic Test:
465585120
232792560
232792560
50106 ms

如您所见,如果在静态之前调用dynamic,则静态速度会急剧下降。

基于此基准:

  

假设这一切都取决于JVM优化。因此我   只是建议你按照经验法则使用静态和   动态方法。

THUMB的规则:

Java: when to use static methods

答案 4 :(得分:0)

请尝试:

public class ProblemFive {
    public static ProblemFive PROBLEM_FIVE = new ProblemFive();

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


        for (int i = start; i > 0; i--) {
            int temp = PROBLEM_FIVE.twentyDivCount(i); // faster way
            // twentyDivCount(i) - slower

            if (temp == 20) {
                result = i;
                System.out.println(result);
                System.out.println((System.currentTimeMillis() - startT) + " ms");
            }
        }

        System.out.println(result);

        long end = System.currentTimeMillis();
        System.out.println((end - startT) + " ms");
    }

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