0

Which is a better / optimized way of declaring and using a variable in such a scenario?

int i;

for(i = 0; i < 10; i++)
    Console.WriteLine(i);

for(i = 0; i < 100; i++)
    Console.WriteLine(i);

OR

for(int i = 0; i < 10; i++)
    Console.WriteLine(i);

for(int i = 0; i < 100; i++)
    Console.WriteLine(i);
skywalker2909
  • 1,636
  • 2
  • 31
  • 46
  • Related [What's the benefit of declaring for the loop index variable outside the loop?](http://stackoverflow.com/questions/13150001/whats-the-benefit-of-declaring-for-the-loop-index-variable-outside-the-loop) – Martin Smith Jun 11 '13 at 16:08
  • @Servy - The dupe is slightly different as that only looks at one loop that uses the variable rather than if there is any benefit of declaring one variable for use in multiple loops. – Martin Smith Jun 11 '13 at 16:16
  • One interesting side note is that if your loop were to contain an anonymous delegate with a closure over the local variable, the former would behave differently from the latter, but only in the (current) latest version of the C# compiler. This is due to a recent breaking change that was made to prevent a common confusing bug due to closing over the changing loop variable when it intuitively appeared as if it had 'inner' scope. – Dan Bryant Jun 11 '13 at 16:18
  • 1
    @DanBryant Nope. This is a `for` loop, not a `foreach` loop. The scope of the variable has not changed between versions. – Servy Jun 11 '13 at 16:21
  • @Servy, ah, good to know, I thought the change affected both loop types. – Dan Bryant Jun 11 '13 at 16:28

3 Answers3

5

It's irrelevant from a performance standpoint.

First there's a reasonably high probability that the runtime will re-using the single variable even in the second example.

Next, even if it doesn't, allocating an additional int to the stack and then reclaiming it later costs literally nothing in performance. It makes the memory footprint of this method 4 bytes larger; that's it. If that's actually an issue for you (namely that you're running out of stack space) then you have larger problems that you need to resolve and this isn't the appropriate method to do so; it probably means you should turn a recursive function into a non-recursive function.

You should do whatever you find to be most readable or easier to write and least likely to cause errors or problems. For me that's virtually always the second case, but if you prefer writing out the first case (and your team is okay with that) then that's entirely up to you, just keep in mind that performance is an entirely non-factor here.

Servy
  • 202,030
  • 26
  • 332
  • 449
1

There are cases where you need the value of the counter variable outside of the for-loop...

int i;
for (i = 0; i < N; i++) {
    if (SomeCondition(i)) {
        break;
    }
}
DoSomeThingWith(i);

... then you would declare it outside of the for-loop. In all other cases I would declare it in the for-loop, as it makes the intention of this variable clearer.

for (int i = 0; i < N; i++) {
    ...
}

Here it is clear that this variable will not have a meaningful value outside of the loop (its scope is limited to the loop anyway). It is also clear that it will be used for this iteration purpose only and nothing else.

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
0

If you take a look in MSIL you can see the differences is the variable declaration:

    .method public hidebysig static 
    void task1 () cil managed 
{
    .locals init (
        [0] int32 i
    )

    IL_0000: ldc.i4.0
    IL_0001: stloc.0
    IL_0002: br.s IL_000e
    .loop
    {
        IL_0004: ldloc.0
        IL_0005: call void [mscorlib]System.Console::WriteLine(int32)
        IL_000a: ldloc.0
        IL_000b: ldc.i4.1
        IL_000c: add
        IL_000d: stloc.0

        IL_000e: ldloc.0
        IL_000f: ldc.i4.s 10
        IL_0011: blt.s IL_0004
    }

    IL_0013: ldc.i4.0
    IL_0014: stloc.0
    IL_0015: br.s IL_0021
    .loop
    {
        IL_0017: ldloc.0
        IL_0018: call void [mscorlib]System.Console::WriteLine(int32)
        IL_001d: ldloc.0
        IL_001e: ldc.i4.1
        IL_001f: add
        IL_0020: stloc.0

        IL_0021: ldloc.0
        IL_0022: ldc.i4.s 100
        IL_0024: blt.s IL_0017
    }

    IL_0026: ret
}

Second case:

    .method public hidebysig static 
    void task2 () cil managed 
{
    .locals init (
        [0] int32 i,
        [1] int32 i
    )

    IL_0000: ldc.i4.0
    IL_0001: stloc.0
    IL_0002: br.s IL_000e
    .loop
    {
        IL_0004: ldloc.0
        IL_0005: call void [mscorlib]System.Console::WriteLine(int32)
        IL_000a: ldloc.0
        IL_000b: ldc.i4.1
        IL_000c: add
        IL_000d: stloc.0

        IL_000e: ldloc.0
        IL_000f: ldc.i4.s 10
        IL_0011: blt.s IL_0004
    }

    IL_0013: ldc.i4.0
    IL_0014: stloc.1
    IL_0015: br.s IL_0021
    .loop
    {
        IL_0017: ldloc.1
        IL_0018: call void [mscorlib]System.Console::WriteLine(int32)
        IL_001d: ldloc.1
        IL_001e: ldc.i4.1
        IL_001f: add
        IL_0020: stloc.1

        IL_0021: ldloc.1
        IL_0022: ldc.i4.s 100
        IL_0024: blt.s IL_0017
    }

    IL_0026: ret
}

The difference is one local declared. And its load to memory. In the first case it loads teh variable twice.

Vitor Canova
  • 3,918
  • 5
  • 31
  • 55
  • And what's the performance implication of adding an additional local variable? Nothing. Also, just because there's an extra local in IL doesn't mean the runtime will create two locals; they could stay on the register, the jitter could reuse the one local, etc. Your big blob of IL doesn't actually prove anything. – Servy Jun 11 '13 at 16:32
  • Sorry if you don't feel confortable with my answer. Indeed looks like the runtime will use two locals or else it could define different. – Vitor Canova Jun 11 '13 at 17:21
  • Keep in mind optimizations aren't generally done by the compiler; it, by design, it's a part of it's job. It saves optimizations for the jitter, so if it would be done, that's where it would be. – Servy Jun 11 '13 at 17:27
  • But many optimizations are made by compiler too. Eric Lippert blog has a lot of those compiler optimizations. – Vitor Canova Jun 11 '13 at 18:32
  • Eric actually makes a strong point himself of stating that most optimizations *aren't* done by the compiler. It does some, but not a whole lot. It's a point he himself has made on a number of occasions. The point here is that if the compiler optimized it then that's fine, but if it doesn't you can't assert that the optimization isn't make; you need to ensure that the jitter doesn't optimize it if you want to assert that the actual executing code has two locals. (And, as my answer states, even if the optimization isn't make it doesn't affect performance anyway.) – Servy Jun 11 '13 at 18:44
  • In this case the compiler did not make any optimization. The jitter I don't have knowledge to make a point. ;) – Vitor Canova Jun 11 '13 at 18:58
  • Sure it does. It's able to determine that, at the time one variable is used for the first time another will never be used again, and that the variables are of the same type. I have no idea if it does it or not, but it's certainly within the realm of possibility. Heck, it's possible for the variable to only ever be enregistered and never be on the stack at all, which would again be re-using the same space. – Servy Jun 11 '13 at 19:01