CSharp - trycatch加快我的代码?

  显示原文与译文双语对照的内容

我编写了一些代码来测试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块就像是this,当我包起来为循环


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 ) 。

抛靶的编辑:乔恩双向飞碟优良的 分析表明,try-catch是以某种方式使得 x86 CLR来使用CPU寄存器于有较优惠的方式在我们的具体例子( 我想我们还没有理解为什么) 。 我确认了发现 jon CLR没有这种差异,而且它比 x86 CLR快。 我还在Fibo方法中使用了 int 类型,而不是 long 类型,然后 x86 CLR与 x64 CLR一样快速。

时间:

其中一个 Roslyn 工程师专攻理解堆栈的使用的优化的关注了一下这并报告对我来说,似乎有一个问题之间的相互作用中 C# 编译器生成本地变量存储方式和 JIT 编译器的方式做登记安排在相应的x86代码。 结果是在局部变量的负载和存储上生成的代码生成不正确。

由于某些原因不清楚,当抖动知道块位于try-protected区域时,会避免出现问题代码生成路径。

这很奇怪。我们将跟踪抖动团队,看看是否能得到一个 Bug,这样他们就可以修正这个问题。

另外,我们正在改进Roslyn到 C# 和VB编译器的'确定局部变量什么时候可用的算法"短暂的"--",而不是在堆栈上分配一个特定的位置,而不是在激活期间分配一个特定的位置。 我们认为这种抖动将能够做得更好的寄存器分配和诸如此类的东西是否可以给它更好的提示有关什么时候局部变量可以由"死键"更早。

感谢你将这引起我们的注意,对奇怪的行为深表歉意。

乔恩的disassemblies所能获得的是,两个版本的快速版本使用一对寄存器( esi,edi ) 来存储一个本地变量中,缓慢的版本不在。区别

JIT编译器对包含try-catch块 vs 代码的代码进行了不同的假设,该代码不支持。 这使得它可以做出不同的寄存器分配选择。 在本例中,这将使用try-catch块的代码。 不同的代码可能导致相反的效果,因此我将不会将它的算作通用的speed-up技术。

最后,是很难确定哪个代码最终运行最快的。 别的寄存器分配影响因素,并根据它就是这样低级的实现细节,我没有看到任何具体的技术如何能够可靠地产生更快的代码。

例如考虑以下两种方法。 它们来自一个真实的例子:


interface IIndexed { int this[int index] { get; set; } }
struct StructArray : IIndexed { 
 public int[] Array;
 public int this[int index] {
 get { return Array[index]; }
 set { Array[index] = value; }
 }
}

static int Generic<T>(int length, T a, T b) where T : IIndexed {
 int sum = 0;
 for (int i = 0; i <length; i++)
 sum += a[i] * b[i];
 return sum;
}
static int Specialized(int length, StructArray a, StructArray b) {
 int sum = 0;
 for (int i = 0; i <length; i++)
 sum += a[i] * b[i];
 return sum;
}

一个是另一个的通用版本。 用 StructArray 替换泛型类型将使方法相同。 因为 StructArray 是一个值类型,它得到了它自己的通用方法编译版本。 但是实际运行时间比专用方法的时间长,但仅用于 x86. 对于 x64,计时几乎完全相同。 在其他情况下,我也观察到了x64的差异。

...