运行一个带有大量迭代的空for循环,我在运行所需的时间内得到了截然不同的数字:
public static class Program
{
static void Main()
{
var sw = new Stopwatch();
sw.Start();
for (var i = 0; i < 1000000000; ++i)
{
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
}
}
以上将在我的机器上运行大约200ms,但如果我将其增加到1000000001,那么它需要 4x !然后,如果我将它设为1000000002,则再次降至200ms!
这个似乎在偶数次迭代中发生。如果我去for (var i = 1; i < 1000000001
,(注意从1开始而不是0)那么它是200ms。或者如果我i <= 1000000001
(注意小于或等于)那么它是200ms。或(var i = 0; i < 2000000000; i += 2)
。
这似乎仅在x64上,但在所有.NET版本上(至少)4.0。此外,只有在处于释放模式且分离调试器时才会出现。
UPDATE 我认为这可能是由于jit中的一些聪明的位移,但以下似乎反驳了:如果你做了一些事情,比如在该循环中创建一个对象,那么< em> 也需要大约4倍的时间:
public static class Program
{
static void Main()
{
var sw = new Stopwatch();
sw.Start();
object o = null;
for (var i = 0; i < 1000000000; i++)
{
o = new object();
}
sw.Stop();
Console.WriteLine(o); // use o so the compiler won't optimize it out
Console.WriteLine(sw.ElapsedMilliseconds);
}
}
这在我的机器上大约需要1秒钟,但随后增加1到1000000001需要 4秒。这是一个额外的3000毫秒,所以它实际上不是由于位移,因为这也会在原始问题中显示为3000毫秒的差异。
答案 0 :(得分:6)
这里有拆解:
00000031 xor eax,eax
for (var i = 0; i < 1000000001; ++i)
00000033 inc eax
00000035 cmp eax,3B9ACA01h
0000003a jl 0000000000000033
0000003c movzx eax,byte ptr [rbx+18h]
00000040 test eax,eax
00000042 je 0000000000000073
和
00000031 xor eax,eax
for (var i = 0; i < 1000000000; ++i)
00000033 add eax,4
00000036 cmp eax,3B9ACA00h
0000003b jl 0000000000000033
0000003d movzx eax,byte ptr [rbx+18h]
00000041 test eax,eax
00000043 je 0000000000000074
我看到的唯一区别是,在偶数循环中,循环索引一次递增4(add eax 4
)而不是一次递增1(inc eax
),因此它完成循环因此而快4倍。
这只是推测,但我认为它是unrolling the loop因子4.所以它将身体放置在循环中4次,并且增加速度快4倍。但由于身体是空的,空体时间4仍然是空的,你获得的收益比环路展开时的收益大得多。