9 ms·
Seems to be better exceptions than what is already there. And fully compliant as far as I can tell. Someone just had to go out there and do it. From doing my ow
by fwsgonzo 7y ago
Seems to be better exceptions than what is already there. And fully compliant as far as I can tell. Someone just had to go out there and do it. From doing my own benchmarks on exceptions, I already know you should basically never use them. The bloat and unexplained slowdowns in real programs is just a headache you don't want.
In the end, I still use exceptions, so this gives me hope. I like C++ exceptions as a feature, but I'm not happy about how it's implemented right now.
- arethuza 7y agoOut of interest (its decades since I last used C++) - do C++ exceptions cause an overhead generally or only if they are thrown?
- monocasa 7y agoSJLJ have overhead (but no one really uses that anymore). The vast majority of modern envs (which boil down to DWARF and MSVC) only have overhead when thrown.
- DerDangDerDang 7y agoTheoretically true, but colleagues of mine have done benchmarks showing MSVC has a slight overhead with exceptions enabled, even if none are thrown. Worth benchmarking yourself if you need to be sure.
- monocasa 7y agoIs that recorded somewhere? The benchmarks I've done have been pretty all over the place and seem to be around jiggling the I$ alignment. Sometimes helps, sometimes hurts.
- fwsgonzo 7y agoI have a site for testing these things: https://droplet.fwsnet.net/ https://droplet.fwsnet.net/ I wrote some tests, not sure if they are perfect: #include <stdexcept> volatile int i = 1; __attribute__((noinline)) int test() { return i; } int main() { if (test() == 0) throw std::runtime_error(""); return 0; } ====> Instruction count: 612 Memory usage: 116 KB, top: 116 KB Compile time: 745326 micros Execution time: 149 micros Binary size: 98 KB The above is with exception, below is without: volatile int i = 1; __attribute__((noinline)) int test() { return i; } int main() { if (test() == 0) return -1; return 0; } ====> Instruction count: 251 Memory usage: 16 KB, top: 16 KB Compile time: 441626 micros Execution time: 53 micros Binary size: 3 KB Just remember to pick newlib as the target, as that is the thinnest, and the no-exceptions variant will be only 16kb.
- monocasa 7y agoUsing full process startup/shutdown for a microbenchmark is a bit specious. Also, what are your compile flags? godbolt shows a pretty big difference with -O2.
- fwsgonzo 7y agoTo monocasa, which I can't reply to: I agree completely, it really does run everything from start to finish. Sometimes that's what you want, and sometimes you don't. I could break on main as an option, and then start the benchmarking after main. It would include teardown, unless you call _exit() at the end of main. The compile flags are -O2, no linker GC.
- DerDangDerDang 7y agoI'm afraid not sorry. Though it probably wasn't a great benchmark. They were only investigating whether enabling exceptions has a cost if not thrown on MSVC. So a single example where there was a penalty was enough to cast doubt on exception usage. Enough that nobody felt like sticking their neck out to fight the cause, anyway ;)
- saagarjha 7y agoWell, there’s a slight code size overhead.
- benibela 7y agoFreePascal uses SJLJ, at least on Linux
- bregma 7y agoWith the more common modern compilers (Microsoft, GCC, LLVM, ICC, many others) there is zero runtime overhead to programming with exceptions unless they are thrown. This is possible because the unwind information is stored externally to the function code. There is still an additional overhead in terms of space, since the .eh_frame (or equivalent) sections need to loaded and possibly relocated by the linkloader at program startup, although that's minimal work if addresses are in self-relative tables like in the ARM EABI. The alternative to having the exception-handling code out of line is, of course, handling all exceptional conditions in line, which not only makes the code harder to read but makes the generated object code bigger and, if it fails to fit in a cache line or causes unwholesomely large numbers of branch evaluations (see spectre and meltdown) results in slower or more insecure code. The real answer to C++ exceptions is the classic C-style programming where all errors are just ignored. Of course, no amount of reasoning or hard data survives in the face of religious belief, and 80% of programmers out there are part of one cargo cult or another, so carry on with what you were doing. It's probably good enough.
- notacoward 7y ago> classic C-style programming where all errors are just ignored That's a mischaracterization of most C code written by professionals, because (with appropriate compiler flags) return codes have to be explicitly ignored. Contrast with languages where most functions don't even have a return code, and the compiler/interpreter doesn't complain about failing to catch possible exceptions. I see this a lot in Python code (which I generally like and have liked since 1.5 BTW), a bit less so in C++, etc. Even "elite" programmers in those languages tend to be sloppy that way, which I rarely see in C programmers with more than a couple of years' experience.
- bregma 7y ago> That's a mischaracterization of most C code written by professionals I've been a professional programmer for 40 years, much of that in C. I've seen a lot of code, some of it in production in critical systems for many years. It is not a mischaracterization, it is a description of the state of the art.
- kats 7y agoOnly when they are thrown. (for reference: https://www.youtube.com/watch?v=COEv2kq_Ht8 https://www.youtube.com/watch?v=COEv2kq_Ht8)
- mhh__ 7y agoYes and no. I haven't measured the exact effect of this, but - although that if the wind doesn't throw the ship doesn't slow - having an exception-al path through the code will stop some compiler optimizations. I have seen this first hand but I also remember Walter Bright mentioning it either here or elsewhere. If this is definitely the case maybe someone could chime in why exactly (I've never fiddled with a backend in the right places to deal with exceptions)
- jcranmer 7y agoBy default, every function call--including external function calls--can cause an exception to be thrown. Any unresolved function call now causes an extra edge in the control-flow graph that goes to what is effectively a return in the function. This extra edge is also harder to manipulate for normal CFG optimizations (e.g., splitting critical edges or duplicating nodes).
- gmueckl 7y agoYes, I also was pretty oblivious to this until Walther Bright mentioned it in the D forums a few months back. I went back and did some simple tests with the compiler explorer and sure enough, I got some extra overhead in the generated code with exceptions enabled as soon as the compiler could not prove the absence of exceptions.
- dgellow 7y ago> I like C++ exceptions as a feature In your opinion, what are situations/examples that justify the use of exceptions? I haven't used a language with exceptions since 3 years (i.e: since I switched to Go), and I haven't yet faced a situation where I felt a need for them. Also, I started to learn C++ as a hobby a few months ago, and I have to say that I still don't understand when I should use exceptions or not, things seem fine so far by just returning some error values.
- ailideex 7y agoExceptions are ideal for happy path programming. You keep state in the stack and build your software so things get thrown away. For some types of programming this is not that important but for OLTP it is very handy. https://ninenines.eu/articles/dont-let-it-crash/ https://ninenines.eu/articles/dont-let-it-crash/ Basically this approach is handle errors if you know what to do with them, otherwise leave the error for something else. In erlang, where you have one process per transaction, and nothing handles the error, the process crashes. In C++, you could just have an exception handler around a transaction, and then when an exception reaches that point you throw away the transaction, but log that it failed, you could have exception handlers below this to try and do rollbacks etc. This is overly simplified but it gives a basic idea.
- dgellow 7y agoThanks for your answer. What is OLTP in that context? Is it https://en.wikipedia.org/wiki/Online_transaction_processing https://en.wikipedia.org/wiki/Online_transaction_processing? First time I see that acronym.
- ailideex 7y agoOLTP in this context is indeed online transaction processing. This general approach of «let it "crash"[1]» or happy path programming is what I used when I wrote OTLP systems for mobile networks which were performing financial transactions. The exception handling would allow you to write fairly complex business logic and relying on exception handling to recover to a stable state. [1]: Crashing in Erlang terms is not at all what people generally mean with crashing. It is a very unfortunate word because in part many of the problems in node.js comes from joyent not understanding what "let it crash" meant - it did not mean let the unix process crash. Erlang does one process per transaction and they are incredibly lightweight. Some details from joyent can be found here: https://www.joyent.com/node-js/production/design/errors https://www.joyent.com/node-js/production/design/errors
- AshamedCaptain 7y agoWhy do people complain about exceptions being "non-deterministic"? Malloc is not deterministic either, so in theory it causes bloat and unexplained slowdowns everywhere.
- fwsgonzo 7y agoWell, there are several kinds of allocation strategies, so while the general malloc does a best-effort, I have no real choice with exceptions at the moment, other than to turn them off. I do a lot of stuff in embedded, and it would be nice to have something like this. I'm also experimenting with using C++ as a compiled scripting language for a game engine, and for that I would like the binaries to be really small. Because of docker, compiling freestanding binaries for any architecture is not such a barrier anymore.
- marcosdumay 7y agoIt's common to allocate all the memory you will need at the beginning of execution and not touch malloc at all after that. You can't do anything similar for exceptions.
- jcelerier 7y ago> It's common in an extremely narrow field. Most if not all the apps running on my desktop right now are written in C++ and I'm pretty sure exactly zero do that.
- notacoward 7y agoApps aren't the entire computing universe. There are probably dozens of processors in the room with you right now, in everything from your smartwatch to your monitor to your thermostat to multiple components within your laptop/phone, each running firmware that somebody had to write in a more constrained way than apps can get away with. It's the "dark matter" of computing, but it's not narrow at all.
- jcelerier 7y ago
- jmiskovic 7y agoExceptions create another execution path through your code base. This means additional complexity, and one that is not often analyzed. I also don't like how exceptions are so often just passed around. They allow you to say "let's make this somebody else's problem". Having such huge mechanisms as optional programming language features is also problematic for collaboration on big project and for using third-party code that made different decision. I prefer when language designer is very opinionated on such decisions, and then the combination of all decisions either proves to be clumsy or gains wide following.