The surprising codegen impact of noexcept
Would spamming the noexcept
keyword make your code faster? Sometimes. But not always. Consider the following snippet of code:
int g();
int f() {
return g();
}
I intentionally do not define g
in this translation unit since otherwise, the compiler will be too smart and inline everything.
Nevertheless, all the major C++ compilers can figure out that f
only contains a tail-call to g
and generate codes like this:
f():
jmp g()
Now lets consider the following code:
int g();
int f() noexcept {
return g();
}
Since the compilers have no idea if g
would throw or not, they are forced to generate codes that invoke std::terminate
in case bad things happened.
Here is the result codegen from various compilers:
msvc
$ip2state$int f(void) DB 02H
DB 08H
DB 00H
$cppxdata$int f(void) DB 060H
DD imagerel $ip2state$int f(void)
int f(void) PROC ; f, COMDAT
$LN5:
sub rsp, 40 ; 00000028H
call int g(void) ; g
npad 1
add rsp, 40 ; 00000028H
ret 0
int f(void) ENDP ; f
gcc
f():
sub rsp, 8
call g()
add rsp, 8
ret
clang
f():
push rax
call g()
pop rcx
ret
mov rdi, rax
call __clang_call_terminate
__clang_call_terminate:
push rax
call __cxa_begin_catch
call std::terminate()
How to deal with C functions?
Now we know that noexcept
can cause overhead if we call a non-noexcept function inside, how do we deal with functions that will not throw but are not mark as noexcept
? Fortunately, the venerable Hana Dusíková came up with a clever solution:
Did you ever get an suboptimal code, because you were calling external C function in your noexcept code?
— Hana Dusíková 🍊 (@hankadusikova) June 27, 2020
Suffer no more:https://t.co/LA7C76a063
You can mark the noexcept_cast
function force inline by compiler-specific extensions so it will not decrease performance in debug mode.
Conclusion
Don't spam noexcept
if you don't have a project-wise "no exception" policy. And be especially careful with higher-order functions that may invoke user-defined functions. All in all, noexcept
is a part of the type system and the contract of your API. Only add noexcept
to function that you want to guarantee not to throw.