C# 试抓表演

C# 试抓表演,c#,performance,C#,Performance,MSDN上的一篇文章指出,只要不引发实际异常,您可以使用任意数量的try-catch块,并且不会产生任何性能成本。 因为我一直认为,即使没有抛出异常,try-catch也会对性能造成很小的影响,所以我做了一个小测试 private void TryCatchPerformance() { int iterations = 100000000; Stopwatch stopwatch = Stopwatch.StartNew();

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));
        }
因此,使用无尝试捕获块似乎毕竟更快?

我发现更奇怪的是,当我用更复杂的东西替换for循环体中的计算时,比如:
c+=I*(2*(int)Math.Floor((double)I))

差别远没有那么显著。
With try catch: 640
Without try catch: 655

我是做错了什么,还是有一个合乎逻辑的解释?

实际的计算是如此之小,以至于精确的测量非常棘手。在我看来,try-catch可能会在常规训练中增加一点固定的额外时间。我不知道异常是如何在C#中实现的,我冒昧地猜测,这主要是异常路径的初始化,可能只是JIT上的一个轻微负载


对于任何实际使用,花在计算上的时间都会超过摆弄try-catch所花的时间,因此try-catch的成本几乎为零。

JIT不会对“受保护的”/“try”块执行优化,我猜这取决于您在try/catch块中编写的代码,这将影响您的性能。

请注意,我只有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 ;
    }
}
分解这些:

// 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

我看到的主要区别是,a.cs在IL_0006时直接进入
ret
,而b.cs必须在IL_0013时离开。对于我的示例,我的最佳猜测是,
leave
在编译为机器代码时是一个(相对)昂贵的跳转——可能是这样,也可能不是这样,尤其是在for循环中。也就是说,try-catch没有固有的开销,但是跳过catch会有成本,就像任何条件分支一样。

有关try/catch块如何工作的讨论,请参阅, 有些实现的开销很高,有些实现的开销为零,
当没有异常发生时。

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

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


如果在尝试/捕获块中没有任何行为,那么“留待重试”与仅仅“重试”的额外成本将占主导地位,并且明显存在可测量的差异。但是,在try子句中存在某种行为的任何其他情况下,块本身的成本将完全被抵消

仅34毫秒的差异小于此类测试的误差幅度

正如您所注意到的,当您增加测试的持续时间时,差异就消失了,两组代码的性能实际上是相同的


在进行这种基准测试时,我尝试在代码的每一部分上循环至少20秒,最好更长,最好是几个小时。

问题首先在于测试代码。 您使用了stopwatch.appeased.millizes,它只显示所用时间的毫秒部分, 使用Total毫秒获取整个零件

如果没有抛出异常,则差异最小

但真正的问题是“我需要检查异常还是让C#处理异常抛出?”

显然。。。独自处理。。。 尝试运行以下命令:

private void TryCatchPerformance()
    {
        int iterations = 10000;
        textBox1.Text = "";
        Stopwatch stopwatch = Stopwatch.StartNew();
        int c = 0;
        for (int i = 0; i < iterations; i++)
        {
            try
            {   
                c += i / (i % 50);
            }
            catch (Exception)
            {

            }
        }
        stopwatch.Stop();
        Debug.WriteLine(String.Format("With try catch: {0}", stopwatch.Elapsed.TotalSeconds));

        Stopwatch stopwatch2 = Stopwatch.StartNew();
        int c2 = 0;
        for (int i = 0; i < iterations; i++)
        {
            int iMod50 = (i%50);
            if(iMod50 > 0)
                c2 += i / iMod50;
        }
        stopwatch2.Stop();
        Debug.WriteLine( String.Format("Without try catch: {0}", stopwatch2.Elapsed.TotalSeconds));

    }
private void TryCatchPerformance()
{
int迭代次数=10000次;
textBox1.Text=“”;
秒表秒表=Stopwatch.StartNew();
int c=0;
对于(int i=0;i0)
c2+=i/iMod50;
}
秒表2.Stop();
WriteLine(String.Format(“不带try-catch:{0}”,stopwatch2.appeased.TotalSeconds));
}
输出: 过时:看下面 带try catch:1.9938401

不带试接:8.92E-05

令人惊讶的是,只有10000个对象,还有200个例外

更正: 我在“调试”和“VS写入异常到输出”窗口上运行代码。。 这些是发布的结果 开销大大减少,但仍有7500%的改进

使用try catch:0.0546915

单独检查:0.0007294


通过try-catch抛出我自己的异常对象:0.0265229

,您只不过证明了基准测试比看起来要困难得多。对于初学者,您应该在两个模块反转的情况下重复测量。反转两个模块不会在输出中产生任何显著差异。你能详细解释一下为什么这很重要吗?晚会有点晚了。但这是因为JIT可能已经做出了影响第二次运行的优化决策。+1的努力,但for循环实际上是domi
// 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
private void TryCatchPerformance()
    {
        int iterations = 10000;
        textBox1.Text = "";
        Stopwatch stopwatch = Stopwatch.StartNew();
        int c = 0;
        for (int i = 0; i < iterations; i++)
        {
            try
            {   
                c += i / (i % 50);
            }
            catch (Exception)
            {

            }
        }
        stopwatch.Stop();
        Debug.WriteLine(String.Format("With try catch: {0}", stopwatch.Elapsed.TotalSeconds));

        Stopwatch stopwatch2 = Stopwatch.StartNew();
        int c2 = 0;
        for (int i = 0; i < iterations; i++)
        {
            int iMod50 = (i%50);
            if(iMod50 > 0)
                c2 += i / iMod50;
        }
        stopwatch2.Stop();
        Debug.WriteLine( String.Format("Without try catch: {0}", stopwatch2.Elapsed.TotalSeconds));

    }