试着抓住表现

Mez*_*Mez 25 c# performance

MSDN上的这篇文章指出,您可以根据需要使用尽可能多的try catch块,并且不会产生任何性能成本,因为不会引发任何实际异常.
因为我总是认为即使没有抛出异常,try-catch总是会受到很小的性能影响,所以我做了一点测试.

 private void TryCatchPerformance()
        {
            int iterations = 100000000;

            Stopwatch stopwatch = Stopwatch.StartNew();
            int c = 0;
            for (int i = 0; i < iterations; i++)
            {
                try
                {
                   // c += i * (2 * (int)Math.Floor((double)i));
                    c += i * 2;
                }
                catch (Exception ex)
                {
                    throw;
                }
            }
            stopwatch.Stop();
            WriteLog(String.Format("With try catch: {0}", stopwatch.ElapsedMilliseconds));

            Stopwatch stopwatch2 = Stopwatch.StartNew();
            int c2 = 0;
            for (int i = 0; i < iterations; i++)
            {
              //  c2 += i * (2 * (int)Math.Floor((double)i));
                c2 += i * 2;
            }
            stopwatch2.Stop();
            WriteLog(String.Format("Without try catch: {0}", stopwatch2.ElapsedMilliseconds));
        }
Run Code Online (Sandbox Code Playgroud)

我得到的输出:

With try catch: 68
Without try catch: 34
Run Code Online (Sandbox Code Playgroud)

所以似乎没有使用try-catch块似乎更快?

我发现更奇怪的是,当我用更复杂的东西替换for循环体中的计算时:c += i * (2 * (int)Math.Floor((double)i));
差异远远不那么显着.

With try catch: 640
Without try catch: 655
Run Code Online (Sandbox Code Playgroud)

我在这里做错了还是有合理的解释?

P.K*_*P.K 15

JIT不对'protected'/'try'块执行优化,我想根据你在try/catch块中编写的代码,这会影响你的性能.

  • 这个答案根本不是真的.优化不会被禁用,它们可能不适用于try块.添加try catch块不会禁用优化.添加try块可以更改代码的可能执行路径,这可能会也可能不会更改jit可以执行的优化集.看起来最大的问题是穿过try块本身的变量:EX int count = 0; 试试{... count ++; ...} finally {print(count); } (4认同)
  • 您可以参考以下链接:http://msmvps.com/blogs/peterritchie/archive/2007/06/22/performance-implications-of-try-catch-finally.aspx (3认同)

jri*_*sta 14

try/catch/finally/fault块本身在优化的发布程序集中基本上没有开销.虽然通常会为catch和finally块添加额外的IL,但是当没有抛出异常时,行为几乎没有差异.而不是一个简单的ret,通常有一个后来退休的假.

处理异常时会发生try/catch/finally块的真实成本.在这种情况下,必须创建异常,必须放置堆栈爬网标记,并且,如果处理异常并访问其StackTrace属性,则会引发堆栈遍历.最重的操作是堆栈跟踪,它跟随先前设置的堆栈爬网标记以构建StackTrace对象,该对象可用于显示错误发生的位置以及它通过的调用.

如果在try/catch块中没有任何行为,那么'leave to ret'与'ret'的额外成本将占主导地位,并且显然会有一个可衡量的差异.但是,在try子句中存在某种行为的任何其他情况下,块本身的成本将完全被否定.


Mar*_*off 6

请注意,我只提供Mono:

// a.cs
public class x {
    static void Main() {
        int x = 0;
        x += 5;
        return ;
    }
}


// b.cs
public class x {
    static void Main() {
        int x = 0;
        try {
            x += 5;
        } catch (System.Exception) {
            throw;
        }
        return ;
    }
}
Run Code Online (Sandbox Code Playgroud)

拆卸这些:

// a.cs
       default void Main ()  cil managed
{
    // Method begins at RVA 0x20f4
    .entrypoint
    // Code size 7 (0x7)
    .maxstack 3
    .locals init (
            int32   V_0)
    IL_0000:  ldc.i4.0
    IL_0001:  stloc.0
    IL_0002:  ldloc.0
    IL_0003:  ldc.i4.5
    IL_0004:  add
    IL_0005:  stloc.0
    IL_0006:  ret
} // end of method x::Main
Run Code Online (Sandbox Code Playgroud)

// b.cs
      default void Main ()  cil managed
{
    // Method begins at RVA 0x20f4
    .entrypoint
    // Code size 20 (0x14)
    .maxstack 3
    .locals init (
            int32   V_0)
    IL_0000:  ldc.i4.0
    IL_0001:  stloc.0
    .try { // 0
      IL_0002:  ldloc.0
      IL_0003:  ldc.i4.5
      IL_0004:  add
      IL_0005:  stloc.0
      IL_0006:  leave IL_0013

    } // end .try 0
    catch class [mscorlib]System.Exception { // 0
      IL_000b:  pop
      IL_000c:  rethrow
      IL_000e:  leave IL_0013

    } // end handler 0
    IL_0013:  ret
} // end of method x::Main
Run Code Online (Sandbox Code Playgroud)

我看到的主要区别是a.cs去直接retIL_0006,而b.cs必须leave IL_0013IL_006.我与最好的猜测我的例子,就是leave当编译成机器代码(相对)昂贵的跳跃-这可能会或可能不会是这样,尤其是在你的循环.也就是说,try-catch没有固有的开销,但跳过 catch会有成本,就像任何条件分支一样.


Ben*_*hes 5

实际计算量非常小,以至于精确测量非常棘手。在我看来,try catch 可能会为例程增加很少的固定额外时间。我会冒险猜测,不知道如何在 C# 中实现异常,这主要是异常路径的初始化,也许只是 JIT 上的轻微负载。

对于任何实际使用,花费在计算上的时间将压倒花在摆弄 try-catch 上的时间,以至于 try-catch 的成本可以接近于零。