Difference between declaring variables before or in loop?

前端 未结 25 1994
长发绾君心
长发绾君心 2020-11-22 02:37

I have always wondered if, in general, declaring a throw-away variable before a loop, as opposed to repeatedly inside the loop, makes any (performance) difference? A (q

相关标签:
25条回答
  • 2020-11-22 03:25

    I made a simple test:

    int b;
    for (int i = 0; i < 10; i++) {
        b = i;
    }
    

    vs

    for (int i = 0; i < 10; i++) {
        int b = i;
    }
    

    I compiled these codes with gcc - 5.2.0. And then I disassembled the main () of these two codes and that's the result:

    1º:

       0x00000000004004b6 <+0>:     push   rbp
       0x00000000004004b7 <+1>:     mov    rbp,rsp
       0x00000000004004ba <+4>:     mov    DWORD PTR [rbp-0x4],0x0
       0x00000000004004c1 <+11>:    jmp    0x4004cd <main+23>
       0x00000000004004c3 <+13>:    mov    eax,DWORD PTR [rbp-0x4]
       0x00000000004004c6 <+16>:    mov    DWORD PTR [rbp-0x8],eax
       0x00000000004004c9 <+19>:    add    DWORD PTR [rbp-0x4],0x1
       0x00000000004004cd <+23>:    cmp    DWORD PTR [rbp-0x4],0x9
       0x00000000004004d1 <+27>:    jle    0x4004c3 <main+13>
       0x00000000004004d3 <+29>:    mov    eax,0x0
       0x00000000004004d8 <+34>:    pop    rbp
       0x00000000004004d9 <+35>:    ret
    

    vs

       0x00000000004004b6 <+0>: push   rbp
       0x00000000004004b7 <+1>: mov    rbp,rsp
       0x00000000004004ba <+4>: mov    DWORD PTR [rbp-0x4],0x0
       0x00000000004004c1 <+11>:    jmp    0x4004cd <main+23>
       0x00000000004004c3 <+13>:    mov    eax,DWORD PTR [rbp-0x4]
       0x00000000004004c6 <+16>:    mov    DWORD PTR [rbp-0x8],eax
       0x00000000004004c9 <+19>:    add    DWORD PTR [rbp-0x4],0x1
       0x00000000004004cd <+23>:    cmp    DWORD PTR [rbp-0x4],0x9
       0x00000000004004d1 <+27>:    jle    0x4004c3 <main+13>
       0x00000000004004d3 <+29>:    mov    eax,0x0
       0x00000000004004d8 <+34>:    pop    rbp
       0x00000000004004d9 <+35>:    ret 
    

    Which are exaclty the same asm result. isn't a proof that the two codes produce the same thing?

    0 讨论(0)
  • 2020-11-22 03:25

    I've always thought that if you declare your variables inside of your loop then you're wasting memory. If you have something like this:

    for(;;) {
      Object o = new Object();
    }
    

    Then not only does the object need to be created for each iteration, but there needs to be a new reference allocated for each object. It seems that if the garbage collector is slow then you'll have a bunch of dangling references that need to be cleaned up.

    However, if you have this:

    Object o;
    for(;;) {
      o = new Object();
    }
    

    Then you're only creating a single reference and assigning a new object to it each time. Sure, it might take a bit longer for it to go out of scope, but then there's only one dangling reference to deal with.

    0 讨论(0)
  • 2020-11-22 03:26

    Well I ran your A and B examples 20 times each, looping 100 million times.(JVM - 1.5.0)

    A: average execution time: .074 sec

    B: average execution time : .067 sec

    To my surprise B was slightly faster. As fast as computers are now its hard to say if you could accurately measure this. I would code it the A way as well but I would say it doesn't really matter.

    0 讨论(0)
  • 2020-11-22 03:28

    I had this very same question for a long time. So I tested an even simpler piece of code.

    Conclusion: For such cases there is NO performance difference.

    Outside loop case

    int intermediateResult;
    for(int i=0; i < 1000; i++){
        intermediateResult = i+2;
        System.out.println(intermediateResult);
    }
    

    Inside loop case

    for(int i=0; i < 1000; i++){
        int intermediateResult = i+2;
        System.out.println(intermediateResult);
    }
    

    I checked the compiled file on IntelliJ's decompiler and for both cases, I got the same Test.class

    for(int i = 0; i < 1000; ++i) {
        int intermediateResult = i + 2;
        System.out.println(intermediateResult);
    }
    

    I also disassembled code for both the case using the method given in this answer. I'll show only the parts relevant to the answer

    Outside loop case

    Code:
      stack=2, locals=3, args_size=1
         0: iconst_0
         1: istore_2
         2: iload_2
         3: sipush        1000
         6: if_icmpge     26
         9: iload_2
        10: iconst_2
        11: iadd
        12: istore_1
        13: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        16: iload_1
        17: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
        20: iinc          2, 1
        23: goto          2
        26: return
    LocalVariableTable:
            Start  Length  Slot  Name   Signature
               13      13     1 intermediateResult   I
                2      24     2     i   I
                0      27     0  args   [Ljava/lang/String;
    

    Inside loop case

    Code:
          stack=2, locals=3, args_size=1
             0: iconst_0
             1: istore_1
             2: iload_1
             3: sipush        1000
             6: if_icmpge     26
             9: iload_1
            10: iconst_2
            11: iadd
            12: istore_2
            13: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
            16: iload_2
            17: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
            20: iinc          1, 1
            23: goto          2
            26: return
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
               13       7     2 intermediateResult   I
                2      24     1     i   I
                0      27     0  args   [Ljava/lang/String;
    

    If you pay close attention, only the Slot assigned to i and intermediateResult in LocalVariableTable is swapped as a product of their order of appearance. The same difference in slot is reflected in other lines of code.

    • No extra operation is being performed
    • intermediateResult is still a local variable in both cases, so there is no difference access time.

    BONUS

    Compilers do a ton of optimization, take a look at what happens in this case.

    Zero work case

    for(int i=0; i < 1000; i++){
        int intermediateResult = i;
        System.out.println(intermediateResult);
    }
    

    Zero work decompiled

    for(int i = 0; i < 1000; ++i) {
        System.out.println(i);
    }
    
    0 讨论(0)
  • 2020-11-22 03:30

    this is the better form

    double intermediateResult;
    int i = byte.MinValue;
    
    for(; i < 1000; i++)
    {
    intermediateResult = i;
    System.out.println(intermediateResult);
    }
    

    1) in this way declared once time both variable, and not each for cycle. 2) the assignment it's fatser thean all other option. 3) So the bestpractice rule is any declaration outside the iteration for.

    0 讨论(0)
  • 2020-11-22 03:32

    I tested for JS with Node 4.0.0 if anyone is interested. Declaring outside the loop resulted in a ~.5 ms performance improvement on average over 1000 trials with 100 million loop iterations per trial. So I'm gonna say go ahead and write it in the most readable / maintainable way which is B, imo. I would put my code in a fiddle, but I used the performance-now Node module. Here's the code:

    var now = require("../node_modules/performance-now")
    
    // declare vars inside loop
    function varInside(){
        for(var i = 0; i < 100000000; i++){
            var temp = i;
            var temp2 = i + 1;
            var temp3 = i + 2;
        }
    }
    
    // declare vars outside loop
    function varOutside(){
        var temp;
        var temp2;
        var temp3;
        for(var i = 0; i < 100000000; i++){
            temp = i
            temp2 = i + 1
            temp3 = i + 2
        }
    }
    
    // for computing average execution times
    var insideAvg = 0;
    var outsideAvg = 0;
    
    // run varInside a million times and average execution times
    for(var i = 0; i < 1000; i++){
        var start = now()
        varInside()
        var end = now()
        insideAvg = (insideAvg + (end-start)) / 2
    }
    
    // run varOutside a million times and average execution times
    for(var i = 0; i < 1000; i++){
        var start = now()
        varOutside()
        var end = now()
        outsideAvg = (outsideAvg + (end-start)) / 2
    }
    
    console.log('declared inside loop', insideAvg)
    console.log('declared outside loop', outsideAvg)
    
    0 讨论(0)
提交回复
热议问题