.NET x64中的for循环性能奇怪:偶数迭代亲和力?

lob*_*ism 8 .net c# performance

运行具有大量迭代的空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);
    }
}
Run Code Online (Sandbox Code Playgroud)

以上将在我的机器上运行大约200ms,但如果我将它增加到1000000001,那么它需要4倍的时间!然后,如果我将它设为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.此外,它仅在处于释放模式且调试器已分离时才会出现.

更新我认为这可能是由于jit中的一些聪明的位移,但以下似乎反驳了:如果你做了类似在该循环内创建一个对象的东西,那么这也需要大约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);
    }
}
Run Code Online (Sandbox Code Playgroud)

这在我的机器上大约需要1秒钟,但随后增加1到1000000001需要4秒钟.这是一个额外的3000毫秒,所以它实际上不是由于位移,因为这也会在原始问题上显示为3000毫秒的差异.

Esa*_*ija 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 
Run Code Online (Sandbox Code Playgroud)

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 
Run Code Online (Sandbox Code Playgroud)

我看到的唯一区别是,在偶数循环中,循环索引一次增加4(add eax 4而不是每次增加1 inc eax),因此它完成循环4x的速度更快.

这只是推测,但我相信它将循环展开了 4倍.因此它将主体放置在循环中4次,并且仅增加4倍.但是因为身体是空的,空体时间4仍然是空的,你获得的收益比环路展开期望的要大得多.

  • 是的,这是循环展开工作.在[这个答案](http://stackoverflow.com/a/2057228/17034)中可以看出这个优化变坏了.一个更好的优化器将它分成两部分,一部分展开,另一部分负责最后几次迭代.但抖动优化器没有足够的时间来处理异常值. (3认同)
  • stackoverflow.com/questions/3423547/how-can-i-view-the-disassembly-of-optimised-jitted-net-code (2认同)