C++ Core Guidelines
isocpp.github.io
isocpp.github.io
Took this code [2] and tested with O2 optimizations with gcc 11.x and clang 13.x versions:
$ ./exceptions-test-gcc
3906 return code errors in 0.000562 seconds
3906 exception errors in 0.005740 seconds
Exceptions are 10.2x slower than return codes
$ ./exceptions-test-clang
3906 return code errors in 0.000142 seconds
3906 exception errors in 0.004568 seconds
Exceptions are 32.2x slower than return codes
[1] https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines...VC++ is much faster, as it pings back into Win32 exceptions, for example.
The exception handling you're referring to about pinging back to Win32 I presume is structured exception handling, which is not the default exception handling mechanism used by MSVC, is not recommended to be used, and is not at all performance sensitive.
This advice comes from Microsoft themselves:
https://learn.microsoft.com/en-us/cpp/cpp/exception-handling...
The C++ exception handling mechanism that is used by default does not "ping back" into Win32.
However the point still stands, that isn't something compiler vendors see commercial value so to speak, when they are so constrained to keep up with ISO revisions, and a big slice of the customer base keeps disabling them.
If your function throws an exception 9% of the time, then it outperforms error codes. That's because you always pay for the cost of an error code, regardless of success, but you only pay for the cost of an exception on failure.
I suspect most functions don't throw an exception 9% of the time that they are called.
For clang, yes the situation is worse, indicating that if your function throws an exception ~3% of the time it's better to use error codes, but once again I don't think most functions throw an exception 3% of the time.
If you need to optimize for latency, or real time performance, then exceptions are not at all suitable and I think most performance sensitive developers who have those requirements already know that.
EDIT: updated the ratio to reflect the GP comment instead of the SO post.
Coming from another language, this sounded wild to me. There is a perfectly fine API in the standard library that we choose not to use but instead reinvent the wheel because of an "implementation detail" of a compiler, something a beginner or even an intermediate level C++ developer is likely unaware of.
When I started coding, my first computer was a Timex 2068, containing a Z80 running at 3.5 MHz, with 64 KB, 48 KB available for data, loading data by tape.
The first computer where I was programming in C++, was a 386SX runnning at 20 Mhz (with a turbo button for 40 Mhz, yuupi!), 2 MB RAM (only accessible with extenders, otherwise 640 KB), 40 MB hard disk (later converted to about 80 MB thanks to disk compression tools of the day), with DR-DOS 5.
My current phone, holds a Exynos 1280, with 2 cores at 2.4 GHz and 6 cores at 2.0GHz, with 8 GB RAM, and SSD storage.
And my phone is a toy, when compared with what a standard desktop computer, or servers are capable of in 2024.
Additionally all modern hardware architectures are NUMA, multi-core, hybrid with GPUs.
So unless someone can really prove that their gut feeling is going to fail on the expected deployment hardware, really fail flat on the floor, like the execution deadline is 5 ms, and the application can't do less than 5 ms no matter what, they are needlessly cargo culting optimizations.
The discussion about exception performance mainly focuses on the runtime performance implications of the exception process, which should be negligible. However, due to the current semantics of exception handling, the throw/catch process significantly affects control and data flow, leading most compilers to be more pessimistic about optimization.
The debate between error codes and exceptions seems like a false choice to me. Both have their strengths and weaknesses and should be used interchangeably in the same codebase depending on the circumstances.
In my opinion, the main advantage of exceptions is when there is a large distance between the point of error generation and the point in the program where there is the best information to handle the error. In such cases, monadic error handling would require every intermediary computation to handle the error, whereas exception propagation allows unrelated computations to essentially ignore the error state.
I would even suggest that C++ needs something like "Conditions and Restarts" as a more general exception mechanism.
That’s not a realistic benchmark though. I’ve never seen real-world non-benchmark code which pre-initializes exceptions. They’re always created completely from scratch. Beyond it being uncommon, pre-initializing would also unnecessarily waste memory when you don’t throw the exception.
When it comes to no_inline, I think one should go the opposite direction. Because no_inline here just showed that error codes can be made slower when compiler knows less about code, bringing it closer to exceptions. What’s disputed though is not that you can make ordinary code slower, but that compilers will realistically make your exception-throwing code faster. So I’d annotate with always_inline instead.
Yeah, that why i mentioned that benchmarking features is always contentious and it's easy to devolve into semantic argument... but here we go.
I guess it depends on what you mean by realistic and what is the benchmark trying to do. If by realistic, we mean "representative" in a sense that this code captures a good approximation o real work code, then i would argue that the original benchmark is completely wrong.
My version focus on being precise : measuring one thing (exception handling) in isolation as much as possible.
With that perspective :
1 - The cost of error handling also depends on the amount of information being reported. In the orginal benchmark, on one side the author is sending basically one bit of information and the other a full string. Pre-allocating the error was a way to normalize for that.
2 - I looked at the code, and most of performance delta between the two version came from the error code being vectorize, no_inline was a way to normalize for that as well.
https://learn.microsoft.com/en-us/cpp/code-quality/using-the...
Or you can get the DevOps team to install Sonar, PVS, and then it gets "automatically" enforced by not allowing merges with broken guidelines.