异常处理 vs 性能
异常处理与性能之间的关系是一个常见的问题。一般来说,捕获异常的成本比抛出异常要高。这是因为大多数异常元数据的收集(例如获取堆栈跟踪等)只在try-catch块的一侧发生(而不是在throw块的一侧)。
堆栈展开实际上是非常快的 - CLR(公共语言运行时)遍历调用栈并只关注找到的finally块;在纯try-finally块中,运行时在任何时候都不会尝试“完成”异常(例如元数据等)。
据我所记,在具有过滤器的try-catch块(例如“catch (FooException) {}”)也同样昂贵 - 即使它们不对异常做任何处理。
我敢说,具有以下代码块的方法(称之为CatchesAndRethrows):
try { ThrowsAnException(); } catch { throw; }
在另一个方法中可能导致更快的堆栈遍历,例如:
try { CatchesAndRethrows(); } catch (Exception ex) // 运行时已经完成了大部分工作。 { // 一些复杂逻辑 }
一些数字:
With: 0.13905ms Without: 0.096ms Percent difference: 144%
这是我运行的基准测试(记住,在发布模式下 - 不带调试运行):
static void Main(string[] args) { Stopwatch withCatch = new Stopwatch(); Stopwatch withoutCatch = new Stopwatch(); int iterations = 20000; for (int i = 0; i < iterations; i++) { if (i % 100 == 0) { Console.Write("{0}%", 100 * i / iterations); Console.CursorLeft = 0; Console.CursorTop = 0; } CatchIt(withCatch, withoutCatch); } Console.WriteLine("With: {0}ms", ((float)(withCatch.ElapsedMilliseconds)) / iterations); Console.WriteLine("Without: {0}ms", ((float)(withoutCatch.ElapsedMilliseconds)) / iterations); Console.WriteLine("Percent difference: {0}%", 100 * withCatch.ElapsedMilliseconds / withoutCatch.ElapsedMilliseconds); Console.ReadKey(true); } static void CatchIt(Stopwatch withCatch, Stopwatch withoutCatch) { withCatch.Start(); try { FinallyIt(withoutCatch); } catch { } withCatch.Stop(); } static void FinallyIt(Stopwatch withoutCatch) { try { withoutCatch.Start(); ThrowIt(withoutCatch); } finally { withoutCatch.Stop(); } } private static void ThrowIt(Stopwatch withoutCatch) { throw new NotImplementedException(); }
我认为OP问的是当异常没有被抛出时的性能影响。
在创建一个ASP.NET Web应用程序时,我不小心将一个非常长的循环放在了try / catch块中。即使循环并不生成异常,但完成循环所需的时间太长。当我回头看到了被循环包围的try / catch块时,我改变了做法,将循环放在try / catch块中。性能有了很大的改善。你可以自己尝试一下:做一些类似下面的事情:
int total; DateTime startTime = DateTime.Now; for(int i = 0; i < 20000; i++) { try { total += i; } catch { // 什么都不做 } } Console.Write((DateTime.Now - startTime).ToString());
然后将try / catch块删除。你会看到一个很大的差别!
嗯,我刚刚在.Net 2.0上尝试了一下(使用了Stopwatch
)。进行了50000次20000次循环迭代的尝试,不使用try-catch
花费了4184毫秒,使用try-catch
花费了4363毫秒。这是一个非常小的差异。如果每次迭代实际上还在做一些简单的加法运算之外的事情,这样的差异甚至更不明显。我在调试和不调试的情况下得到了类似的结果。
异常处理的性能成本非常小。异常处理的主要成本是获取堆栈跟踪和其他元数据,这是一个直到实际抛出异常时才付出的成本。
但这将因语言和实现而异。为什么不在C#中编写一个简单的循环并自行计时呢?
是的。try实际上是免费的,因为它基本上是由在实际抛出异常时才检查的元数据处理的。
异常处理与性能之间的关系是一个常见的问题,特别是在需要处理大量异常的情况下。异常处理的性能成本可能会影响程序的整体性能,因此需要仔细考虑和优化。
异常处理的性能成本主要来自于获取堆栈跟踪和其他元数据的操作。这些操作在实际抛出异常之前是不会执行的,因此在没有异常发生时是没有成本的。然而,一旦发生异常,获取堆栈跟踪和其他元数据的成本将变得非常昂贵。
具体的性能成本会因语言和实现而异。不同的编程语言和异常处理机制可能会有不同的性能特点。因此,为了了解异常处理的性能成本,最好编写一些简单的代码来测试和计时。
在C#中,try块的性能成本非常小,因为它基本上是由在实际抛出异常时才检查的元数据处理的。这意味着try块本身并不会引入太多的性能开销。
然而,如果需要频繁地抛出和处理异常,那么异常处理的性能成本可能会变得更加显著。在这种情况下,可以考虑采取一些优化措施,例如减少异常的抛出次数,或者使用更高效的异常处理机制。
总之,异常处理的性能成本是需要注意和优化的一个方面。了解具体语言和实现的性能特点,以及采取适当的优化措施,可以帮助我们在保证程序正确性的同时提高性能。