Live data from Hacker News

C++'s `noexcept` can sometimes help or hurt performance

16bpp.net

21–30 of 166 posts

Re: C++'s `noexcept` can sometimes help or hurt performance

#21
I would like to have seen a comparison that actually includes -fno-exceptions, rather than just noexcept. My assumption is that to get a consistent gain from noexcept, you would need every function called to be explicitly noexcept, because a bunch of the cost of exceptions is code size and state required to support unwinding. So if the performance cost exception handling is causing is due to that, then if _anything_ can cause an exception (or I guess more accurately unless every opaque call is explicitly indicated to not cause an exception) then that overhead remains.

That said, I'm still confused by the perf results of the article, especially the perlin noise vs MSVC one. It's sufficiently weird outlier that it makes me wonder if something in the compiler has a noexcept path that adds checks that aren't usually on (i.e imagine the code has a "debug" mode that did bounds checks or something, but the function resolution you hit in the noexcept path always does the bounds check - I'm really not sure exactly how you'd get that to happen, but "non-default path was not benchmarked" is not exactly an uncommon occurrence)

Re: C++'s `noexcept` can sometimes help or hurt performance

#22
post #7
post #4

Shouldn't the compiler deduce noexcept for you?

No, noexcept confusingly doesn't mean "does not throw exceptions" in that sense. There is no constraint that says you can only call noexcept code from noexcept code - quite the opposite. Noexcept puts NO constraints on the code. All noexcept does is catch any exception and immediately std::terminate. Confusingly this means that noexcept should really be called deathexcept, since any exception thrown within kills the…

> All noexcept does is catch any exception and immediately std::terminate.

I don't think this is a decent interpretation of what no except does. It misses the whole point of this feature, and confuses a failsafe with the point of using it.

The whole point of noexcept is to tell the compiler that the function does not throw exceptions. This allows the compiler to apply optimizations, such as not needing to track down the necessary info to unwind the call stack when an exception is thrown. Some containers are also designed to only invoke move constructors if they are noexcept and otherwise will copy values around.

As the compiler omits the info required to recover from exceptions, if one is indeed thrown and bubbles up to the noexcept function then it's not possible to do the necessary janitorial work. Therefore, std::terminate is called instead.

Re: C++'s `noexcept` can sometimes help or hurt performance

#23

Earlier quoted context omitted.

> There are countless scenarios where you want deterministic random numbers (for testing if nothing else), so std's distributions are unusable. Fortunately you can just plug in Boost's implementation. I don't understand what's your complain. If you're already plugging in alternative implementations,what stops you from actually stubbing these random number generators with any realization at all?

It's a compromised and goofy implementation with lots of warts. What's the point it in having a /standard/ library then?

> It's a compromised and goofy implementation with lots of warts.

I don't think this case qualifies as an example. I think the only goofy detail in the story is expecting a random number generator to be non-random and deterministic with the only conceivable usecase being poorly designed and implemented test fxtures.

> What's the point it in having a /standard/ library then?

The point of standardized components is to provide reusable elements that can be used across all platforms and implementations, thus saving on the development effort of upgrading and porting the code across implementations and even platforms. If you cannot design working software, that's not a problem you can pin on the tools you don't know how to use.

Re: C++'s `noexcept` can sometimes help or hurt performance

#24

Earlier quoted context omitted.

It's a compromised and goofy implementation with lots of warts. What's the point it in having a /standard/ library then?

> It's a compromised and goofy implementation with lots of warts. I don't think this case qualifies as an example. I think the only goofy detail in the story is expecting a random number generator to be non-random and deterministic with the only conceivable usecase being poorly designed and implemented test fxtures. > What's the point it in having a /standard/ library then? The point of standardized components is to…

> with the only conceivable usecase being poorly designed and implemented test fxtures.

Reproducible pseudo-randomness is a necessity with fuzz testing. It is not a poor design approach when it is actually useful.

Re: C++'s `noexcept` can sometimes help or hurt performance

#25
post #13

Earlier quoted context omitted.

Panic=abort is more like -fno-exceptions since it applies to all the code being compiled and not just function. Codegen can also take advantage of the fact that it won't have to unwind. I don't think there is a rust equivalent of noexcept.

Does C++ std::terminate unwind the stack and call destructors? Then noexcept would be pretty close to regular Rust panics, wouldn't it? Basically an unrecoverable exception?

The behavior of `-fno-exceptions` isn't standardized, because it's a compiler feature, not a part of the C++ standard. The standard says:

> In the situation where no matching [exception] handler is found, it is implementation-defined whether or not the stack is unwound before std::terminate is invoked. In the situation where the search for a handler encounters the outermost block of a function with a non-throwing exception specification, it is implementation-defined whether the stack is unwound, unwound partially, or not unwound at all before the function std::terminate is invoked.

So, the whole thing is basically implementation-defined (including `-fno-exceptions`, since that is something that implementing compilers provide).

Re: C++'s `noexcept` can sometimes help or hurt performance

#26

The most common place where noexcept improves performance is on move constructors and move assignments when moving is cheaper than copying. If your type is not nothrow moveable std::vector will copy it instead of moving when resizing, as the move constructor throwing would leave the vector in an invalid state (while the copy constructor throwing leaves the vector unchanged). Platforms with setjmp-longjmp based except…

There is another standard library related scenario: hash tables. The std unordered containers will store the hash of each key unless your hash function is noexcept. Analogous to how vector needs noexcept move for fast reserve and resize, unordered containers need noexcept hash to avoid extra memory usage. See https://gcc.gnu.org/onlinedocs/libstdc++/manual/unordered_as...

Re: C++'s `noexcept` can sometimes help or hurt performance

#27

Earlier quoted context omitted.

It's a compromised and goofy implementation with lots of warts. What's the point it in having a /standard/ library then?

> It's a compromised and goofy implementation with lots of warts. I don't think this case qualifies as an example. I think the only goofy detail in the story is expecting a random number generator to be non-random and deterministic with the only conceivable usecase being poorly designed and implemented test fxtures. > What's the point it in having a /standard/ library then? The point of standardized components is to…

> The point of standardized components is to provide reusable elements that can be used across all platforms and implementations, thus saving on the development effort of upgrading and porting the code across implementations and even platforms.

It's a shame that C++'s "standardized" components ARE COMPLETELY DIFFERENT on different platforms.

Some of the C++ standard requires per-platform implementation work. For example std::thread on Linux and Windows obviously must have a different implementation. However a super majority of the standard API is just vanilla C++ code. For example std::vector or std::unordered_map. The fact that the standard defines a spec which is then implemented numerous times is absurd, stupid, and bad. The specs are simultaneously over-constrained and under-constrained. It's a disaster.

Re: C++'s `noexcept` can sometimes help or hurt performance

#28
post #2

> I didn't know std::uniform_int_distribution doesn't actually produce the same results on different compilers I think this is genuinely my biggest complaint about the C++ standard library. There are countless scenarios where you want deterministic random numbers (for testing if nothing else), so std's distributions are unusable. Fortunately you can just plug in Boost's implementation.

> I think this is genuinely my biggest complaint about the C++ standard library

What do you think of Abseil hash tables randomizing themselves (piggybacking on ASLR) on each start of your program?

Re: C++'s `noexcept` can sometimes help or hurt performance

#29
post #7

Earlier quoted context omitted.

No, noexcept confusingly doesn't mean "does not throw exceptions" in that sense. There is no constraint that says you can only call noexcept code from noexcept code - quite the opposite. Noexcept puts NO constraints on the code. All noexcept does is catch any exception and immediately std::terminate. Confusingly this means that noexcept should really be called deathexcept, since any exception thrown within kills the…

> All noexcept does is catch any exception and immediately std::terminate. While that's a possible implementation; the standard is a bit more relaxed: `noexcept` may also call `std::terminate` immediately when an exception is thrown, without calling destructors in the usual way a catch block would do. https://godbolt.org/z/YTe84M5vq test1 has a ~S() destructor call if maybe_throw() throws; test2 never calls ~S(). MSV…

More than an optimization is a different exception handling philosophy.

AFAIK itanium ABI exception handling requires two phase unwinding: first the stack is traversed looking for a valid landing pad: if it succeeds then the stack is traversed again calling all destructors. If it fails it calls std terminate. This is actually slower as it need to traverse twice, but the big advantage is that if the program would abort, the state of the program is preserved in the core file. This is easily generalized with noexcept functions: no unwind info is generated for those, so unwind always fail.

MSVC used to do one pass unwind, but I thought they changed it when they implemented table based unwind for x64.

Re: C++'s `noexcept` can sometimes help or hurt performance

#30
post #8

I don't feel like this article illuminates anything about how noexcept works. The asm diff at the end suggests _there is no difference_ in the emitted code. I plugged it into godbolt myself and see absolutely no difference. https://godbolt.org/z/jdro5jdnG It seems the selected example function may not be exercising noexcept. I suppose the assumption is that operator[] is something that can throw, but ... perhaps the…

> I don't feel like this article illuminates anything about how noexcept works. The asm diff at the end suggests _there is no difference_ in the emitted code.

You are absolutely correct. The OP is basically testing the hypothesis "Wrapping a function in `noexcept` will magically make it faster," which is (1) nonsense to anyone who knows how C++ works, and also (2) trivially easy to falsify, because all you have to do is look at the compiled code. Same codegen? Then it's not going to be faster (or slower). You needn't spend all those CPU cycles to find out what you already know by looking.

There has been a fair bit of literature written on the performance of exceptions and noexcept, but OP isn't contributing anything with this particular post.

Here are two of my own blog posts on the subject. The first one is just an explanation of the "vector pessimization" which was also mentioned (obliquely) in OP's post — but with an actual benchmark where you can see why it matters. https://quuxplusone.github.io/blog/2022/08/26/vector-pessimi... https://godbolt.org/z/e4jEcdfT9

The second one is much more interesting, because it shows where `noexcept` can actually have an effect on codegen in the core language. TLDR, it can matter on functions that the compiler can't inline, such as when crossing ABI boundaries or when (as in this case) it's an indirect call through a function pointer. https://quuxplusone.github.io/blog/2022/07/30/type-erased-in...

Post reply on HN