我写了一些代码来测试 try-catch 的影响,但看到了一些令人惊讶的结果。
static void Main(string[] args)
{
Thread.CurrentThread.Priority = ThreadPriority.Highest;
Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.RealTime;
long start = 0, stop = 0, elapsed = 0;
double avg = 0.0;
long temp = Fibo(1);
for (int i = 1; i < 100000000; i++)
{
start = Stopwatch.GetTimestamp();
temp = Fibo(100);
stop = Stopwatch.GetTimestamp();
elapsed = stop - start;
avg = avg + ((double)elapsed - avg) / i;
}
Console.WriteLine("Elapsed: " + avg);
Console.ReadKey();
}
static long Fibo(int n)
{
long n1 = 0, n2 = 1, fibo = 0;
n++;
for (int i = 1; i < n; i++)
{
n1 = n2;
n2 = fibo;
fibo = n1 + n2;
}
return fibo;
}
在我的电脑上,这始终打印出一个大约 0.96 的值..
当我用这样的 try-catch block 将 for 循环包装在 Fibo() 中时:
static long Fibo(int n)
{
long n1 = 0, n2 = 1, fibo = 0;
n++;
try
{
for (int i = 1; i < n; i++)
{
n1 = n2;
n2 = fibo;
fibo = n1 + n2;
}
}
catch {}
return fibo;
}
现在它始终打印出 0.69...——它实际上运行得更快!但是为什么?
注意:我使用发布配置编译它并直接运行 EXE 文件(在 Visual Studio 之外)。
编辑:Jon Skeet's excellent analysis显示 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 代码中进行寄存器调度。结果是在本地的加载和存储上生成次优代码。
由于某些我们都不清楚的原因,当 JITter 知道 block 位于 try-protected 区域时,就会避免有问题的代码生成路径。
这很奇怪。我们将跟进 JITter 团队,看看我们是否可以输入错误,以便他们可以修复此问题。
此外,我们正在努力改进 Roslyn 对 C# 和 VB 编译器算法的改进,以确定何时可以使本地变量“短暂”——也就是说,只是在堆栈上压入和弹出,而不是分配一个特定的位置激活期间的堆栈。我们相信 JITter 将能够更好地完成寄存器分配以及诸如此类的工作,如果我们给它更好的提示关于何时可以让本地人更早“死亡”。
感谢您提请我们注意此事,并对奇怪的行为表示歉意。
关于c# - Try-catch 加速我的代码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8928403/