为什么这种性能差异?(异常捕获)

Dre*_*vak 7 c# performance exception-handling

在这里读了一个关于我们的计算机可以在一秒钟内完成什么事情的问题后,我做了一个小小的考试,我想了一会儿,我对结果感到非常惊讶.看:

捕获空异常的简单程序,需要将近一秒钟来完成1900次迭代:

for(long c = 0; c < 200000000; c++)
{
    try
    {
        test = null;
        test.x = 1;
    }
    catch (Exception ex)
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

或者,在进行分配之前检查test == null,相同的pogram可以在一秒内执行aprox 200000000次迭代.

for(long c = 0; c < 1900; c++)
{
    test = null;
    f (!(test == null))
    {
        test.x = 1;
    }
}
Run Code Online (Sandbox Code Playgroud)

任何人都有一个详细解释为什么这个巨大的差异?

编辑:在发布模式下运行测试,在Visual Studio之外我得到35000-40000次迭代与400000000次迭代(总是aprox)

注意我用蹩脚的PIV 3.06Ghz运行它

Jon*_*eet 12

除非你在调试器中运行,否则没有办法在1900次迭代中花费一秒钟.在调试器下运行性能测试是个坏主意.

编辑:请注意,这不是更改为发布版本的情况 - 这是在没有调试器的情况下运行的情况; 即按Ctrl-F5而不是F5.

话虽如此,当你可以很容易地避免它们时引发异常也是一个坏主意.

我对异常的表现的看法:如果你正确地使用它们,它们不应该导致重大的性能问题,除非你处于某种灾难性的情况(例如你正试图制作成千上万的web服务电话和网络已关闭).

调试器下的异常是昂贵的 - 无论如何,无论如何在Visual Studio中 - 由于计算是否进入调试器等,并且可能进行任何数量的堆栈分析,否则这是不必要的.无论如何,它们仍然有些昂贵,但你不应该投入足够的注意力.仍然有堆栈展开,相关的捕获处理程序等等 - 但这应该只在首先出现问题时才会发生.

编辑:当然,抛出一个异常仍然会让你每秒的迭代次数减少(虽然35000仍然是一个非常低的数字 - 我期望超过100K),因为在非异常情况下你几乎什么也没做.我们来看看这两个:

循环体的非异常版本

  • 将null赋给变量
  • 检查变量是否为空; 它是,所以回到循环的顶部

(正如评论中所提到的,JIT很可能无论如何都会优化它......)

例外版本:

  • 将null赋给变量
  • 解引用变量
    • 隐式检查无效
    • 创建一个异常对象
    • 检查要调用的任何已过滤的异常处理程序
    • 查找堆栈以获取要跳转到的catch块
    • 检查任何finally块
    • 适当分支

你看到的表现会更差,难道难怪吗?

现在将其与您执行大量工作(可能是IO,对象创建等)的更常见情况进行比较 - 并且可能会抛出异常.然后差异变得不那么重要了.