Live data from Hacker News

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

16bpp.net

71–80 of 166 posts

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

#71
post #33

Earlier quoted context omitted.

> 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 exam…

I consider the current tradeoff to be a feature. It permits implementations to take advantage of target-specific affordances (your thread case is an example) as well as taking different implementation strategies (e.g. the small string optimization is different in libc++ and libstdc++). Also you may use another, independent standard library because you prefer its implementation decisions. Meanwhile they remain compati…

> as well as taking different implementation strategies (e.g. the small string optimization is different in libc++ and libstdc++

As a user this is really not a good thing when the stdlib is tied to the platform. In the end the only sane thing to do if you want any reproducibility across operating systems is to exclusively use libc++ everywhere.

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

#72
post #37
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.

It's actually really important that uniform_int_distribution is implementation defined. The 'right' way to do it on one architecture is probably not the right way to do it on a different architecture. For instance, Apple's new CPUs has very fast division. A convenient and useful tool to implement uniform_int_distribution relies on using modulo. So the implementation that runs on Apple's new CPUs ought to use the modu…

> Overzealously dictating an implementation is why C++ ended up with a relatively bad hash table and very bad regex in the standard.

What parts of the standard dictate a particular regex implementation? IIRC the performance issues are usually blamed on ABI compatibility constraints rather than the standard making a fast(er) implementation impossible.

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

#73
post #41

There's a lot of mysticism and superstition surrounding C++ exceptions. It's instructive to sit down with godbolt and examine specific scenarios in which noexcept (or exceptions generally) can affect performance. Read the machine code. Understand why the compiler does what it does. Don't want to invest at that level? You probably want to use a higher level language.

You won't get a sense of how bad exceptions can be by using Godbolt. A lot of the magic of exceptions is handled behind the scenes by the compiler and/or the Itanium ABI. For example one disasterous consequence of using exceptions in GCC is that there is a global application wide lock used to manage stack unwinding. This means that only one single thread can unwind a stack at a time and the lock is held from the star…

> For example one disasterous consequence of using exceptions in GCC is that there is a global application wide lock used to manage stack unwinding.

This might have been (partially?) fixed? GCC Bug 71744 "Concurrently throwing exceptions is not scalable" is marked "RESOLVED FIXED" [0], and commit 6e80a1d164d1 in particular looks interesting:

> eliminate mutex in fast path of __register_frame > > > > This commit eliminates both the mutex and the sorted list from the atomic fast path, and replaces it with a btree that uses optimistic lock coupling during lookup. This allows for fully parallel unwinding and is essential to scale exception handling to large core counts.

I'm not particularly familiar with the unwinding machinery though so I don't know if the issue is fully resolved.

[0]: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71744

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

#74
post #33

Earlier quoted context omitted.

I consider the current tradeoff to be a feature. It permits implementations to take advantage of target-specific affordances (your thread case is an example) as well as taking different implementation strategies (e.g. the small string optimization is different in libc++ and libstdc++). Also you may use another, independent standard library because you prefer its implementation decisions. Meanwhile they remain compati…

> as well as taking different implementation strategies (e.g. the small string optimization is different in libc++ and libstdc++ As a user this is really not a good thing when the stdlib is tied to the platform. In the end the only sane thing to do if you want any reproducibility across operating systems is to exclusively use libc++ everywhere.

>In the end the only sane thing to do if you want any reproducibility across operating systems is to exclusively use ...

no std library code in portable code. FTFY.

Ofc there are no absolutes. In gamedev essential type info bits and intrinsics are either allowed back or wrapped. Algorithm library is another bit allowed for the most part ( no unstable sort and such ).

I know your approach of 'one ring-libc++ to rule them all' is much more popular in community but gamedevs needed cross platform code for long time. It always had been working well regardless of the opinions.

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

#75

Earlier quoted context omitted.

Added to my list why I compile with -fno-exceptions

-fno-exceptions only prevents you from calling throw. If you don't want overhead likely you want -fno-asynchronous-unwind-tables + that clang flag that specifies that extern "C" functions don't throw

Looking how the docs for https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html#ind...

How does that affect code generation? It reads as only effecting debug information and binary size to my untrained eyes.

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

#76

Earlier quoted context omitted.

Added to my list why I compile with -fno-exceptions

-fno-exceptions only prevents you from calling throw. If you don't want overhead likely you want -fno-asynchronous-unwind-tables + that clang flag that specifies that extern "C" functions don't throw

I'm pretty sure I could see a roughly 10% binary size decrease in my C++ projcts just by setting -fno-exceptions, and that was for C++ code that didn't use exceptions in the first place, so there must be more to it then just forbidding throw. Last time I tinkered with this stuff was around 2017 though.

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

#77
post #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...

For many key types and access patterns, storing the hash is faster anyway. I assume people who care about performance are already not using std::unordered_map though.

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

#78
post #37
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.

It's actually really important that uniform_int_distribution is implementation defined. The 'right' way to do it on one architecture is probably not the right way to do it on a different architecture. For instance, Apple's new CPUs has very fast division. A convenient and useful tool to implement uniform_int_distribution relies on using modulo. So the implementation that runs on Apple's new CPUs ought to use the modu…

But reproducibility is as important as performance for the vast majority of use cases, if these implementation-defined bits start to affect the observable outcomes. (That's why we define the required time complexity for many container-related functions but do not actually specify the exact algorithm; difference in Big-O time complexity is just large enough to be "observed".)

A common solution is to provide two versions of such features, one for the less reproducible but maximally performant version and another for common middle grounds that can be reproduced reasonably efficiently across many common platforms. In fact I believe `std::chrono` was designed in that way to sidestep many uncertainties in platform clock implementations.

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

#79
post #11

Earlier quoted context omitted.

No, it can't do that. My speculation is that likely it is so because in general case this might be a NP hard problem similar to the halting problem https://en.wikipedia.org/wiki/Halting_problem . The best it can do is to say whether a given function is qualified with `noexcept` (see noexcept() operator https://en.cppreference.com/w/cpp/language/noexcept )

Dude I am going to blow your mind

Eeeermm... I haven't heard an explosion, but 4+ hours has passed. Are you alive there, dude?

Jokes aside - I'm always happy to be corrected, so please go on if I made a mistake somewhere.

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

#80
post #20
post #10

That's quite interesting and a huge work has been done here, respect for that. Here's what has jumped out at me: `noexcept` qualifier is not free in some cases, particularly, when a qualified function could actually throw, but is marked `noexcept`. In that case, a compiler still must set something up to fulfil the main `noexcept` promise - call `std::terminate()` if an exception is thrown. That means, that putting `n…

> then unfortunately it's totally invalid since it measures time with the `std::chrono::system_clock` which isn't monotonic. Given how long the code required to run, it's almost certain that the clock has been adjusted several times monotonic clocks are mostly useful for short measurement periods. for long-term timing wall-time clocks (with their adjustments) are more accurate because they will drift less.

Ah, that's a great correction, thank you! Yes, indeed, due to a drift, in order to discern second+ (?) differences on different machines (or same machines, but different OSes?), one definitely needs to use a wall-clock time, otherwise it's comparing apples to oranges. There's a lot of interesting questions related to that, but they out of the scope of the thread. If I'm not mistaken the author has also timed some individual small functions, which, if correct, still poses a problem to me, but for measuring huge long running tasks like a full suite running 10+ hours, they are probably right in choosing wall-clock timer indeed.

However, before researching into results any further (for example, -10% difference for `noexcept` case is extremely interesting to debug up to the root cause), I'd still like to understand how the code was run and measured exactly. I didn't find a plausible looking benchmark runner in their code base.

Post reply on HN