5 ms·
> Point performance is the worst way to decide about error handling. My point is exactly that. It's more important to have a single common way of doing error h
by randyrand 5y ago
> Point performance is the worst way to decide about error handling.
My point is exactly that. It's more important to have a single common way of doing error handling than it is to have a small perf improvement. That's why I recommend return values. You'll never hit a perf problem with them.
Exceptions cannot be used in code hot paths (~thousands of iterations) when it's not a small perf problem anymore. Therefore, they cannot be your only method for signaling failure. And therefore, assuming we care about consistency and composability of code, it's far better to use return value based error handling unless you have good reason (almost never). Ideally with syntatic sugar from the compiler (see Rust, Zig, Swift, etc).
> when you need code to clean up, the destructors are right there.
Exceptions are not needed to achieve this. C++ calls the same 'well tested' destructors when you return from a function.
The one point in your favor, that I acknowledged above, is that C++ requires exceptions to report failure from Constructors and Destructors, for OOP, std::vector initialization, for much of the standard library etc. Thats a travesty.
- kllrnohj 5y ago> That's why I recommend return values. You'll never hit a perf problem with them. > Exceptions cannot be used in code hot paths (~thousands of iterations) when it's not a small perf problem anymore. You should read the paper in the link. Both of your claims here are exactly backward. The problem is "just" that exceptions don't scale across multiple threads due to internally using a global lock. They are otherwise much faster than return values (4x faster in the trivial example)
- ncmncm 5y agoYes. The article author's mistake was actually throwing those exceptions too often. For arithmetic errors, NaN is usually the better choice.
- kllrnohj 5y agoIt's an exploration in scaling, how could that possibly have been a "mistake"? The paper isn't advocating for throwing an exception instead of returning NaN in the specific case of calculating a square root. It's instead only exploring the overhead & scaling of various error handling types.
- randyrand 5y agoAre you sure? Single threaded 18-16ms (boost::LEAF) vs 19-23ms (exceptions) A better comparison is found here: https://lordsoftech.com/programming/error-codes-are-far-slower-than-exceptions/ https://lordsoftech.com/programming/error-codes-are-far-slow... > TL;DR On modern 64-bit PC architectures, C++ exceptions only add unreachable code with destructor calls into functions and their effect on performance is below 1%, but such low values are difficult to measure. Handling rare errors with return values requires additional branching that slows down the program in realistic scenarios by about 5% and are also less convenient. If an exception is actually thrown, stack unwinding costs about 2 µs per stack frame. 5% cost to using error codes. In exchange for that 5% you don't fall off a performance cliff if you hit a bunch of errors unexpectedly, and can confidently use them in hot paths and on CPUs with multiple cores. I'll take the lower perf complexity and greater perf robustness of error codes any day. Reliability and predictability is more important to me than a 5% perf cost.
- kllrnohj 5y agoYour "better comparison" finds that error codes are slower than exceptions? It's right there at the top in bold even if you don't want to read the whole thing: > TL;DR On modern 64-bit PC architectures, C++ exceptions only add unreachable code with destructor calls into functions and their effect on performance is below 1%, but such low values are difficult to measure. Handling rare errors with return values requires additional branching that slows down the program in realistic scenarios by about 5% and are also less convenient. Also I see you "cherry picked" your counter example using the sqrt test, which is less influenced by per-call overhead. Using instead the fib test, which is basically just a test of call overhead, we get instead: 12ms-14ms (exceptions) vs. 22-23ms (boost::LEAF) Both the paper & your link are in agreement - exceptions are faster than error return values. > 5% cost to using error codes. In exchange for that 5% you don't fall off a performance cliff if you hit a bunch of errors unexpectedly, and can confidently use them in hot paths and on CPUs with multiple cores. You also get more fragility since error handling is now manual. I'd rather have performance and reliability, which only exceptions can provide. They don't provide that today thanks to the global mutex, but the point of the paper & related explorations is that that both can be fixed, and that fixing it provides a superior performing solution. And a more reliable one. Unless you're arguing that it's better to just always have a 5% performance tax & manual, error-prone error handling? We shouldn't attempt to fix the toolchain at all? The paper isn't arguing that people should use exceptions today. The paper is arguing that exceptions should be fixed so that they can be used in the future.
- ncmncm 5y agoWithout exceptions, you will hit a performance problem on every single call, successful or not. That is what happens at Google, and in Rust: almost every last call site in the whole system has a "if" check. I.e., it is both badly obscured, and also slow. You are arguing that programmers should not be obliged to make judicious choices. But that is exactly the job of every programmer. Dodging consequential choices practically defines a bad programmer. Your assertion that destructors handle errors reported by return value is disingenuous, if not actively dishonest. The code checking for and acting on a failure is not in a destructor, and is never exercised except when you succeed in provoking exactly that failure. You are writing bad code, and should be ashamed.
- jstimpfle 5y agoI can't imagine error-checking if's being a source of slowness. How are exceptions generated in the first place? Using if's. And anyway, even well structured code is full of if's (e.g. array iteration), and it's not a problem. Especially for error-checking, when the error case is rare, the CPU will correctly predict the branch to take most of the time, so the cost of the if is negligible. The only call where if's should probably be minimized is on hot paths, where there shouldn't be any I/O or other exception code anyway. I would agree that manual stack unwinding (many stack frames deep) by propating error values up the call chain is bad. The problem here is not the if's, but that the program structure is unnecessarily convoluted. This can happen when specific code is calling generic code a lot (which can be avoided by having the generic code call the specific code instead). > You are writing bad code, and should be ashamed. Woah, calm your horses!
- ncmncm 5y agoThat "if" that guards the throw is the same "if" as would guard the error return. It is the second "if", in the caller, checking the returned result, that up-to-doubles your branch-prediction cache footprint. On, yes, the hot path. Program structure corruption is another problem. They add. Bad code is a tax on all of us.
- 5y ago