Java loop efficiency

I compared the efficiency of nested for, while and do while loops in Java, and I encountered some strange results that I needed help understanding

public class Loops {
    public static void main(String[] args) {
        int L = 100000;    // number of iterations per loop
        // for loop
        double start = System.currentTimeMillis();
        long s1 = 0;
        for (int i=0; i < L; i++) {
            for (int j = 0; j < L; j++) {
                s1 += 1;
            }
        }
        double end = System.currentTimeMillis();
        String result1 = String.format("for loop: %.5f",(end-start) / 1000);
        System.out.println(s1);
        System.out.println(result1);

        // do-while loop
        double start1 = System.currentTimeMillis();
        int i = 0;
        long s2 = 0;
        do {
            i++;
            int j = 0;
            do {
                s2 += 1;
                j++;
            } while (j < L);
        } while (i < L);
        double end1 = System.currentTimeMillis();
        String result2 = String.format("do-while: %.5f",(end1-start1) / 1000);
        System.out.println(s2);
        System.out.println(result2);

        // while loop
        double start2 = System.currentTimeMillis();
        i = 0;
        long s3 = 0;
        while (i < L) {
            i++;
            int j = 0;
            while (j < L) {
                s3 += 1;
                j++;
            }
        }
        double end2 = System.currentTimeMillis();
        String result3 = String.format("while: %.5f",(end2-start2) / 1000);
        System.out.println(s3);
        System.out.println(result3);
    }
}

The respective counters of all cycles reach 10 billion; The result puzzled me:

For loop: 6.48300

do-while:0.41200

And: 9.71500

Why is the do while loop much faster? This performance gap extends in parallel with any change in L I run these loops independently and do the same

Solution

I've run the code you provided, and I'm surprised to find these performance differences Guided by curiosity, I began to investigate and found that although these cycles seem to do the same thing, there are some important differences between them

The result of running these cycles for the first time is:

for loop: 1.43100
do-while: 0.51300
while: 1.54500

However, when I run these three loops at least 10 times, the performance of these loops is almost the same

for loop: 0.43200
do-while: 0.46100
while: 0.42900

JIT can optimize these loops over time, but there must be some differences, resulting in different initial performance of these loops There are actually two differences:

>The do while loop executes fewer times than the for loop and the while loop

For simplicity, assume L = 1

long s1 = 0;
for (int i=0; i < L; i++) {
    for (int j = 0; j < L; j++) {
        s1 += 1;

Outer ring: 0 < 1 inner loop: 0 < 1 inner ring: 1 < 1 outer ring: 1 < 1 4 Comparisons

int i = 0;
long s2 = 0;
do {
    i++;
    int j = 0;
    do {
        s2 += 1;
        j++;
    } while (j < L);
} while (i < L);

Inner ring: 1 < 1 outer ring: 1 < 1 2 Comparisons > different generated bytecodes

For further investigation, I have changed your class slightly, which will not affect the work of the class

public class Loops {
    final static int L = 100000; // number of iterations per loop

    public static void main(String[] args) {
        int round = 10;
        while (round-- > 0) {
            forLoop();
            doWhileLoop();
            whileLoop();
        }
    }

    private static long whileLoop() {
        int i = 0;
        long s3 = 0;
        while (i++ < L) {
            int j = 0;
            while (j++ < L) {
                s3 += 1;
            }
        }
        return s3;
    }

    private static long doWhileLoop() {
        int i = 0;
        long s2 = 0;
        do {
            int j = 0;
            do {
                s2 += 1;
            } while (++j < L);
        } while (++i < L);
        return s2;
    }

    private static long forLoop() {
        long s1 = 0;
        for (int i = 0; i < L; i++) {
            for (int j = 0; j < L; j++) {
                s1 += 1;
            }
        }
        return s1;
    }
}

Then compile it and call javap - C - S - Private - L loop to get the bytecode

The first is the bytecode of dowileloop

0:   iconst_0        // push the int value 0 onto the stack
   1:   istore_1        // store int value into variable 1 (i)
   2:   lconst_0        // push the long 0 onto the stack
   3:   lstore_2        // store a long value in a local variable 2 (s2)
   4:   iconst_0        // push the int value 0 onto the stack
   5:   istore  4   // store int value into variable 4 (j)
   7:   lload_2     // load a long value from a local variable 2 (i)
   8:   lconst_1        // push the long 1 onto the stack
   9:   ladd        // add two longs
   10:  lstore_2        // store a long value in a local variable 2 (i)
   11:  iinc    4,1    // increment local variable 4 (j) by signed byte 1
   14:  iload   4   // load an int value from a local variable 4 (j)
   16:  iload_0     // load an int value from a local variable 0 (L)
   17:  if_icmplt   7   // if value1 is less than value2,branch to instruction at 7
   20:  iinc    1,1    // increment local variable 1 (i) by signed byte 1
   23:  iload_1     // load an int value from a local variable 1 (i)
   24:  iload_0     // load an int value from a local variable 0 (L)
   25:  if_icmplt   4   // if value1 is less than value2,branch to instruction at 4
   28:  lload_2     // load a long value from a local variable 2 (s2)
   29:  lreturn     // return a long value

Bytecode of current while loop:

0:   iconst_0        // push int value 0 onto the stack
   1:   istore_1        // store int value into variable 1 (i)
   2:   lconst_0        // push the long 0 onto the stack
   3:   lstore_2        // store a long value in a local variable 2 (s3)
   4:   goto        26
   7:   iconst_0        // push the int value 0 onto the stack
   8:   istore  4   // store int value into variable 4 (j)
   10:  goto        17
   13:  lload_2     // load a long value from a local variable 2 (s3)
   14:  lconst_1        // push the long 1 onto the stack
   15:  ladd        // add two longs
   16:  lstore_2        // store a long value in a local variable 2 (s3)
   17:  iload   4   // load an int value from a local variable 4 (j)
   19:  iinc    4,1    // increment local variable 4 (j) by signed byte 1
   22:  iload_0     // load an int value from a local variable 0 (L)
   23:  if_icmplt   13  // if value1 is less than value2,branch to instruction at 13
   26:  iload_1     // load an int value from a local variable 1 (i)
   27:  iinc    1,1    // increment local variable 1 by signed byte 1
   30:  iload_0     // load an int value from a local variable 0 (L)
   31:  if_icmplt   7   // if value1 is less than value2,branch to instruction at 7
   34:  lload_2     // load a long value from a local variable 2 (s3)
   35:  lreturn     // return a long value

To make the output more readable, I have attached comments describing the execution of each instruction based on "Java bytecode instruction lists"

If you look closely, you will see that there are significant differences between the two bytecodes The while loop (as well as the for loop) defines an IF statement (if_icmplt instruction) at the end of the bytecode This means that to check the exit condition of the first loop, you must call the conversion to line 26 and, similarly, go to line 17 of the second loop

The bytecode above is javac 1.6.0 on Mac OS X 0_ 45 generated

outline

I think the comparison of different quantities and the existence of goto instructions in the bytecode of while and for loops are the reasons for the performance differences between these loops

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>