Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why don't throw() and noexcept have any overhead?

throw() was added in C++03 as an exception specifier, however it was deprecated in C++11 for the noexcept specifier.

After profiling some code to find the speed using throw(), noexcept and just plain functions I found that all of them had roughly the same time for the function call.

Results:

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

Here is the code I used to profile:

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;
}

The functions are defined as:

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();
}

I was surprised by these results because I was under the impression that throw() and noexcept added some overhead to the function.

Why doesn't throw() and noexcept add any overhead to the function call as compared to the regular function call?

like image 891
phantom Avatar asked Apr 29 '15 23:04

phantom


1 Answers

gcc and clang have very similar code generation as they are largely ABI compatible with one another. I only have clang to answer your question with, but my answer should apply to your gcc compiler fairly closely.

One can disassemble throw_func, noexcept_func and std_func using the -S command line flag. Upon doing so, you will note that the three functions all generate remarkably similar assembly.

Differences include:

  1. The mangled name of the functions will differ: __Z10throw_funcv, __Z13noexcept_funcv, and __Z8std_funcv.

  2. The "normal path" assembly code will be identical for all three functions except perhaps for the names of labels.

  3. throw_func and noexcept_func will generate "exception tables" after the code. These tables instruct the low-level C++ runtime library how to unwind the stack. This includes instructions on what destructors must be run, what catch blocks to try, and in the case of these two functions, what to do if an exception tries to propagate out. throw_func will contain a call to ___cxa_call_unexpected and noexcept_func will contain a call to ___clang_call_terminate (for gcc that will be named something else).

  4. std_func will not contain calls to unexpected nor terminate. Nor will it have an "exception table". There are no destructors to run, no try/catch clauses to land in, and there is no need to call unexpected or terminate.

In summary, the only difference in these three functions is in the "exceptional path". And the "exceptional path" adds code size, but is never executed by your main. In real-world code, just the added code size can impact run-time performance. However for code that is executed often, and small enough to fit in cache (such as this test), the code size hit won't cause any run-time performance hit.

For completeness, here is the noexcept_func assembly. Everything below LBB0_1: is to handle the exceptional path. LBB0_1: thru Ltmp1: throws the exception. Ltmp2: is a "landing pad" that the runtime will branch to if an exception tries to unwind through here. And GCC_except_table0: is the exception table itself.

    .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
like image 54
Howard Hinnant Avatar answered Sep 28 '22 19:09

Howard Hinnant