Try-catch speeding up my code? 技术背景 在代码优化过程中,通常认为 try-catch
块会增加额外的开销,因为它需要处理异常情况。然而,在某些特定的 C# 代码中,使用 try-catch
块反而使代码运行速度加快。这一现象源于 C# 编译器生成局部变量存储的方式与 JIT(Just-in-time)编译器在相应 x86 代码中进行寄存器调度的方式之间的交互问题,导致局部变量的加载和存储代码生成不佳。
实现步骤 测试代码示例 以下是一个简单的测试代码,用于验证 try-catch
块对代码性能的影响:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 using System;using System.Diagnostics;class Program { static void Main (string [] args ) { int hundredMillion = 1000000 ; DateTime start = DateTime.Now; double sqrt; for (int i = 0 ; i < hundredMillion; i++) { sqrt = Math.Sqrt(DateTime.Now.ToOADate()); } DateTime end = DateTime.Now; double sqrtMs = (end - start).TotalMilliseconds; Console.WriteLine("Elapsed milliseconds: " + sqrtMs); DateTime start2 = DateTime.Now; double sqrt2; for (int i = 0 ; i < hundredMillion; i++) { try { sqrt2 = Math.Sqrt(DateTime.Now.ToOADate()); } catch (Exception e) { int br = 0 ; } } DateTime end2 = DateTime.Now; double sqrtMsTryCatch = (end2 - start2).TotalMilliseconds; Console.WriteLine("Elapsed milliseconds: " + sqrtMsTryCatch); Console.WriteLine("ratio is " + sqrtMsTryCatch / sqrtMs); Console.ReadLine(); } }
代码分析 运行上述代码,会发现使用 try-catch
块的代码运行时间可能更短。 通过反汇编代码可以进一步分析原因。例如,在一个斐波那契数列计算的代码中,快速版本使用 try-catch
块,在反汇编代码中使用了一对寄存器(esi,edi
)来存储局部变量,而慢速版本则没有使用这些寄存器,更多地使用了栈空间。 核心代码 以下是一个简化的斐波那契数列计算代码示例,展示 try-catch
块对性能的影响:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 using System;using System.Diagnostics;class Program { static int Fibo (int n ) { int a = 0 , b = 1 , c; for (int i = 0 ; i < n; i++) { c = a + b; a = b; b = c; } return a; } static void Main () { var stopwatch = Stopwatch.StartNew(); for (int i = 1 ; i < 100000000 ; i++) { Fibo(100 ); } stopwatch.Stop(); Console.WriteLine("Elapsed time without try-catch: " + stopwatch.Elapsed); stopwatch = Stopwatch.StartNew(); for (int i = 1 ; i < 100000000 ; i++) { try { Fibo(100 ); } catch { } } stopwatch.Stop(); Console.WriteLine("Elapsed time with try-catch: " + stopwatch.Elapsed); } }
最佳实践 性能测试 :在实际项目中,如果对性能有较高要求,建议对包含和不包含 try-catch
块的代码进行性能测试,以确定哪种方式更适合。代码优化 :如果发现 try-catch
块能提升性能,可以考虑合理使用。但要注意,不同的代码可能会有不同的结果,不能将其作为通用的加速技术。常见问题 为什么 try-catch
块会影响寄存器分配? JIT 编译器对于包含 try-catch
块的代码和不包含的代码在寄存器使用上有不同的假设,导致不同的寄存器分配选择。在某些情况下,这种分配选择有利于包含 try-catch
块的代码。
在 x64 系统上,try-catch
块对性能没有明显影响,为什么? x64 CLR 比 x86 CLR 快很多,并且有更多的寄存器(如 r9 - r15)。此外,x64 可以在一个寄存器(如 rax)中存储一个长整型,因此 try-catch
块对性能的影响不明显。
能否将使用 try-catch
块加速代码作为通用方法? 不可以。不同的代码可能会导致相反的效果,寄存器分配和影响它的因素是底层实现细节,很难确定哪种代码最终会运行得最快。