相同代码块执行的持续时间如此不同的原因是什么?

IL_*_*ent 10 .net c# .net-core

码:

 internal class Program
{
    private static void Main(string[] args)
    {
        const int iterCount = 999999999;

        var sum1 = 0;
        var sum2 = 0;

        using (new Dis())
        {
            var sw = DateTime.Now;
            for (var i = 0; i < iterCount; i++)
                sum1 += i;
            Console.WriteLine(sum1);
            Console.WriteLine(DateTime.Now - sw);
        }

        using (new Dis())
        {
            var sw = DateTime.Now;
            for (var i = 0; i < iterCount; i++)
                sum2 += i;
            Console.WriteLine(sum2);
            Console.WriteLine(DateTime.Now - sw);
        }

        Console.ReadLine();
    }

    private class Dis : IDisposable
    {
        public void Dispose(){}
    }
}
Run Code Online (Sandbox Code Playgroud)

相同使用中的两个相同的块.

输出:

2051657985
00:00:00.3690996
2051657985
00:00:02.2640266
Run Code Online (Sandbox Code Playgroud)

第二块需要2.2秒!但是如果要摆脱使用,持续时间变得相同(~0.3秒,就像第一个一样).我试过.net framework 4.5和.net core 1.1,在发布中,结果是一样的.

任何人都可以解释这种行为吗?

Han*_*ant 12

您必须查看抖动生成的机器代码以查看根本原因.使用工具>选项>调试>常规>取消选中抑制JIT优化选项.切换到发布版本.在第一个和第二个循环上设置断点.当它命中时使用Debug> Windows> Disassembly.

您将看到for循环体的机器代码:

                    sum1 += i;
00000035  add         esi,eax 
Run Code Online (Sandbox Code Playgroud)

和:

                    sum2 += i;
000000d9  add         dword ptr [ebp-24h],eax 
Run Code Online (Sandbox Code Playgroud)

或者换句话说,sum1变量存储在CPU寄存器中esi.但是sum2变量存储在内存中,在方法的堆栈帧上.巨大的,巨大的差异.寄存器非常快,内存很慢.堆栈帧的内存将位于L1缓存中,在访问该缓存的现代机器上具有3个周期的延迟.存储缓冲区将很快被大量写入所淹没,并导致处理器停止.

找到一种将变量保存在CPU寄存器中的方法是主要的抖动优化任务之一.但这有局限性,特别是x86几乎没有可用的寄存器.当它们全部用完时,抖动没有选择,只能使用内存.请注意,该using语句在引擎盖下有一个额外的隐藏局部变量,这就是它产生影响的原因.

理想情况下,抖动优化器可以更好地选择如何分配寄存器.将它们用于循环变量(它所做的)和和变量.一个提前编译器可以做到这一点,有足够的时间来执行代码分析.但是,即时编译器在严格的时间限制下运行.

基本的对策是:

  • 将代码分解为单独的方法,以便可以重用ESI之类的寄存器.
  • 删除抖动强制(项目>属性>构建选项卡>取消选中"首选32位").x64提供了8个额外的寄存器.

最后一个子弹对遗留的x64抖动(目标.NET 3.5使用它)有效,但不适用于4.6中首次提供的x64抖动重写(又名RYuJIT).重写是必要的,因为遗留抖动花费了太多时间来优化代码.令人失望的是,RyuJIT确实有令人失望的诀窍,我认为它的优化者可以在这里做得更好.