6 ms·
(quoting desc) sigh (end quote) Exceptions make your code faster in the non-exceptional case. See all those if-branches for error checks that don't exist in y
by lallysingh 7y ago
(quoting desc)
sigh
(end quote)
Exceptions make your code faster in the non-exceptional case.
See all those if-branches for error checks that don't exist in your code? That branch-free code is going to run faster than your branchy code. And the code's shorter, because it doesn't have the checks and handling intermixed, which makes it fit better into cache.
This isn't rocket science, I'm amazed that this isn't obvious.
- abainbridge 7y agoCheck out the godbolt link I posted under desc's thread. The answer isn't that obvious. Both approaches have a conditional. The compiler/linker marks the exception handling code as cold. In theory this could go in a section of the exe that the loader never pages into RAM. (I'm getting out of my depth here).
- thedance 7y agoYeah because that's going to go well: taking a major page fault whenever you throw. Let me know how that works at scale.
- cwzwarich 7y agoYou already take a page fault with DWARF exceptions, since the unwind / LSDA info is probably on a different page from any of your code, as well as the personality routine itself.
- lallysingh 7y agoIf you're throwing, performance isn't the concern. You just want to clean up, return your 500 or what not, and get back to a sane state.
- gpderetta 7y agothat potentially happen even with normal if based error handing. If the compiler thinks the error handling path is unlikely to be run, it can move it to a cold page.
- getpost 7y agoYes! Exceptions make your code faster and smaller. And by isolating exception handling to many fewer cases, it's more likely your exception handling will be correct.
- zozbot234 7y agoBranchy code where the branches are statically known to be very unlikely ought to be practically as fast as the branch-free equivalent. You're probably forgoing a few optimization opportunities since you do have to build a proper exception record (and perhaps a stack trace) if the code errors out but other than that, it just doesn't matter that much.
- mhh__ 7y agoStatic branch prediction isn't found on any major architectures, right? You can optimize the code generation around that knowledge but not passing it directly to the CPU
- chrisseaton 7y ago> Static branch prediction isn't found on any major architectures, right? All major AMD and Intel processors do static branch prediction.
- hrydgard 7y agoIt is absolutely done, it's just implicit rather than explicit. The simplest heuristic is to predict backwards branches as taken (loops) and forward branches as not taken, and it works quite well (though I think it might be more sophisticated than that).
- ncmncm 7y agoOf course the error-code checking branches will most usually be forward branches, thus not predicted correctly the first time through, or after spilling.
- ncmncm 7y agoBranchy checks for error codes -- the alternative to using exceptions -- are not statically predictable. Each such branch is dynamically predictable, but burns one of a strictly limited number of available branch prediction slots. So, while the successfully predicted branches are pretty cheap, many fewer can be predicted. Synthetic benchmarks generally have many fewer branches to predict than live code has.
- kova12 7y agoI too am amazed. He wrote a code which will throw in 50% cases or already very fast code `(random() % 2 == 0)`. Hell yes, a huge performance penalty is expected. But what about `nullptr == fopen(..)`? I bet the performance penalty would be negligible here.
- cletus 7y agoThis is a good example of something that sounds reasonable, even intuitive, but in practice just isn't necessarily true. Ever since the 486 we've had zero-cost instructions, for example. More pertinent to this, modern CPUs have multi-stage pipelines and branch prediction. Third, just the possibility of throwing an exception has a cost as the program has to have the code to unwind the stack. Last, humans have become increasingly bad at second-guessing compiler optimizations. > This isn't rocket science, I'm amazed that this isn't obvious. Thing is, it kind of is rocket science. And it's not obvious. That's why you have to test. Now you could take the position that exception lead to better designed code and might have other such benefits. Personally, I've become more convinced over the years that exceptions are a false economy.
- kllrnohj 7y ago> More pertinent to this, modern CPUs have multi-stage pipelines and branch prediction. A correctly predicted branch is still slower than no branch. An incorrectly predicted branch is one of the slowest things you can have happen on a modern CPU. > Third, just the possibility of throwing an exception has a cost as the program has to have the code to unwind the stack. Incorrect. The exception handling code does not execute in the normal path. -fno-exceptions ~never makes your code faster. It does, however, shrink your binary size. Which is a non-trivial cost, but not the focus of the discussion.
- akira2501 7y ago> An incorrectly predicted branch is one of the slowest things you can have happen on a modern CPU Per Agner Fog, misprediction on Ryzen will cost you 18 cycles. On Haswell family, 15 to 20. Floating point division can cost you more cycles, both in latency and throughput. Even just plain old L3 latency can be worse than misprediction.
- jupp0r 7y agoThat’s oversimplified. If the falsely predicted branch issues a load instruction for example, you might use up valuable memory bandwidth, trash cache lines, etc. There are way more side effects that can make false branch predictions more expensive. It’s also extremely microarchitecture dependent, proprietary and undocumented what exactly happens, which is why we need to benchmark the actual code under consideration.
- jandrewrogers 7y agoThis is an over-simplification. The "fitting in cache" is quite complicated, and as a good rule of thumb exceptions make cache locality less optimal even if you rarely call them. There is little that is "branch-free" about exception code, it literally implies branching. For performance-sensitive code, I've yet to find a real-world case where exceptions out-perform exception-free code. Nor have I seen much in the way of contrary evidence among people that care about performance.
- lallysingh 7y agoWhat's the complication on the fitting in cache? The error handling is taken out of the code that's loaded in icache. When you throw, a runtime library call is made that walks your stack. Until throw, that code has no reason to be in your icache.
- sansnomme 7y agoDoes that mean Go/Rust code will be generally slower than C/C++ on a sufficiently complex program?
- ncmncm 7y agoGo has GC, which has real costs always lied about because they are hard to tie to specific instruction sequences. So, Go will always be slower in certain key applications where latency matters, but in most other cases not so anybody notices. Otherwise, there are too many differences in detail to generalize. Rust can take advantage of reduced aliasing that C++ can't, but the compiler doesn't because that code is still too buggy to turn on. Last time I compared carefully, Rust and C++ built with Clang had identical performance, to well under 1%: much smaller than, e.g., the difference between Clang and Gcc.