Is开发者_运维知识库 the limit in the following loop (12332*324234) calculated once or every time the loop runs?
for(int i=0; i<12332*324234;i++)
{
//Do something!
}
For this it is calculated once, or more likely 0 times.
The compiler will optimize the multiplication away for you.
However this is not always the case if you have something like.
for(int i=0; i<someFunction();i++)
{
//Do something!
}
Because the compiler is not always able to see what someFunction()
will return. So even if someFunction()
does return a constant value every time, if the compiler doesn't know that, it cannot optimize it.
EDIT: As MainMa said in a comment, you are in this situation you can eliminate the cost by doing something like this:
int limit = someFunction();
for(int i=0; i<limit ;i++)
{
//Do something!
}
IF you are certain that the value of someFunction()
will not change during the loop.
This is one of the most commonly misunderstood behavior of loops in C#.
Here's what you need to know:
Loop bounds calculations, if non-constant and involving a variable, property-access, function call, or delegate invocation will re-compute the value of the bounds before each iteration of the loop.
So, for example:
for( int i = 0; i < 1234*1234; i++ ) { ... }
In this case, the expression 1234*1234
is a compile time constant, and as a result will not be recomputed on each iteration. In fact, it is calculated at compile time and replaced with a constant.
However, in this case:
int k = 10;
for( int i = 0; i < k; i++ ) { k -= 1; ... }
The value of k
has to be examined on each iteration. After all it can change .. in in this example does. Fortunately, since k
is simply a local variable, the cost of accessing it is very low - and in many cases it will either be retained in the local CPU cache or perhaps even maintained in a register (depending on how the JIT processes and emits the machine code).
In the case of something like the following:
IEnumerable<int> sequence = ...;
for( int i = 0; i < sequence.Count(); i++ ) { ... }
The cost of computing the sequence.Count()
can be quite expensive. And since it's evaluated on each iteration of the loop it can add up quickly.
The compiler cannot optimize away the calls to methods or properties that occur within the loop bounds expression because they may also change with each iteration. Imagine if the loop above was written as:
IEnumerable<int> sequence = ...;
for( int i = 0; i < sequence.Count(); i++ ) {
sequence = sequence.Concat( anotherItem );
}
Clearly sequence
is changing on each iteration ... and therefore the Count()
is likely to be different on each iteration. The compiler does not attempt to perform some static analysis to determine whether the loop bounds expression could be constant ... that would be extremely complicated, if not impossible. Instead, it assumes that if the expression is not a constant it must be evaluated on each iteration.
Now, in most cases, the cost of computing the bounds constraint for a loop is going to be relatively inexpensive, so you don't have to worry about it. But you do need to understand how the compiler treats loop bounds like this. Also, as a developer you need to be careful about using properties or methods that have side-effects as part of a bounds expression - after all, these side effects will occur on each iteration of the loop.
Actually that won't compile because it will overflow but if you make it a smaller number and open up Reflector you will find something like this.
for (int i = 0; i < 0x3cf7b0; i++)
{
}
There's two ways to interpret your question:
- Is the multiplication of 12332*324234 evaluated on each loop
- Is the expression in the loop condition evaluated on each loop
The answer to those two, different, questions are:
- No, it is in fact evaluated at compile-time, since it involves two constants
- Yes, if necessary, they are
In other words:
for (int i = 0; i < someString.Length; i++)
If the evaluation of someString.Length
is costly, it will incur a penalty on each loop iteration.
First of all the for loop in the question will not compile. But lets say it was
for (int i = 0; i < 20; i++)
{
Console.WriteLine(i);
i++;
}
VS
for (int i = 0; i < 10*2; i++)
{
Console.WriteLine(i);
i++;
}
the IL code is exactly same .
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 2
.locals init (
[0] int32 i,
[1] bool CS$4$0000)
L_0000: nop
L_0001: ldc.i4.0
L_0002: stloc.0
L_0003: br.s L_0016
L_0005: nop
L_0006: ldloc.0
L_0007: call void [mscorlib]System.Console::WriteLine(int32)
L_000c: nop
L_000d: ldloc.0
L_000e: ldc.i4.1
L_000f: add
L_0010: stloc.0
L_0011: nop
L_0012: ldloc.0
L_0013: ldc.i4.1
L_0014: add
L_0015: stloc.0
L_0016: ldloc.0
L_0017: ldc.i4.s 20
L_0019: clt
L_001b: stloc.1
L_001c: ldloc.1
L_001d: brtrue.s L_0005
L_001f: call int32 [mscorlib]System.Console::Read()
L_0024: pop
L_0025: ret
}
Now even if I replace it with a function in the loop like
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < Foo(); i++)
{
Console.WriteLine(i);
i++;
}
Console.Read();
}
private static int Foo()
{
return 20;
}
I get this IL code
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 2
.locals init (
[0] int32 i,
[1] bool CS$4$0000)
L_0000: nop
L_0001: ldc.i4.0
L_0002: stloc.0
L_0003: br.s L_0016
L_0005: nop
L_0006: ldloc.0
L_0007: call void [mscorlib]System.Console::WriteLine(int32)
L_000c: nop
L_000d: ldloc.0
L_000e: ldc.i4.1
L_000f: add
L_0010: stloc.0
L_0011: nop
L_0012: ldloc.0
L_0013: ldc.i4.1
L_0014: add
L_0015: stloc.0
L_0016: ldloc.0
L_0017: call int32 TestBedForums.Program::Foo()
L_001c: clt
L_001e: stloc.1
L_001f: ldloc.1
L_0020: brtrue.s L_0005
L_0022: call int32 [mscorlib]System.Console::Read()
L_0027: pop
L_0028: ret
}
which looks same to me.
So to me it looks like there is no difference in FOR LOOP with finite limit , another with calculate limit , and last with limit coming from a function.
So As long as the code copiles , you know what you are doing in a mammoth loop like this and you have enough memory in process i think it will work and produce same perf. (in C#)
As @Chaos noted, it won't compile. But if you use a representable expression (like 100*100), the result will probably be hard-coded. On Mono, the CIL includes:
IL_0007: ldloc.0
IL_0008: ldc.i4.1
IL_0009: add
IL_000a: stloc.0
IL_000b: ldloc.0
IL_000c: ldc.i4 10000
IL_0011: blt IL_0007
As you can see, 100 * 100 is hard-coded as 10000. However, in general it will be evaluated each time, and if a method or property is called, this probably can't be optimized away.
It looks to be calculated every time. Disassembly from VS2008.
0000003b nop
for (Int64 i = 0; i < (Int64)12332 * (Int64)324234; i++)
0000003c mov qword ptr [rsp+20h],0
00000045 jmp 000000000000005E
{
00000047 nop
bool h = false;
00000048 mov byte ptr [rsp+28h],0
}
0000004d nop
for (Int64 i = 0; i < (Int64)12332 * (Int64)324234; i++)
0000004e mov rax,qword ptr [rsp+20h]
00000053 add rax,1
00000059 mov qword ptr [rsp+20h],rax
0000005e xor ecx,ecx
00000060 mov eax,0EE538FB8h
00000065 cmp qword ptr [rsp+20h],rax
0000006a setl cl
0000006d mov dword ptr [rsp+2Ch],ecx
00000071 movzx eax,byte ptr [rsp+2Ch]
00000076 mov byte ptr [rsp+29h],al
0000007a movzx eax,byte ptr [rsp+29h]
0000007f test eax,eax
00000081 jne 0000000000000047
In addition to KLee1's answer, I'm writing the absolutely same thing just a little different to make it more human-readable:
for(int i=0, limit = someFunction(); i<limit ;i++)
{
//Do something!
}
someFunction()
is still only executed once, and the loop's head still fits comfortably in 1 line without confusing your teammates too much. Hopefully! ;-)
BTW, this works in C++, too.
Yes, comparison value is calculated every loop cycle.
If you absolutely have to use for-loop, which is rarely really necessary anymore, afaik there is only one "good" loop template:
for (int i = first(), last = last(); i != last; ++i)
{
// body
}
Notice the prefix increment too.
精彩评论