虽然这完全是一个微不足道的优化问题。有趣的是,这两个实际上是不同的,有趣的是,当你提取方法均环与VS2010我得到如下:
private static String forLoop(ref Int64 i)
{
String x;
for (; FIVE_BN > i; i++)
x = null; //Replace with only ; in both loops and the for loop is faster
return x;
}
private static void whileloop(ref String x, ref Int64 i)
{
while (FIVE_BN > i++)
x = null; //Replace with only ; in both loops and the for loop is faster
}
这是很有趣......这表明,这两种功能是确有不同。
现在当我们;
替换循环的逻辑,我们得到以下的提取方法代替:
private static Int64 forLoopShort(Int64 i)
{
for (; FIVE_BN > i; i++)
; //Replace with only ; in both loops and the for loop is faster
return i;
}
private static Int64 whileLoopShort(Int64 i)
{
while (FIVE_BN > i++)
; //Replace with only ; in both loops and the for loop is faster
return i;
}
这说明了为什么循环运行基本上与此配置相同。我们需要看看优化后的CLR编码看起来是什么样子(尽管优化器可能实际上删除了这两个函数之间的任何显着差异)。这就是为什么在内联(而不是提取到方法中)时它们是如何不同的用于以后的编辑。
编辑:
的CIL揭示了差异:
For循环具有.maxstack 2
但while循环具有.maxstack 4
,否则在操作顺序有点性差异,由于事实,增量while
发生在循环的开始,但for
操作发生在循环结束时(将循环的内容更改为Console.WriteLine(i)
并查看While循环将从1打印,但For循环将从0打印(两者都会尽管循环次数相同)
当环路内容只是一个;
两个环路是2线短于CIL除去了以下行(两个回路):
IL_0006: ldnull
IL_0007: stloc.0
然而,当我们建立在释放代码非常不同:
对于任一循环,x = null;
和;
之间的区别不是什么,因为优化程序已经注意到该值从不变为非空值。是
for和while循环优化的区别如下:
CIL for
循环:
IL_0000: ldc.i4.0
IL_0001: conv.i8
IL_0002: stloc.0
IL_0003: br.s IL_000a
IL_0005: ldloc.0
IL_0006: ldc.i4.1
IL_0007: conv.i8
IL_0008: add
IL_0009: stloc.0
IL_000a: ldc.i8 0x12a05f200
IL_0013: ldloc.0
IL_0014: bgt.s IL_0005
IL_0016: ret
而且CIL while
循环:
IL_0000: ldc.i4.0
IL_0001: conv.i8
IL_0002: stloc.0
IL_0003: ldc.i8 0x12a05f200
IL_000c: ldloc.0
IL_000d: dup
IL_000e: ldc.i4.1
IL_000f: conv.i8
IL_0010: add
IL_0011: stloc.0
IL_0012: bgt.s IL_0003
IL_0014: ret
所以我们可以看到,一个优化while循环比for循环快2个操作,但它使用更多的堆栈空间。
这两者之间的区别似乎完全与i++
发生位置的差异有关。
事实上,这是通过使一个新的方法证实:
private static void forLoopVeryShort()
{
string x;
Int64 i = 0;
for (; FIVE_BN > i++;)
; //Replace with only ; in both loops and the for loop is faster
}
为内置当此for
方法(在任一释放或调试)CIL代码是相同的,所述while
循环。
有谎言你的区别。当循环执行完全相同的行为时,循环执行与while循环完全相同。您注意到的差异完全是由于在调试中运行代码而不是发布,与JIT并不总是像发布代码优化器一样高效。
我很喜欢这个问题,我从中学到了一些东西;我希望别人也这样做。 +1
不要使用'DateTime'作为基准 - 使用专门用于这种用途的'Stopwatch'类。 – Oded 2012-01-09 10:18:45
也许某些东西已经被你的编译器优化了。 – 2012-01-09 10:18:46
只需几个指针,尝试使用'Stopwatch'进行性能计时,并执行多次运行以过滤掉正在进行JIT运行的冷启动(基本忽略第一次运行)。 – 2012-01-09 10:19:00