C#中的例外有多贵?

时间:2009-05-21 02:57:26

标签: c# exception

C#中的例外有多贵?只要堆叠不深,它们似乎并不是非常昂贵;但我读过相互矛盾的报道。

是否有尚未被驳回的明确报告?

10 个答案:

答案 0 :(得分:64)

Jon Skeet于2006年1月撰写Exceptions and Performance in .NET

更新了Exceptions and Performance Redux(感谢@Gulzar)

Rico Mariani在The True Cost of .NET Exceptions -- Solution

中说道

另请参阅:Krzysztof Cwalina - Design Guidelines Update: Exception Throwing

答案 1 :(得分:15)

在阅读了异常在性能方面代价高昂之后,我将一个简单的测量程序放在一起,非常类似于Jon Skeet published years ago。我在这里提到这一点主要是为了提供更新的数字。

程序花费了29914毫秒来处理一百万个异常,相当于每毫秒33个异常。这足够快,使异常成为大多数情况下返回代码的可行替代方案。

请注意,使用返回代码而不是异常,同一程序运行时间不到一毫秒,这意味着异常至少比返回代码慢30,000倍。正如Rico Mariani所强调的那样,这些数字也是最小数字。在实践中,抛出和捕获异常将花费更多时间。

在配备英特尔酷睿2双核T8100 @ 2,1 GHz和.NET 4.0的笔记本电脑上进行测量,版本不在调试器下运行(这会使速度变慢)。

这是我的测试代码:

static void Main(string[] args)
{
    int iterations = 1000000;
    Console.WriteLine("Starting " + iterations.ToString() + " iterations...\n");

    var stopwatch = new Stopwatch();

    // Test exceptions
    stopwatch.Reset();
    stopwatch.Start();
    for (int i = 1; i <= iterations; i++)
    {
        try
        {
            TestExceptions();
        }
        catch (Exception)
        {
            // Do nothing
        }
    }
    stopwatch.Stop();
    Console.WriteLine("Exceptions: " + stopwatch.ElapsedMilliseconds.ToString() + " ms");

    // Test return codes
    stopwatch.Reset();
    stopwatch.Start();
    int retcode;
    for (int i = 1; i <= iterations; i++)
    {
        retcode = TestReturnCodes();
        if (retcode == 1)
        {
            // Do nothing
        }
    }
    stopwatch.Stop();
    Console.WriteLine("Return codes: " + stopwatch.ElapsedMilliseconds.ToString() + " ms");

    Console.WriteLine("\nFinished.");
    Console.ReadKey();
}

static void TestExceptions()
{
    throw new Exception("Failed");
}

static int TestReturnCodes()
{
    return 1;
}

答案 2 :(得分:14)

我想我在营地里,如果异常的表现会影响你的应用程序,那么你就会抛出太多的 WAY 。例外情况应该是特殊条件,而不是常规错误处理。

也就是说,我对如何处理异常的回忆基本上是在堆栈中找到一个与抛出的异常类型相匹配的catch语句。因此,性能将受到影响最大的因素来自捕获量以及您拥有的捕获语句数量。

答案 3 :(得分:4)

就我而言,例外非常昂贵。我重写了这个:

public BlockTemplate this[int x,int y, int z]
{
    get
    {
        try
        {
            return Data.BlockTemplate[World[Center.X + x, Center.Y + y, Center.Z + z]];
        }
        catch(IndexOutOfRangeException e)
        {
            return Data.BlockTemplate[BlockType.Air];
        }
    }
}

进入这个:

public BlockTemplate this[int x,int y, int z]
{
    get
    {
        int ix = Center.X + x;
        int iy = Center.Y + y;
        int iz = Center.Z + z;
        if (ix < 0 || ix >= World.GetLength(0)
            || iy < 0 || iy >= World.GetLength(1)
            || iz < 0 || iz >= World.GetLength(2)) 
            return Data.BlockTemplate[BlockType.Air];
        return Data.BlockTemplate[World[ix, iy, iz]];
    }
}

注意到速度提升了大约30秒。启动时,此函数至少被调用32K次。代码并不清楚意图是什么,但节省的成本是巨大的。

答案 4 :(得分:3)

我做了自己的测量,以找出异常含义有多严重。我没有尝试测量抛出/捕捉异常的绝对时间。我最感兴趣的是如果在每次传递中抛出异常,循环将变得多慢。测量代码如下所示

     for(; ; ) {
        iValue = Level1(iValue);
        lCounter += 1;
        if(DateTime.Now >= sFinish) break;
     }

VS

     for(; ; ) {
        try {
           iValue = Level3Throw(iValue);
        }
        catch(InvalidOperationException) {
           iValue += 3;
        }
        lCounter += 1;
        if(DateTime.Now >= sFinish) break;
     }

差异是20倍。第二个片段慢了20倍。

答案 5 :(得分:3)

C#中的Barebones异常对象相当轻量级;它通常是封装InnerException的能力,当对象树变得太深时,它会使它变得沉重。

至于一个明确的报告,我不知道任何,虽然粗略的dotTrace配置文件(或任何其他探查器)的内存消耗和速度将相当容易。

答案 6 :(得分:2)

出现异常的性能似乎是在生成异常对象时(尽管太小而不能在90%的时间内引起任何问题)。因此,建议对您的代码进行概要分析 - 如果异常 导致性能损失,则编写一个不使用异常的新高性能方法。 (想到的一个例子是(引入TryParse以解决使用异常的Parse的perf问题)

在大多数情况下,大多数情况下do not cause significant performance hits都有例外情况 - 所以MS Design Guideline是通过抛出异常来报告失败

答案 7 :(得分:1)

只提供我的个人经历: 我正在使用NewtonSoft开发一个解析JSON文件并从中提取数据的程序。

我重写了这个:

  • 选项1,有例外。
try
{
    name = rawPropWithChildren.Value["title"].ToString();
}
catch(System.NullReferenceException)
{
    name = rawPropWithChildren.Name;
}

对此:

  • 选项2,无例外。
if(rawPropWithChildren.Value["title"] == null)
{
    name = rawPropWithChildren.Name;
}
else
{
    name = rawPropWithChildren.Value["title"].ToString();
}

Ofc您实际上没有上下文可以判断,但这是我的结果:
处于调试模式

  • 选项1,但有例外。 38.50秒

  • 选项2,但有例外。 06.48s

为了提供一些背景信息,我正在使用数千个可以为空的json属性。异常抛出过多,例如在执行时间的15%之内。好吧,不是很精确,但是它们被扔了太多次了。 我想解决此问题,所以我更改了代码,但我不明白为什么执行时间如此之快。那是因为我对异常的处理不善。

因此,我从中学到的东西是:仅在特殊情况下以及无法用简单的条件语句进行测试的情况下,才需要使用异常。还必须尽可能少地抛出它们。

这对您来说是一个随机的故事,但是我想从现在开始在我的代码中使用异常之前,我肯定会三思而后行!

答案 8 :(得分:0)

我最近在一个求和循环中测量了C#异常(抛出和捕获),该循环在每次加法时都会引发算术溢出。在四核笔记本电脑上,算术溢出的抛出和捕获约为8.5微秒= 117 KiloExceptions /秒。

答案 9 :(得分:0)

异常的代价很高,但是要在异常代码和返回代码之间进行选择时,异常代码的功能更多。

从历史上讲,该参数是,异常可确保代码被强制处理情况,而返回代码可以忽略,我从不赞成使用这些参数,因为没有程序员会故意忽略并破坏其代码-特别是一支优秀的测试团队/或编写良好的测试用例绝对不会忽略返回代码。

从现代编程实践的角度出发,不仅要考虑异常例外的代价,而且还要考虑其可行性。

1st。 由于大多数前端都将与抛出异常的API断开连接。例如使用Rest API的移动应用。相同的api也可以用于基于Angular js的Web前端。

任何一种情况都将首选返回码而不是异常。

第二。 如今,黑客随机尝试破坏所有Web实用程序。在这种情况下,如果他们不断攻击您的应用程序登录api,并且应用程序不断抛出异常,那么您每天将要处理成千上万的异常。当然,许多人会说防火墙会处理此类攻击,但是并非所有人都在花钱来管理专用防火墙或昂贵的反垃圾邮件服务,因此最好为这些情况准备代码。