出于兴趣,我测试了for循环和while循环是否存在任何差异.是什么原因导致while循环在我的计算机(AMD Phenom II X6 1090T @ 3.20GHz)上比for循环长约2-2.5秒?他们不是在做同样的事情吗?你得到类似的结果吗?
此外,当我用x = null;循环语句替换循环语句时,while循环将明显更快.这里发生了什么?
当然,迭代次数非常多,但差异仍然非常显着?
static void Main(string[] args)
{
String x;
const Int64 FIVE_BN = 5000000000;
Int64 i = 0;
DateTime start = DateTime.Now;
for (; FIVE_BN > i; i++)
x = null; //Replace with only ; in both loops and the for loop is faster
Console.Out.WriteLine(FIVE_BN.ToString() + " times (for): " + (DateTime.Now - start));
i = 0;
start = DateTime.Now;
while(FIVE_BN > i++)
x = null; //Replace with only ; in both loops and the for loop is faster
Console.Out.WriteLine(FIVE_BN.ToString() + " times (while): " + (DateTime.Now - start));
Console.Read();
return;
}
Run Code Online (Sandbox Code Playgroud)
Sep*_*eph 11
虽然这完全是微观优化,但永远不会成为性能瓶颈.有趣的是,这两者实际上是不同的,有趣的是当你用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
}
Run Code Online (Sandbox Code Playgroud)
这非常有趣......它表明这两个功能确实不同.
现在,当我们用循环中的逻辑替换时,;我们得到以下提取的方法:
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;
}
Run Code Online (Sandbox Code Playgroud)
这表明循环与此配置基本相同的原因.
为了弄清楚它们在内联时是如何不同的(而不是提取到方法中),我们需要看看优化的CLR编码是什么样的(虽然优化器实际上可能会删除两个函数之间的任何显着差异)..这是以后的事情编辑.
编辑:
CIL揭示了差异:
For循环有,.maxstack 2但while循环有.maxstack 4,否则操作顺序有一点差异,因为while循环开始时发生的增量但for操作发生在循环结束时(更改内容)循环到Console.WriteLine(i)并看到While循环将从1打印但For循环将从0打印(尽管两者都执行相同数量的循环迭代).
当循环内容只是;两个循环在CIL中缩短了2行时删除了以下行(对于两个循环):
IL_0006: ldnull
IL_0007: stloc.0
Run Code Online (Sandbox Code Playgroud)
但是,当我们构建版本时,代码是非常不同的:
对于任一循环,x = null;和之间的区别;是什么,因为优化器已经注意到值永远不会变为非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
Run Code Online (Sandbox Code Playgroud)
和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
Run Code Online (Sandbox Code Playgroud)
因此我们可以看到优化的while循环比2循环的for循环更快,但它使用更多的堆栈空间.
这两者之间的差异似乎完全与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
}
Run Code Online (Sandbox Code Playgroud)
for构建时(在发布或调试中)此方法的CIL代码与while循环的CIL代码相同.
你的不同之处就在于此.For循环执行与完全相同的行为时,while循环执行完全相同.您注意到的差异完全是由于在调试中运行代码而不是发布,结合JIT并不总是与发布代码优化器一样高效.
我很喜欢这个问题,我从中学到了一些东西; 我希望其他人也这样做.+1
你可能想要打破cordbg(并小心地启用所有JIT优化)来查看生成的本机代码,以确定为什么会发生这种情况......但为什么要这么麻烦?在实际代码中,差异不会很大,因为您将在循环中进行实际工作.
微观优化完全不切实际的代码并不是一项富有成效的工作,IMO.即使微观优化实际代码通常也不富有成效,除非您已经确认这是瓶颈.