试着加速我的代码?

我写了一些代码来测试try-catch的影响,但看到了一些令人惊讶的结果

静态void Main(字符串[]args)
{
Thread.CurrentThread.Priority=ThreadPriority.Highest;
Process.GetCurrentProcess().PriorityClass=ProcessPriorityClass.RealTime;
长时间启动=0,停止=0,经过=0;
双平均值=0.0;
长温=Fibo(1);
对于(int i=1;i<100000000;i++)
{
start=Stopwatch.GetTimestamp();
温度=Fibo(100);
stop=Stopwatch.GetTimestamp();
经过=停止-启动;
平均值=平均值+((双)经过时间-平均值)/i;
}
控制台写入线(“经过:+avg”);
Console.ReadKey();
}
静态长Fibo(整数n)
{
长n1=0,n2=1,fibo=0;
n++;
对于(int i=1;i<n;i++)
{
n1=n2;
n2=fibo;
fibo=n1+n2;
}
返回fibo;
}

在我的计算机上,它始终打印出0.96左右的值

当我用try-catch块将for循环包装到Fibo()中时,如下所示:

静态长Fibo(int n)
{
长n1=0,n2=1,fibo=0;
n++;
尝试
{
对于(int i=1;i<n;i++)
{
n1=n2;
n2=fibo;
fibo=n1+n2;
}
}
捕获{}
返回fibo;
}

现在它一直打印出0.69…——它实际上跑得更快!但是为什么呢

注意:我使用发行版配置编译了这个文件,并直接运行了EXE文件(在VisualStudio外部)

编辑:Jon Skeet的优秀分析表明,try-catch在某种程度上导致x86 CLR在这种特定情况下以更有利的方式使用CPU寄存器(我认为我们还不了解原因)。我证实了Jon的发现,x64 CLR没有这种差异,而且比x86 CLR快。我还使用Fibo方法中的int类型而不是long类型进行了测试,然后x86 CLR的速度与x64 CLR一样快


更新:看起来这个问题已经被Roslyn解决了。同样的机器,同样的CLR版本——当使用VS 2013编译时,问题仍然存在,但当使用VS 2015编译时,问题消失了

一位专门研究堆栈使用优化的Roslyn工程师对此进行了研究,并向我报告说,C#编译器生成局部变量存储的方式与JIT编译器在相应x86代码中注册调度的方式之间的交互似乎存在问题。结果是局部变量的加载和存储产生了次优代码

由于一些我们都不清楚的原因,当抖动知道块处于try保护区域时,可以避免有问题的代码生成路径

这很奇怪。我们将与JITter团队跟进,看看是否可以输入一个bug,以便他们能够修复这个问题

此外,我们正在为Roslyn改进C#和VB编译器的算法,以确定局部变量何时可以变得“短暂”——也就是说,只是在堆栈上推送和弹出,而不是在激活期间在堆栈上分配特定位置。我们相信,如果我们能给出更好的提示,告诉它什么时候本地人可以更早地“死亡”,抖动将能够更好地完成寄存器分配等工作

感谢您提醒我们注意这一点,并为这种奇怪的行为道歉

发表评论