代码之家  ›  专栏  ›  技术社区  ›  Platinum Azure

在C#中尝试/最终的开销?

  •  69
  • Platinum Azure  · 技术社区  · 15 年前

    try / catch 和 / 抓住 finally . 我知道肯定有一个 尝试 / 最后 (尤其是因为 using

    我们也看到关于 the overhead of try/catch and exceptions 。

    假设没有例外 尝试 最后 语句在离开 阻塞(有时通过从函数返回)?

    再说一次,我只问 尝试 / ,不 抓住

    谢谢!

    好吧,我会尽量展示我的用例。

    我应该用哪一个, DoWithTryFinally 或 DoWithoutTryFinally

    public bool DoWithTryFinally()
    {
      this.IsBusy = true;
    
      try
      {
        if (DoLongCheckThatWillNotThrowException())
        {
          this.DebugLogSuccess();
          return true;
        }
        else
        {
          this.ErrorLogFailure();
          return false;
        }
      }
      finally
      {
        this.IsBusy = false;
      }
    }
    
    public bool DoWithoutTryFinally()
    {
      this.IsBusy = true;
    
      if (DoLongCheckThatWillNotThrowException())
      {
        this.DebugLogSuccess();
    
        this.IsBusy = false;
        return true;
      }
      else
      {
        this.ErrorLogFailure();
    
        this.IsBusy = false;
        return false;
      }
    }
    

    这个例子过于简单化,因为只有两个返回点,但想象一下如果有四个。。。或者十。。。或者一百个。

    在某个时候我想用 尝试 / 原因如下:

    • 保持干燥原则(特别是出口点的数量越高)
    • this.Working 设置为 false .

    性能问题、可维护性和干原则, 对于多少出口点(特别是如果我) 可以 尝试 / ?

    编辑2: 这个。工作 到 this.IsBusy . 抱歉,忘了提到这是多线程的(尽管只有一个线程真正调用该方法);其他线程将轮询以查看对象是否正在执行其工作。如果工作按预期进行,则返回值只是成功或失败。

    6 回复  |  直到 9 年前
        1
  •  95
  •   plinth    15 年前

    为什么不看看你到底得到了什么?

        static void Main(string[] args)
        {
            int i = 0;
            try
            {
                i = 1;
                Console.WriteLine(i);
                return;
            }
            finally
            {
                Console.WriteLine("finally.");
            }
        }
    

    下面是调试生成中得到的IL:

    .method private hidebysig static void Main(string[] args) cil managed
    {
        .entrypoint
        .maxstack 1
        .locals init ([0] int32 i)
        L_0000: nop 
        L_0001: ldc.i4.0 
        L_0002: stloc.0 
        L_0003: nop 
        L_0004: ldc.i4.1 
        L_0005: stloc.0 
        L_0006: ldloc.0 // here's the WriteLine of i 
        L_0007: call void [mscorlib]System.Console::WriteLine(int32)
        L_000c: nop 
        L_000d: leave.s L_001d // this is the flavor of branch that triggers finally
        L_000f: nop 
        L_0010: ldstr "finally."
        L_0015: call void [mscorlib]System.Console::WriteLine(string)
        L_001a: nop 
        L_001b: nop 
        L_001c: endfinally 
        L_001d: nop 
        L_001e: ret 
        .try L_0003 to L_000f finally handler L_000f to L_001d
    }
    

    下面是JIT在debug中运行时生成的程序集:

    00000000  push        ebp 
    00000001  mov         ebp,esp 
    00000003  push        edi 
    00000004  push        esi 
    00000005  push        ebx 
    00000006  sub         esp,34h 
    00000009  mov         esi,ecx 
    0000000b  lea         edi,[ebp-38h] 
    0000000e  mov         ecx,0Bh 
    00000013  xor         eax,eax 
    00000015  rep stos    dword ptr es:[edi] 
    00000017  mov         ecx,esi 
    00000019  xor         eax,eax 
    0000001b  mov         dword ptr [ebp-1Ch],eax 
    0000001e  mov         dword ptr [ebp-3Ch],ecx 
    00000021  cmp         dword ptr ds:[00288D34h],0 
    00000028  je          0000002F 
    0000002a  call        59439E21 
    0000002f  xor         edx,edx 
    00000031  mov         dword ptr [ebp-40h],edx 
    00000034  nop 
            int i = 0;
    00000035  xor         edx,edx 
    00000037  mov         dword ptr [ebp-40h],edx 
            try
            {
    0000003a  nop 
                i = 1;
    0000003b  mov         dword ptr [ebp-40h],1 
                Console.WriteLine(i);
    00000042  mov         ecx,dword ptr [ebp-40h] 
    00000045  call        58DB2EA0 
    0000004a  nop 
                return;
    0000004b  nop 
    0000004c  mov         dword ptr [ebp-20h],0 
    00000053  mov         dword ptr [ebp-1Ch],0FCh 
    0000005a  push        4E1584h 
    0000005f  jmp         00000061 
            }
            finally
            {
    00000061  nop 
                Console.WriteLine("finally.");
    00000062  mov         ecx,dword ptr ds:[036E2088h] 
    00000068  call        58DB2DB4 
    0000006d  nop 
            }
    0000006e  nop 
    0000006f  pop         eax 
    00000070  jmp         eax 
    00000072  nop 
        }
    00000073  nop 
    00000074  lea         esp,[ebp-0Ch] 
    00000077  pop         ebx 
    00000078  pop         esi 
    00000079  pop         edi 
    0000007a  pop         ebp 
    0000007b  ret 
    0000007c  mov         dword ptr [ebp-1Ch],0 
    00000083  jmp         00000072 
    

    现在,如果我注释掉try-finally和return,我将从JIT获得几乎相同的程序集。您将看到的区别是跳转到finally块和一些代码,以确定finally执行后的去向。所以你说的是微小的差异。在release中,跳转到finally将得到优化的out-braces是nop指令,因此这将成为跳转到下一条指令,这也是nop-这是一个简单的窥视孔优化。pop eax和jmp eax同样便宜。

        {
    00000000  push        ebp 
    00000001  mov         ebp,esp 
    00000003  push        edi 
    00000004  push        esi 
    00000005  push        ebx 
    00000006  sub         esp,34h 
    00000009  mov         esi,ecx 
    0000000b  lea         edi,[ebp-38h] 
    0000000e  mov         ecx,0Bh 
    00000013  xor         eax,eax 
    00000015  rep stos    dword ptr es:[edi] 
    00000017  mov         ecx,esi 
    00000019  xor         eax,eax 
    0000001b  mov         dword ptr [ebp-1Ch],eax 
    0000001e  mov         dword ptr [ebp-3Ch],ecx 
    00000021  cmp         dword ptr ds:[00198D34h],0 
    00000028  je          0000002F 
    0000002a  call        59549E21 
    0000002f  xor         edx,edx 
    00000031  mov         dword ptr [ebp-40h],edx 
    00000034  nop 
            int i = 0;
    00000035  xor         edx,edx 
    00000037  mov         dword ptr [ebp-40h],edx 
            //try
            //{
                i = 1;
    0000003a  mov         dword ptr [ebp-40h],1 
                Console.WriteLine(i);
    00000041  mov         ecx,dword ptr [ebp-40h] 
    00000044  call        58EC2EA0 
    00000049  nop 
            //    return;
            //}
            //finally
            //{
                Console.WriteLine("finally.");
    0000004a  mov         ecx,dword ptr ds:[034C2088h] 
    00000050  call        58EC2DB4 
    00000055  nop 
            //}
        }
    00000056  nop 
    00000057  lea         esp,[ebp-0Ch] 
    0000005a  pop         ebx 
    0000005b  pop         esi 
    0000005c  pop         edi 
    0000005d  pop         ebp 
    0000005e  ret 
    

        2
  •  53
  •   Brian Rasmussen    15 年前

    所以假设有一个开销。你要停止使用 finally

    IMO性能指标只有在您可以在不同选项之间进行选择时才是相关的。我看不出你怎么能得到 最后 最后 .

        3
  •  28
  •   Andrew Barber Eric Lafortune    15 年前

    try/finally try/catch/finally 只要没有抛出异常。

    我有一个快速的个人资料应用程序,我刚才做了测试,在一个紧密的循环,它真的没有增加任何执行时间。

    我会再发一次,但很简单;只要运行一个紧密的循环来做一些事情,用 尝试/抓住/最后 它不会在循环中抛出任何异常,并根据没有 .

        4
  •  11
  •   Nicholas Petersen    14 年前

    让我们把一些基准数字放在这里。这个基准表明,实际上,try/finally的时间与调用空函数的开销一样小(可能更好的说法是:“跳转到下一条指令”,正如IL专家上面所说)。

                static void RunTryFinallyTest()
                {
                    int cnt = 10000000;
    
                    Console.WriteLine(TryFinallyBenchmarker(cnt, false));
                    Console.WriteLine(TryFinallyBenchmarker(cnt, false));
                    Console.WriteLine(TryFinallyBenchmarker(cnt, false));
                    Console.WriteLine(TryFinallyBenchmarker(cnt, false));
                    Console.WriteLine(TryFinallyBenchmarker(cnt, false));
    
                    Console.WriteLine(TryFinallyBenchmarker(cnt, true));
                    Console.WriteLine(TryFinallyBenchmarker(cnt, true));
                    Console.WriteLine(TryFinallyBenchmarker(cnt, true));
                    Console.WriteLine(TryFinallyBenchmarker(cnt, true));
                    Console.WriteLine(TryFinallyBenchmarker(cnt, true));
    
                    Console.ReadKey();
                }
    
                static double TryFinallyBenchmarker(int count, bool useTryFinally)
                {
                    int over1 = count + 1;
                    int over2 = count + 2;
    
                    if (!useTryFinally)
                    {
                        var sw = Stopwatch.StartNew();
                        for (int i = 0; i < count; i++)
                        {
                            // do something so optimization doesn't ignore whole loop. 
                            if (i == over1) throw new Exception();
                            if (i == over2) throw new Exception();
                        }
                        return sw.Elapsed.TotalMilliseconds;
                    }
                    else
                    {
                        var sw = Stopwatch.StartNew();
                        for (int i = 0; i < count; i++)
                        {
                            // do same things, just second in the finally, make sure finally is 
                            // actually doing something and not optimized out
                            try
                            {
                                if (i == over1) throw new Exception();
                            } finally
                            {
                                if (i == over2) throw new Exception();
                            }
                        }
                        return sw.Elapsed.TotalMilliseconds;
                    }
                }
    

    结果:33,33,32,35,32 63,64,69,66,66 (毫秒,请确保已启用代码优化)

    1000万 循环。

    每试一次/最后一次,我们说的是0.033/10000000=

        5
  •  6
  •   Bengie    15 年前

    安德鲁·巴伯说的话。除非抛出异常,否则实际的TRY/CATCH语句不会增加/忽略不计的开销。最后没有什么特别的。您的代码总是在try+catch语句中的代码完成后跳到finally

        6
  •  6
  •   Donal Fellows    15 年前

    finally 就像 else 如果条件不满足。它实际上是汇编程序(IL)中的一个跳转。

    推荐文章