抛出异常的原因是如此之慢

时间:2014-09-17 09:43:34

标签: c++ exception-handling

int foo(int n)
{
    //if (n==0) throw 0;  // throw is so slow
    if (n==0) return 0;   //return is faster
    //foo(n-1); sorry , it should be ..
    return foo(n-1);
}

int main()
{
  for (int i=0;i<50000;++i)
  {
      try{
          foo(80);
      }
      catch(...){

      }
  }
  return 0;
}

我测试了以上程序。当我使用return 0时,for循环执行得非常快,而使用throw 0,非常非常慢。我知道异常句柄效率不高,但我觉得它太慢了。

2 个答案:

答案 0 :(得分:2)

不要使用throw作为回报

您的代码也有缺陷,我假设您想要返回n!=0

的内容

抛出异常是一种复杂的机制,可能是编译器后端最难处理的部分。它涉及倒带堆栈,调用范围内对象的析构函数,可能在大多数平台上处理安全检查并找到可以处理异常的upframe。

在你的情况下,它可能会稍微快一点但不如呼叫返回快。

将gcc的优化版本与return 0进行比较:

foo(int):                                # @foo(int)
    xorl    %eax, %eax
    ret

main:                                   # @main
    xorl    %eax, %eax
    ret

以及异常版本(两种情况下 -O3 优化):

foo(int):                                # @foo(int)
    pushq   %rbp
    movq    %rsp, %rbp
    movl    $4, %edi
    callq   __cxa_allocate_exception
    movl    $0, (%rax)
    movq    %rax, %rdi
    movl    typeinfo for int, %esi
    xorl    %edx, %edx
    callq   __cxa_throw

main:                                   # @main
    pushq   %rbp
    movq    %rsp, %rbp
    pushq   %rbx
    pushq   %rax
    movl    $-1, %ebx
.LBB1_1:                                # =>This Inner Loop Header: Depth=1
    incl    %ebx
    cmpl    $49999, %ebx            # imm = 0xC34F
    jg  .LBB1_4
    movl    $4, %edi
    callq   __cxa_allocate_exception
    movl    $0, (%rax)
    movq    %rax, %rdi
    movl    typeinfo for int, %esi
    xorl    %edx, %edx
    callq   __cxa_throw
    movq    %rax, %rdi
    callq   __cxa_begin_catch
    callq   __cxa_end_catch
    jmp .LBB1_1
.LBB1_4:
    xorl    %eax, %eax
    addq    $8, %rsp
    popq    %rbx
    popq    %rbp
    ret
GCC_except_table1:
    .byte   255                     # @LPStart Encoding = omit
    .byte   3                       # @TType Encoding = udata4
    .byte   175                     # @TType base offset
    .zero   1,128
    .zero   1
    .byte   3                       # Call site Encoding = udata4
    .byte   39                      # Call site table length
    .long   .Lset0
    .long   .Lset1
    .long   0                       #     has no landing pad
    .byte   0                       #   On action: cleanup
    .long   .Lset2
    .long   .Lset3
    .long   .Lset4
    .byte   1                       #   On action: 1
    .long   .Lset5
    .long   .Lset6
    .long   0                       #     has no landing pad
    .byte   0                       #   On action: cleanup
    .byte   1                       # >> Action Record 1 <<
    .byte   0                       #   No further actions
    .long   0                       # TypeInfo 1

正如您所看到的,代码涉及的方式更多:着陆垫已设置和分配,个性例程可以完成其工作等等。请查看here。这个东西也很难optimize您支付使用的费用

答案 1 :(得分:2)

以下是gcc编译代码的内容,return 0;中的foo(有或没有修复,以确保foo始终返回某些内容,尽管它有&#39} ;技术上UB没有修复):

main:
    xorl    %eax, %eax
    ret

编译器可以证明循环不会产生可观察到的副作用,并抛弃整个事物。