异常处理 vs 性能

27 浏览
0 Comments

异常处理 vs 性能

我知道异常会带来性能损耗,通常避免异常比将大量代码放到try/catch块中更高效,但是对于try块本身呢?即使它从未抛出异常,仅仅声明try/catch会有什么代价呢?

0
0 Comments

异常处理与性能之间的关系是一个常见的问题。一般来说,捕获异常的成本比抛出异常要高。这是因为大多数异常元数据的收集(例如获取堆栈跟踪等)只在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问的是当异常没有被抛出时的性能影响。

0
0 Comments

在创建一个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毫秒。这是一个非常小的差异。如果每次迭代实际上还在做一些简单的加法运算之外的事情,这样的差异甚至更不明显。我在调试和不调试的情况下得到了类似的结果。

0
0 Comments

异常处理的性能成本非常小。异常处理的主要成本是获取堆栈跟踪和其他元数据,这是一个直到实际抛出异常时才付出的成本。

但这将因语言和实现而异。为什么不在C#中编写一个简单的循环并自行计时呢?

是的。try实际上是免费的,因为它基本上是由在实际抛出异常时才检查的元数据处理的。

异常处理与性能之间的关系是一个常见的问题,特别是在需要处理大量异常的情况下。异常处理的性能成本可能会影响程序的整体性能,因此需要仔细考虑和优化。

异常处理的性能成本主要来自于获取堆栈跟踪和其他元数据的操作。这些操作在实际抛出异常之前是不会执行的,因此在没有异常发生时是没有成本的。然而,一旦发生异常,获取堆栈跟踪和其他元数据的成本将变得非常昂贵。

具体的性能成本会因语言和实现而异。不同的编程语言和异常处理机制可能会有不同的性能特点。因此,为了了解异常处理的性能成本,最好编写一些简单的代码来测试和计时。

在C#中,try块的性能成本非常小,因为它基本上是由在实际抛出异常时才检查的元数据处理的。这意味着try块本身并不会引入太多的性能开销。

然而,如果需要频繁地抛出和处理异常,那么异常处理的性能成本可能会变得更加显著。在这种情况下,可以考虑采取一些优化措施,例如减少异常的抛出次数,或者使用更高效的异常处理机制。

总之,异常处理的性能成本是需要注意和优化的一个方面。了解具体语言和实现的性能特点,以及采取适当的优化措施,可以帮助我们在保证程序正确性的同时提高性能。

0