Why is there a slowdown in this identical code?

I tried to measure the runtime of this loop:

for (boolean t : test) {
    if (!t)
        ++count;
}

And we got inconsistent results. In the end, I managed to get consistent results with the following code:

public class Test {
    public static void main(String[] args) {
        int size = 100;
        boolean[] test = new boolean[10_000_000];
        java.util.Random r = new java.util.Random();
        for (int n = 0; n < 10_000_000; ++n)
            test[n] = !r.nextBoolean();

        int expected = 0;
        long acumulated = 0;
        for (int repeat = -1; repeat < size; ++repeat) {
            int count = 0;
            long start = System.currentTimeMillis();
            for (boolean t : test) {
                if (!t)
                    ++count;
            }
            long end = System.currentTimeMillis();
            if (repeat != -1)  // First run does not count, VM warming up
                acumulated += end - start;
            else                  // Use count to avoid compiler or JVM
                expected = count; //optimization of inner loop
            if ( count!=expected )
                throw new Error("Tests don't run same ammount of times");
        }
        float average = (float) acumulated / size;
        System.out.println("1st test : " + average);

        int expectedBis = 0;
        acumulated = 0;
        if ( "reassign".equals(args[0])) {
            for (int n = 0; n < 10_000_000; ++n)
                test[n] = test[n];
        }
        for (int repeat = -1; repeat < size; ++repeat) {
            int count = 0;
            long start = System.currentTimeMillis();
            for (boolean t : test) {
                if (!t)
                    ++count;
            }
            long end = System.currentTimeMillis();
            if (repeat != -1)  // First run does not count, VM warming up
                acumulated += end - start;
            else                     // Use count to avoid compiler or JVM
                expectedBis = count; //optimization of inner loop
            if ( count!=expected || count!=expectedBis)
                throw new Error("Tests don't run same ammount of times");
        }
        average = (float) acumulated / size;
        System.out.println("2nd test : " + average);
    }

}

The received results:

$ java -jar Test.jar noreassign
1st test : 23.98
2nd test : 23.97
$ java -jar Test.jar reassign
1st test : 23.98
2nd test : 40.86
$ java -version
java version "1.7.0_79"
OpenJDK Runtime Environment (IcedTea 2.5.5) (Gentoo package icedtea-7.2.5.5)
OpenJDK 64-Bit Server VM (build 24.79-b02, mixed mode)

The difference is whether or not this cycle is completed before the second test.

for (int n = 0; n < 10_000_000; ++n)
    test[n] = test[n];

Why? Why does this reevaluation lead to the fact that these cycles will then be performed twice?
Obtaining rights to profiles is difficult ...

+4
source share
3 answers

"As for why the JIT compiler causes this behavior ... it goes beyond my skills and knowledge."

Three key facts:

  • Code is faster to execute after compiling JIT.

  • JIT , . ( "" JVM .)

  • JIT .

, 1 2, , , , JIT... 2 .

main . .

( JIT . JIT, , ...)

NONE, JIT, .


, - . Q & A:

+3

, , .

. # .NET.

Java, # "" .

,

 if ( "reassign".equals(args[0])) {
        for (int n = 0; n < 5_000_000; ++n)
            test[n] = test[n];
    }

?

+3

.
JIT.
JIT, :

$ java -jar Test.jar -Djava.compiler=NONE noreassign  
1st test : 19.23  
2nd test : 19.33  
$ java -jar Test.jar -Djava.compiler=NONE reassign  
1st test : 19.23  
2nd test : 19.32  

The strange slowdown disappears after deactivating the JIT compiler.
As to why the JIT compiler causes this behavior ... it goes beyond my skills and knowledge.
But this does not happen in all JVMs, as the tests of Marius Dornei show.

+3
source

All Articles