By Rabarberski


2009-01-02 16:06:40 8 Comments

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 (quite pointless) example in Java:

a) declaration before loop:

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

b) declaration (repeatedly) inside loop:

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

Which one is better, a or b?

I suspect that repeated variable declaration (example b) creates more overhead in theory, but that compilers are smart enough so that it doesn't matter. Example b has the advantage of being more compact and limiting the scope of the variable to where it is used. Still, I tend to code according example a.

Edit: I am especially interested in the Java case.

24 comments

@SteveOC 64 2018-02-19 05:25:25

Tried the same thing in Go, and compared the compiler output using go tool compile -S with go 1.9.4

Zero difference, as per the assembler output.

@Daniel Earwicker 2009-01-02 16:18:10

Which is better, a or b?

From a performance perspective, you'd have to measure it. (And in my opinion, if you can measure a difference, the compiler isn't very good).

From a maintenance perspective, b is better. Declare and initialize variables in the same place, in the narrowest scope possible. Don't leave a gaping hole between the declaration and the initialization, and don't pollute namespaces you don't need to.

@Antoops 2014-02-24 12:00:12

Instead of Double, if it deals with String, still the case "b" better?

@Daniel Earwicker 2015-02-25 12:32:39

@Antoops - yes, b is better for reasons that have nothing to do with the data type of the variable being declared. Why would it be different for Strings?

@luka 2016-07-13 11:06:52

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.

@user137717 2016-04-04 20:32:19

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)

@UserX 2015-10-10 14:24:23

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?

@user137717 2016-04-04 19:24:07

yea, and it's cool that you did this, but this comes back to what people were saying about the language / compiler dependence. I wonder how JIT or interpreted language performance would be affected.

@Marcelo Faísca 2011-10-23 12:39:49

Well, you could always make a scope for that:

{ //Or if(true) if the language doesn't support making scopes like this
    double intermediateResult;
    for (int i=0; i<1000; i++) {
        intermediateResult = i;
        System.out.println(intermediateResult);
    }
}

This way you only declare the variable once, and it'll die when you leave the loop.

@particle 2010-01-13 11:52:39

The following is what I wrote and compiled in .NET.

double r0;
for (int i = 0; i < 1000; i++) {
    r0 = i*i;
    Console.WriteLine(r0);
}

for (int j = 0; j < 1000; j++) {
    double r1 = j*j;
    Console.WriteLine(r1);
}

This is what I get from .NET Reflector when CIL is rendered back into code.

for (int i = 0; i < 0x3e8; i++)
{
    double r0 = i * i;
    Console.WriteLine(r0);
}
for (int j = 0; j < 0x3e8; j++)
{
    double r1 = j * j;
    Console.WriteLine(r1);
}

So both look exactly same after compilation. In managed languages code is converted into CL/byte code and at time of execution it's converted into machine language. So in machine language a double may not even be created on the stack. It may just be a register as code reflect that it is a temporary variable for WriteLine function. There are a whole set optimization rules just for loops. So the average guy shouldn't be worried about it, especially in managed languages. There are cases when you can optimize manage code, for example, if you have to concatenate a large number of strings using just string a; a+=anotherstring[i] vs using StringBuilder. There is very big difference in performance between both. There are a lot of such cases where the compiler cannot optimize your code, because it cannot figure out what is intended in a bigger scope. But it can pretty much optimize basic things for you.

@luka 2017-12-07 14:24:31

int j = 0 for (; j < 0x3e8; j++) 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.

@Michael Haren 2009-01-02 16:20:15

This is a gotcha in VB.NET. The Visual Basic result won't reinitialize the variable in this example:

For i as Integer = 1 to 100
    Dim j as Integer
    Console.WriteLine(j)
    j = i
Next

' Output: 0 1 2 3 4...

This will print 0 the first time (Visual Basic variables have default values when declared!) but i each time after that.

If you add a = 0, though, you get what you might expect:

For i as Integer = 1 to 100
    Dim j as Integer = 0
    Console.WriteLine(j)
    j = i
Next

'Output: 0 0 0 0 0...

@ChrisA 2009-01-02 17:33:45

I've been using VB.NET for years and hadn't come across this!!

@Michael Haren 2009-01-02 17:59:56

Yes, it's unpleasant to figure this out in practice.

@ferventcoder 2009-01-02 19:05:14

Here is a reference about this from Paul Vick: panopticoncentral.net/archive/2006/03/28/11552.aspx

@Eric Schneider 2011-05-17 21:26:13

Anyone have a link on this that works?

@Mark Hurd 2011-05-19 14:58:13

@eschneider @ferventcoder Unfortunately @PaulV has decided to drop his old blog posts, so this is now a dead link.

@Eric Schneider 2011-05-23 14:41:10

yea, just recently ran across this; was looking for some official docs on this...

@smile.al.d.way 2011-11-22 14:25:33

yep it doesn't dim the variable again. ran into this yesterday and series of googling let to this.

@annakata 2009-01-02 16:11:26

It is language dependent - IIRC C# optimises this, so there isn't any difference, but JavaScript (for example) will do the whole memory allocation shebang each time.

@user137717 2016-04-04 19:29:30

Yea, but that doesn't amount to much. I ran a simple test with a for loop executing 100 million times and I found that the biggest difference in favor of declaring outside the loop was 8 ms. It was usually more like 3-4 and occasionally declaring outside the loop performed WORSE (up to 4 ms), but that was not typical.

@Alex 2015-04-20 12:29:41

From a performance perspective, outside is (much) better.

public static void outside() {
    double intermediateResult;
    for(int i=0; i < Integer.MAX_VALUE; i++){
        intermediateResult = i;
    }
}

public static void inside() {
    for(int i=0; i < Integer.MAX_VALUE; i++){
        double intermediateResult = i;
    }
}

I executed both functions 1 billion times each. outside() took 65 milliseconds. inside() took 1.5 seconds.

@Tomasz Przychodzki 2016-02-12 10:47:33

Must have been Debug unoptimized compilation then, huh?

@luka 2017-12-07 14:25:18

int j = 0 for (; j < 0x3e8; j++) 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.

@Joshua Siktar 2015-03-06 03:38:12

It's an interesting question. From my experience there is an ultimate question to consider when you debate this matter for a code:

Is there any reason why the variable would need to be global?

It makes sense to only declare the variable once, globally, as opposed to many times locally, because it is better for organizing the code and requires less lines of code. However, if it only needs to be declared locally within one method, I would initialize it in that method so it is clear that the variable is exclusively relevant to that method. Be careful not to call this variable outside the method in which it is initialized if you choose the latter option--your code won't know what you're talking about and will report an error.

Also, as a side note, don't duplicate local variable names between different methods even if their purposes are near-identical; it just gets confusing.

@Grantly 2015-12-30 22:53:24

lol I disagree for so many reasons...However, no down vote... I respect your right to choose

@fat 2014-02-05 06:03:24

My practice is following:

  • if type of variable is simple (int, double, ...) I prefer variant b (inside).
    Reason: reducing scope of variable.

  • if type of variable is not simple (some kind of class or struct) I prefer variant a (outside).
    Reason: reducing number of ctor-dtor calls.

@Mark Sowul 2012-11-07 21:15:13

There is a difference in C# if you are using the variable in a lambda, etc. But in general the compiler will basically do the same thing, assuming the variable is only used within the loop.

Given that they are basically the same: Note that version b makes it much more obvious to readers that the variable isn't, and can't, be used after the loop. Additionally, version b is much more easily refactored. It is more difficult to extract the loop body into its own method in version a. Moreover, version b assures you that there is no side effect to such a refactoring.

Hence, version a annoys me to no end, because there's no benefit to it and it makes it much more difficult to reason about the code...

@enthusiasticgeek 2010-05-11 14:28:54

A) is a safe bet than B).........Imagine if you are initializing structure in loop rather than 'int' or 'float' then what?

like

typedef struct loop_example{

JXTZ hi; // where JXTZ could be another type...say closed source lib 
         // you include in Makefile

}loop_example_struct;

//then....

int j = 0; // declare here or face c99 error if in loop - depends on compiler setting

for ( ;j++; )
{
   loop_example loop_object; // guess the result in memory heap?
}

You are certainly bound to face problems with memory leaks!. Hence I believe 'A' is safer bet while 'B' is vulnerable to memory accumulation esp working close source libraries.You can check usinng 'Valgrind' Tool on Linux specifically sub tool 'Helgrind'.

@R. Carr 2010-01-16 06:01:16

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.

@Ajoy Bhatia 2010-11-18 21:28:07

A new reference is not allocated for each object, even if the the reference is declared within the 'for'-loop. In BOTH cases: 1) 'o' is a local variable and stack space is allocated once for it at the start of the function. 2) There is a new Object created in each iteration. So there is no difference in performance. For code organization, readability and maintainability, declaring the reference within the loop is better.

@Jesse C. Slicer 2010-12-17 16:53:55

While I can't speak for Java, in .NET the reference is not 'allocated' for each object in the first example. There is a single entry on the stack for that local (to the method) variable. For your examples, the IL created is identical.

@PhiLho 2009-01-02 17:41:30

A co-worker prefers the first form, telling it is an optimization, preferring to re-use a declaration.

I prefer the second one (and try to persuade my co-worker! ;-)), having read that:

  • It reduces scope of variables to where they are needed, which is a good thing.
  • Java optimizes enough to make no significant difference in performance. IIRC, perhaps the second form is even faster.

Anyway, it falls in the category of premature optimization that rely in quality of compiler and/or JVM.

@Powerlord 2009-01-02 16:16:59

In my opinion, b is the better structure. In a, the last value of intermediateResult sticks around after your loop is finished.

Edit: This doesn't make a lot of difference with value types, but reference types can be somewhat weighty. Personally, I like variables to be dereferenced as soon as possible for cleanup, and b does that for you,

@new123456 2011-10-08 03:24:41

sticks around after your loop is finished - although this doesn't matter in a language like Python, where bound names stick around until the function ends.

@Powerlord 2011-10-08 15:42:49

@new123456: The OP asked for Java specifics, even if the question was asked somewhat generically. Many C-derived languages have block-level scoping: C, C++, Perl (with the my keyword), C#, and Java to name 5 I've used.

@new123456 2011-10-08 17:34:03

I know - it was an observation, not a criticism.

@Mark 2009-01-02 16:25:15

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.

@Mark Davidson 2009-01-02 16:27:02

You beat me I was just about to post my results for profiling, I got more or less the same and yes surprisingly B is faster really would have thought A if I had needed to bet on it.

@Mark 2009-01-02 16:40:51

Ok cool, yeah i only looked at execution time, as R. Bemrose pointed out in A the variable sticks around after the loop has completed. Did you profile results tell you anything about memory usage ?

@Arkadiy 2009-01-02 16:47:02

Not much surprise - when variable is local to the loop, it does not need to be preserved after each iteration, so it can stay in a register.

@MGOwen 2010-05-05 00:53:00

+1 for actually testing it, not just an opinion/theory the OP could have made up himself.

@Philip 2012-08-19 04:13:37

What's the execution time after JIT kicks in?

@javatarz 2013-05-14 09:46:50

@GoodPerson to be honest, I'd like that to be done. I ran this test around 10 times on my machine for 50,000,000-100,000,000 iterations with almost an identical piece of code (that I would love to share with anyone who wants to run stats). The answers were split almost equally either way usually by a margin of 900ms (over 50M iterations) which isn't really much. Though my first thought is that it's going to be "noise", it might lean one by just a bit. This effort seems purely academic to me though (for most real life applications).. I'd love to see a result anyway ;) Anyone agree?

@Ali 2014-05-25 15:12:08

2 is nicer actually

@aandis 2014-05-27 12:46:25

Anyone has any idea why is B's performance better?

@Wolf 2014-06-04 15:34:14

-1 for choosing A without a good reason.

@Mark 2014-06-04 15:54:33

@Wolf You don't understand, I'm not saying one is better then the other. I'm just agreeing with Rabarberski that I tend to also code example 'A' way.

@Wolf 2014-06-04 20:34:58

Ok, then sorry about the downvote. Do you think, it's possible to improve your answer (my vote is locked now)? BTW: really measuring instead of guessing is worth to be +1ed. But from the developer's performance POV, I'd tend to use the B version, if there is no evidence of a performance hit (normally loop bodies are not such trivial).

@user6538026 2016-07-29 12:41:58

This doesn't make any sense. It would be better if you provided a statistic. Why on the earth should A be slower than B!?

@Leo 2016-11-22 14:45:32

Bet my finest pound sterling 99% upvoted w/o checking.

@Ted Hopp 2017-01-19 19:59:17

Unless you used a good microprofiling harness, there are all sorts of reasons that A performed better than B that are unrelated to the code differences. Did you try running the tests in reverse order? Did you run warm-up cycles before starting the timed tests? How did you handle the I/O times and did you account for that having its own (sometimes quite substantial) variability?

@Holger 2017-02-15 14:36:16

Showing test results without documenting the setup, is worthless. That’s especially true in this case, where both code fragments produce identical bytecode, so any measured difference is just a sign of insufficient test conditions.

@Collin Bell 2018-09-15 06:44:08

@TedHopp. Your comment stated A performed better than B when in reality B performed better than A.

@Ted Hopp 2018-09-16 00:37:52

@CollinBell - Yes, I got that wrong. However, it makes no difference to the point I was trying to make in my comment.

@Abgan 2009-01-02 16:13:07

Even if I know my compiler is smart enough, I won't like to rely on it, and will use the a) variant.

The b) variant makes sense to me only if you desperately need to make the intermediateResult unavailable after the loop body. But I can't imagine such desperate situation, anyway....

EDIT: Jon Skeet made a very good point, showing that variable declaration inside a loop can make an actual semantic difference.

@Triptych 2009-01-02 16:18:13

I would always use A (rather than relying on the compiler) and might also rewrite to:

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

This still restricts intermediateResult to the loop's scope, but doesn't redeclare during each iteration.

@Jon Skeet 2009-01-02 16:21:21

Do you conceptually want the variable to live for the duration of the loop instead of separately per iteration? I rarely do. Write code which reveals your intention as clearly as possible, unless you've got a very, very good reason to do otherwise.

@Rabarberski 2009-01-02 16:25:30

Ah, nice compromise, I never thought of this! IMO, the code does become a bit less visually 'clear' though)

@Triptych 2009-01-02 17:44:46

@Jon - I have no idea what the OP is actually doing with the intermediate value. Just thought it was an option worth considering.

@Collin Bell 2018-09-15 06:46:04

Your code smells like Richard Stallman's feet

@Jon Skeet 2009-01-02 16:16:53

It depends on the language and the exact use. For instance, in C# 1 it made no difference. In C# 2, if the local variable is captured by an anonymous method (or lambda expression in C# 3) it can make a very signficant difference.

Example:

using System;
using System.Collections.Generic;

class Test
{
    static void Main()
    {
        List<Action> actions = new List<Action>();

        int outer;
        for (int i=0; i < 10; i++)
        {
            outer = i;
            int inner = i;
            actions.Add(() => Console.WriteLine("Inner={0}, Outer={1}", inner, outer));
        }

        foreach (Action action in actions)
        {
            action();
        }
    }
}

Output:

Inner=0, Outer=9
Inner=1, Outer=9
Inner=2, Outer=9
Inner=3, Outer=9
Inner=4, Outer=9
Inner=5, Outer=9
Inner=6, Outer=9
Inner=7, Outer=9
Inner=8, Outer=9
Inner=9, Outer=9

The difference is that all of the actions capture the same outer variable, but each has its own separate inner variable.

@Royi Namir 2012-05-31 07:44:32

in example B (original question), does it actually creates a new variable each time ? what happening in the eyes of the stack ?

@nawfal 2014-07-08 18:10:58

@Jon, was it a bug in C# 1.0? Shouldn't ideally Outer be 9?

@Jon Skeet 2014-07-08 18:32:44

@nawfal: I don't know what you mean. Lambda expressions weren't in 1.0... and Outer is 9. What bug do you mean?

@Jon Skeet 2014-07-08 18:40:18

@nawfal: My point is that there weren't any language features in C# 1.0 where you could tell the difference between declaring a variable inside a loop and declaring it outside (assuming that both compiled). That changed in C# 2.0. No bug.

@nawfal 2014-07-08 18:42:25

@JonSkeet Oh yes, I get you now, I completely overlooked the fact that you cant close over variables like that in 1.0, my bad! :)

@Chris 2009-01-02 16:15:15

As a general rule, I declare my variables in the inner-most possible scope. So, if you're not using intermediateResult outside of the loop, then I'd go with B.

@Stew S 2009-01-02 16:14:05

I suspect a few compilers could optimize both to be the same code, but certainly not all. So I'd say you're better off with the former. The only reason for the latter is if you want to ensure that the declared variable is used only within your loop.

@SquidScareMe 2009-01-02 16:08:53

I think it depends on the compiler and is hard to give a general answer.

Related Questions

Sponsored Content

31 Answered Questions

[SOLVED] How to break out of nested loops in Java?

  • 2009-05-20 09:07:43
  • boutta
  • 986080 View
  • 1607 Score
  • 31 Answer
  • Tags:   java loops

22 Answered Questions

[SOLVED] Accessing the index in 'for' loops?

  • 2009-02-06 22:47:54
  • Joan Venge
  • 1543884 View
  • 2773 Score
  • 22 Answer
  • Tags:   python loops list

10 Answered Questions

34 Answered Questions

[SOLVED] How do I loop through or enumerate a JavaScript object?

24 Answered Questions

39 Answered Questions

[SOLVED] JavaScript closure inside loops – simple practical example

36 Answered Questions

[SOLVED] Differences between HashMap and Hashtable?

38 Answered Questions

[SOLVED] Loop through an array in JavaScript

20 Answered Questions

[SOLVED] What is the difference between call and apply?

9 Answered Questions

[SOLVED] Improve INSERT-per-second performance of SQLite?

Sponsored Content