4 ms·
Exceptions are not meant to be fast. In fact, exceptions are meant to be exceptional. Hence the name. Exceptions are meant to be fast if and only if you do not
by fefe23 7y ago
Exceptions are not meant to be fast. In fact, exceptions are meant to be exceptional. Hence the name.
Exceptions are meant to be fast if and only if you do not throw any. And even this is only truly true on Unix.
The original implementation of exceptions on Windows put extra unrolling info on the stack and then cleaned it up at the end of the function, adding a runtime cost even when no exceptions were thrown.
The Unix way of exception handling does not put anything extra on the stack, so exceptions are free if you don't throw any. The compiler puts some metadata into your binary and generates cleanup code. If an exception is thrown, the run-time (libstdc++ for gcc on Linux) will grab the return address from the stack and then do a binary search through the metadata to find the right unwinding code generated by the compiler. This is obviously a comparatively enormous overhead. In particular because the metadata are separated from the code, so the OS never has to read it from disk unless you actually throw an exception.
The reason for that is that you are not supposed to use exceptions for signaling. They are for error handling. Exceptions are for occasions like "you asked for memory but there was none left" or "you asked for the 15th element in this vector but it only has 10". They are not meant for non-exceptional errors like "you asked for data from the network but there is no data yet" or "you wanted me to check if the file exists and it does not".
The thinking behind all this is that the error handling path is not time critical. If it happens, something exceptional has happened, and the OS will probably have to swap in the error handling code from disk to begin with, which is horrendously slow. In fact, this is not just a possibility. The feature was designed specifically so that the linker would move the exception handling code to a different page and the OS would only read that page from disk if an exception actually occurs. This is not an accident. It is deliberate planning and took engineering effort to achieve.
Running a benchmark of throw vs return is an attempt to answer the wrong question.
Important side note: operator[] on a vector does not do range checking. at() does. That's why you should always use at() instead of operator[].
- deleted 7y ago[deleted]
- quietbritishjim 7y agoYou seem to be arguing that, because exception tables end up on separate pages, we should consider exceptions as being as expensive as disk accesses. (Apologies if I've just built a straw man, that's how I read it.) I don't buy that. If you use exceptions more than totally exceptionally, according to your definition, then those pages will be loaded to memory and kept there – while the optimisation sounds very clever and sensible, I have a hard time imagining that exception metadata is huge in comparison with the rest of the program code. Once that data is in memory, you're back to the numbers in the article, where throwing an exception can take barely more than a microsecond. That's huge compared to a function call, but is fast compared to a lot of things that an overly cautious programmers wouldn't dare follow up with an exception. To steal your own example, checking whether a file exists certainly falls into this category – that's likely to take milliseconds. (Even your other example – no data available to read (I'm imagining a select / poll situation) – would probably be fine with exceptions, though I agree it's not the right tool for that situation. Just making a call to the OS is likely to cost a comparible amount to throwing an exception, even though you're describing a non blocking call. And by definition this won't end to being called in a critical inner loop - if data becomes available then the exception will do being thrown.)
- edoo 7y agoHe makes the point that exceptions should be truly exceptional conditions for your program that would prevent it from running, generally due to system malfunction. Some people use it for basic business logic and it was never meant for that. It is meant to help you gracefully abort things when everything is about to go to hell.
- PaulDavisThe1st 7y agoThis is not an accurate summary of what C++ exceptions where "meant for". Here's Bjarne in 1989: "The author of a library can detect errors, but does not in general have any idea what to do about them. The user of a library may know how to cope with such errors, but cannot detect them – or else they would have been handled in the user’s code and not left for the library to find. The primary purpose of the exception handling mechanism described here is to cope with this problem for C++ programs; other uses of what has been called exception handling in the literature are considered secondary. See the references for discussions of exception handling techniques and mechanisms."
- gpderetta 7y agoI believe that MSVC in 64 bit mode also uses an (incompatible) table based unwind mechanism.