c – 为什么不throw()和noexcept有什么开销?

在C 03中将throw()作为异常说明符添加,但在C11中对于noexcept说明符已被弃用.

在分析一些代码后,使用throw(),noexcept和简单的函数找到速度,我发现所有这些函数函数调用大致相同.

结果:

throw()       noexcept      plain old function
11233 ms      11105 ms      11216 ms
11195 ms      11122 ms      11150 ms
11192 ms      11151 ms      11231 ms
11214 ms      11218 ms      11228 ms

compiled with MinGW using g++ -o test.exe inc.cpp no.cpp -std=c++11 -O3

以下是我用于配置文件代码

int main() {
    unsigned long long iter = (unsigned long long)1 << (unsigned long long)40;
    auto t1 = std::chrono::high_resolution_clock::Now();

    for(unsigned long long i = 0; i < iter; i++)
    {
#ifdef THROW
        throw_func();
#elif defined NOEXCEPT
        noexcept_func();
#else
        std_func();
#endif
     }

     auto t2 = std::chrono::high_resolution_clock::Now();

     std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count() << " ms" << std::endl;
}

功能定义如下:

unsigned long long val = 1;

struct exception {  };

void throw_func(void) throw()
{
    if (!vaL++)
       throw exception();
}
void noexcept_func(void) noexcept
{
    if (!vaL++)
        throw exception();
}
void std_func(void)
{
    if (!vaL++)
        throw exception();
}

我对这些结果感到惊讶,因为我的印象是throw()和noexcept为函数添加了一些开销.

与常规函数调用相比,为什么throw()和noexcept会增加函数调用的任何开销?

解决方法

gcc和clang具有非常相似的代码生成,因为它们在很大程度上与ABI兼容.我只是cl to回答你的问题,但我的答案应该适用于你的gcc编译器.

可以使用-S命令行标志来反汇编throw_func,noexcept_func和std_func.在这样做时,您将注意到,这三个功能都会产生非常相似的装配.

差异包括

>这个函数名称不一样:__Z10throw_funcv,__Z13noexcept_funcv和__Z8std_funcv.
>“正常路径”汇编代码对于所有三个函数都是相同的,除了可能是标签名称.
> throw_func和noexcept_func将在代码之后生成“异常表”.这些表指示低级C运行时库如何解开堆栈.这包括关于什么析构函数必须运行的指令,要尝试的catch块,在这两个函数的情况下,如果异常尝试传播出来,该怎么做. throw_func将包含对___cxa_call_unexpected的调用,并且noexcept_func将包含对___clang_call_terminate的调用(对于将被命名为其他东西的gcc).
> std_func不会包含意外或终止的调用.它也不会有“例外表”.没有运行的析构函数,没有try / catch子句进入,没有必要调用意外或终止.

总之,这三个功能的唯一区别是在“异常路径”中.而“特殊的路径”增加代码大小,但是永远不会被你的主机执行.在现实世界的代码中,增加代码大小可能会影响运行时性能.然而,对于经常执行并且足够小以适应高速缓存(例如此测试)的代码,代码大小命中不会导致任何运行时性能命中.

为了完整,这里是noexcept_func程序集. LBB0_1以下的一切都是处理异常路径的. LBB0_1:通过Ltmp1:抛出异常. Ltmp2:是一个“着陆垫”,运行时将分支到一个异常尝试通过这里解开.而GCC_except_table0是异常表本身.

.globl  __Z13noexcept_funcv
    .align  4,0x90
__Z13noexcept_funcv:                    ## @_Z13noexcept_funcv
    .cfi_startproc
    .cfi_personality 155,___gxx_personality_v0
Leh_func_begin0:
    .cfi_lsda 16,Lexception0
## BB#0:
    pushq   %rbp
Ltmp3:
    .cfi_def_cfa_offset 16
Ltmp4:
    .cfi_offset %rbp,-16
    movq    %rsp,%rbp
Ltmp5:
    .cfi_def_cfa_register %rbp
    movq    _val(%rip),%rax
    leaq    1(%rax),%rcx
    movq    %rcx,_val(%rip)
    testq   %rax,%rax
    je  LBB0_1
LBB0_2:
    popq    %rbp
    retq
LBB0_1:
    movl    $1,%edi
    callq   ___cxa_allocate_exception
Ltmp0:
    movq    __ZTI9exception@GOTPCREL(%rip),%rsi
    xorl    %edx,%edx
    movq    %rax,%rdi
    callq   ___cxa_throw
Ltmp1:
    jmp LBB0_2
LBB0_3:
Ltmp2:
    movq    %rax,%rdi
    callq   ___clang_call_terminate
    .cfi_endproc
Leh_func_end0:
    .section    __TEXT,__gcc_except_tab
    .align  2
GCC_except_table0:
Lexception0:
    .byte   255                     ## @LPStart Encoding = omit
    .byte   155                     ## @TType Encoding = indirect pcrel sdata4
    .asciz  "\242\200\200"          ## @TType base offset
    .byte   3                       ## Call site Encoding = udata4
    .byte   26                      ## Call site table length
Lset0 = Leh_func_begin0-Leh_func_begin0 ## >> Call Site 1 <<
    .long   Lset0
Lset1 = Ltmp0-Leh_func_begin0           ##   Call between Leh_func_begin0 and Ltmp0
    .long   Lset1
    .long   0                       ##     has no landing pad
    .byte   0                       ##   On action: cleanup
Lset2 = Ltmp0-Leh_func_begin0           ## >> Call Site 2 <<
    .long   Lset2
Lset3 = Ltmp1-Ltmp0                     ##   Call between Ltmp0 and Ltmp1
    .long   Lset3
Lset4 = Ltmp2-Leh_func_begin0           ##     jumps to Ltmp2
    .long   Lset4
    .byte   1                       ##   On action: 1
    .byte   1                       ## >> Action Record 1 <<
                                        ##   Catch TypeInfo 1
    .byte   0                       ##   No further actions
                                        ## >> Catch TypeInfos <<
    .long   0                       ## TypeInfo 1
    .align  2

相关文章

本程序的编译和运行环境如下(如果有运行方面的问题欢迎在评...
水了一学期的院选修,万万没想到期末考试还有比较硬核的编程...
补充一下,先前文章末尾给出的下载链接的完整代码含有部分C&...
思路如标题所说采用模N取余法,难点是这个除法过程如何实现。...
本篇博客有更新!!!更新后效果图如下: 文章末尾的完整代码...
刚开始学习模块化程序设计时,估计大家都被形参和实参搞迷糊...