Large difference in speed of equivalent static and non static methods

Stabbz picture Stabbz · May 26, 2015 · Viewed 8.5k times · Source

In this code when I create an Object in the main method and then call that objects method: ff.twentyDivCount(i)(runs in 16010 ms) , it runs much faster than calling it using this annotation: twentyDivCount(i)(runs in 59516 ms). Of course, when I run it without creating an object, I make the method static, so it can be called in the 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");
    }
}

EDIT: So far it seems that different machines produce different results, but using JRE 1.8.* is where the original result seems to be consistently reproduced.

Answer

assylias picture assylias · May 26, 2015

Using JRE 1.8.0_45 I get similar results.

Investigation:

  1. running java with the -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompilation -XX:+PrintInlining VM options shows that both methods get compiled and inlined
  2. Looking at the generated assembly for the methods themselves shows no significant difference
  3. Once they get inlined, however, the generated assembly within main is very different, with the instance method being more aggressively optimised, especially in terms of loop unrolling

I then ran your test again but with different loop unrolling settings to confirm the suspicion above. I ran your code with:

  • -XX:LoopUnrollLimit=0 and both methods run slowly (similar to the static method with the default options).
  • -XX:LoopUnrollLimit=100 and both methods run fast (similar to the instance method with the default options).

As a conclusion it seems that, with the default settings, the JIT of hotspot 1.8.0_45 is not able to unroll the loop when the method is static (although I'm not sure why it behaves that way). Other JVMs may yield different results.