tags:

views:

829

answers:

10

What are the performance implications of using exceptions in C++0x? How much is this compiler dependent? Should we expect to use exceptions more for general logic handling like in Java?

+9  A: 

Exception performance is very compiler dependent. You'll have to profile your application to see if it is a problem. In general, it should not be.

You really should use exceptions for "exceptional conditions", not general logic handling. Exceptions are ideal for separating normal paths through your code and error paths.

Brian Neal
+1 for using exceptions only for exceptional conditions
Alex B
+1 from me too. Using exceptions for normal logic can lead to "exception spaghetti", where it's a job and a half just to figure out where the thing is caught. Especially with polymorphic code, when you might not even know which implementation of ListenerRegistry is your caller, short of running it and seeing what stack trace you get in your current configuration. If exceptions are only used for situations expected to be unrecoverable, then most levels of code won't even think about trying to recover from them :-)
Steve Jessop
+3  A: 

One would imagine they would have about the same performance as in C++03, which is "very slow!" And no, exceptions should be use in exceptional circumstances due to the try-catch-throw construct, in any language. You're doing something wrong if you are using throw for program flow control in java.

rlbond
For values of "very slow!" approximating 1ms on my laptop and c++ compiler. Obviously if that's in your innermost loop it's a problem. I think the reason not to use exceptions for non-error situations is that it makes your code's control flow harder to follow, not FUD about performance.
Steve Jessop
@onebyone - it's not FUD about performance, the performance hit is real and very significant, and exceptions wreak havoc with real-time applications.
Not Sure
It's FUD if you say the cost of exceptions is "very significant" without knowing what percentage slower an actual app will run as a consequence of using them. Sometimes it's significant, usually it isn't. And you can't use "it would break a realtime app" as a reason not to use a particular language feature, since (a) almost all apps are not realtime, and (b) almost all language features could break a realtime app, by making it impossible to predict cache misses and whatnot.
Steve Jessop
A: 

Profiling for exception overhead can be quite tricky, actually... unwinding the call stack may not be observed by the profiler on some platforms.

Andrew
+3  A: 

I don't think C++0x adds to or changes anything in the way C++ exceptions work. For the general advise take a look here.

Nikolai N Fetissov
A: 

Exception handling is an expensive feature, in general, because throwing/catching implies additional code to be executed to ensure stack unwinding and catch condition evaluation.

As far as I understood from some readings, for Visual C++, for example, there are some pretty complex structures and logic embedded in the code to ensure that. Since most of the functions might call other functions that might throw exceptions, some overhead for stack unwinding may exist even in these cases.

However, before thinking on exception overhead, it is best to measure the impact of exception usage in your code before any optimization action. Avoiding exception overusage should prevent exception handling significant overheads.

Cătălin Pitiș
You realise that if you return normally, the stack still has to unwind?
Steve Jessop
Exceptions are basically free while there is no exception propogating the extra cost in placing try catch blocks is neglagable at best. Unwinding because of an exception cost more but no more than it would to write the code to pass error codes back. So the statement that it is expensive is totally false.
Martin York
+3  A: 

Imagine a bell goes off, the computer stops accepting any input for three seconds, and then someone kicks the user in the head.

That's the cost of an exception. If it's preventing data loss or the machine from catching on fire, it's worth the cost. Otherwise, it probably isn't.

EDIT: And since this got a downvote (plus an upvote, so +8 for me!), I'll clarify the above with less humor and more information: exception, at least in C++ land, require RTTI and compiler and possibly operating system magic, which makes the performance of them a giant black hole of uncertainty. (You aren't even guaranteed that they will fire, but those cases happen during other more serious events, like running out of memory or the user just killing the process or the machine actually catching on fire.) So if you use them, it should be because you want to gracefully recover from a situation that otherwise would cause something horrible to happen, but that recovery cannot have any expectations of running performantly (whatever that may be for your specific application).

So if you are going to use exceptions, you cannot assume anything about the performance implications.

MSN
What? Exceptions do not require operating system magic, nor compiler magic. They are well understood. Not guaranteed to fire? Sorry but this is flat out wrong.Exceptions can be extremely useful for managing error and unexpected conditions, as well as for creating more maintanable code by separating normal path and error paths. You simply cannot say they are almost never worth the cost. Not all applications require high or realtime performance, and frequently you don't care about performance when your program is in an error condition.
Brian Neal
Looking at the page faults per second on my machine, I'm a very worried that someone someday might combine earlz answer ("interrupts emulated with exceptions") and yours ("exceptions kick user in head").
Steve Jessop
IN C++, you cannot always guarantee a particular exception will fire. For example, if you throw an exception and you run out of stack space or memory to allocate the exception from, bad things happen.At a higher level, this isn't a statement about the utility of exceptions; it's a statement of the performance implications if you care about them. Which you shouldn't. Which is my whole point.
MSN
Sure, if more bad things happen while an exception is already active, it may not "fire". But at that point it is game over and your program is likely to crash anyway. I.e. nothing is going to work. This is not a reason to avoid exceptions.
Brian Neal
Systems which need to be robust under failure can use stack margins and so on if they also want to use exceptions. Same way that interrupt handlers are (often) run on a separate stack, so that they still work in critical out-of-resource situations.
Steve Jessop
"So if you are going to use exceptions, you cannot assume anything about the performance implications."I suppose I can edit the post to say that you need to weigh everything, but I thought that was implicit in that final statement.
MSN
Tempting to downvote because of the nonsense about "not guaranteed to fire", but the rest of your post makes sense (you can't assume anything much about the performance of exceptions), so I won't. True, an exception isn't technically guaranteed to fire, but then neither is a function guaranteed to be evaluated when you call it. You could run out of stack space there too. The user could switch off the computer. Saying they are not guaranteed is misleading in the extreme.
jalf
@jalf, I edited my post to clarify what I mean by "not guaranteed to fire".
MSN
Your answer is still a lot of FUD. "Compiler magic", "Operating System magic". C'mon. Examples please. Plenty of programs could benefit from the use of exceptions. Saying they are only worth it to prevent your machine from catching on fire because their performance cost is too great is absurd. Have you ever done any large scale C++ development? First you say they are too expensive, then you say you can't assume anything about their performance. Sorry, bad answer.
Brian Neal
"First you say they are too expensive, then you say you can't assume anything about their performance." Um, those two statements aren't contradictory. I am trying to point out that if you are worried about the cost of exceptions, the cost is unbounded without additional constraints. If the question is can I use exceptions with a reasonable cost in cases where I don't throw them, then that's a more precise question with a less ambiguous answer. But whatever. Feel free to disagree with any or all of it.
MSN
+14  A: 
#include <iostream>
#include <stdexcept>

struct SpaceWaster {
    SpaceWaster(int l, SpaceWaster *p) : level(l), prev(p) {}
    // we want the destructor to do something
    ~SpaceWaster() { prev = 0; }
    bool checkLevel() { return level == 0; }
    int level;
    SpaceWaster *prev;
};

void thrower(SpaceWaster *current) {
    if (current->checkLevel()) throw std::logic_error("some error message goes here\n");
    SpaceWaster next(current->level - 1, current);
    // typical exception-using code doesn't need error return values
    thrower(&next);
    return;
}

int returner(SpaceWaster *current) {
    if (current->checkLevel()) return -1;
    SpaceWaster next(current->level - 1, current);
    // typical exception-free code requires that return values be handled
    if (returner(&next) == -1) return -1;
    return 0;
}

int main() {
    const int repeats = 1001;
    int returns = 0;
    SpaceWaster first(1000, 0);

    for (int i = 0; i < repeats; ++i) {
        #ifdef THROW
            try {
                thrower(&first);
            } catch (std::exception &e) {
                ++returns;
            }
        #else
            returner(&first);
            ++returns;
        #endif
    }
    #ifdef THROW
        std::cout << returns << " exceptions\n";
    #else
        std::cout << returns << " returns\n";
    #endif
}

Mickey Mouse benchmarking results:

$ make throw -B && time ./throw
g++     throw.cpp   -o throw
1001 returns

real    0m0.547s
user    0m0.421s
sys     0m0.046s

$ make throw CPPFLAGS=-DTHROW -B && time ./throw
g++  -DTHROW   throw.cpp   -o throw
1001 exceptions

real    0m2.047s
user    0m1.905s
sys     0m0.030s

So in this case, throwing an exception up 1000 stack levels, rather than returning normally, takes about 1.5ms. That includes entering the try block, which I believe on some systems is free at execution time, on others incurs a cost each time you enter try, and on others only incurs a cost each time you enter the function which contains the try. For a more likely 100 stack levels, I upped the repeats to 10k because everything was 10 times faster. So the exception cost 0.1ms.

For 10 000 stack levels, it was 18.7s vs 4.1s, so about 14ms additional cost for the exception. So for this example we're looking at a pretty consistent overhead of 1.5us per level of stack (where each level is destructing one object).

Obviously C++0x doesn't specify performance for exceptions (or anything else, other than big-O complexity for algorithms and data structures). I don't think it changes exceptions in a way which will seriously impact many implementations, either positively or negatively.

Steve Jessop
This is a great answer because the benchmarks and interpretation of data say much more than "I think bla bla bla".If you could repeat those with Windows and some versions of visual studio this woule be the "de facto" answer to exception handling
Edison Gustavo Muenz
I don't have MSVC to hand (this was run on Windows XP using g++ and cygwin). I'd also be *very* cautious about claiming that this is a good benchmark, since you never know with simple stuff whether some smart-alec compiler will figure out how to optimise away the recursion in one or other of the cases. Had that happen once in a conformance test that was supposed to check that the system had stack for N levels of recursion. Laying down structs as you go is a fairly reliable way to prevent it, but I make no promises, and the results should be interpeted quite carefully on each platform.
Steve Jessop
Testing with Windows/VS wouldn't be enough. The exception implementation is completely different between win32 and win64. (As well as for Windows running on x86 vs Itanium)
jalf
I don't think (no pun intended) that testing an implementation is more worth than saying what you think about something. If your thoughts are judged and based on facts, then they are equally valid, in a different way. This one would be the de-facto answer for "gcc and msvc, unoptimized build for x86". But as you imagine, there are a lot more compilers, and a lot more build configurations :) Anyway, +1 from me for this research answer
Johannes Schaub - litb
Not really that usefull. You need to compare the use of exceptions against the alternative (which is returning the error code). I bet the extra cost of the code is about the same. And the code looks a lot messey as a result.
Martin York
Also, try with a shorter stack depth as well. Exceptions are unlikely to benefit from the CPU's return prediction cache, so the difference ought to be more pronounced if the call stack is short (say, < 8 calls) so the non-exception case will benefit from said cache. Oh, and make your SpaceWaster destructor have some global effect so the optimizer can't eliminate the catch. :)
bdonlan
You're not throwing up 1000 stack frames. You're iterating 1000 times, and throwing up one stack frame. That's it. So a SINGLE tack frame adds 1.5ms of overhead. <quote>// typical exception-using code doesn't need error return values</quote> You should *ALWAYS* check and use return codes where appropriate, even if an exceptional condition can occur. Don't get into this habit.
Chris Kaminski
@bdonlan: just tried it with 3-depth recursion as well. 1M loops, 1.6s vs 14.7s, so down to 13us per exception. We can probably call this the cost of creating and throw/catching the exception. Unfortunately a small test case can't simulate the effects of reading exception frame data from all over the place.
Steve Jessop
@darthcoder: sorry, I don't know what you mean. Have I missed some optimisation the compiler will perform on some platforms? I only have one try/catch block, so only one *exception* frame if that's what the compiler uses, but I'm certainly creating 1000 stack frames on each rep, and every one of them has an object to destroy on an exception. Adding try/catch(int) in "thrower" costs an extra ~5% with 1000 reps of 1000 depth in my implementation. But it's not typical exception usage to catch at every level.
Steve Jessop
@darth: to your second point, the question was "should we use exceptions for general logic handling". I think it's fair to assume that if one does that (and this test is designed to investigate what would happen if one did that), then functions will only return on success, and throw on failure. Thus, no failure codes to check. I'm not saying I'd always like the resulting code, but the question is how it would perform. The answer seems to be, "in this oversimplified benchmark, not too bad for many practical purposes". If you have a better benchmark, let's see it :-)
Steve Jessop
+5  A: 

I once created an x86 emulation library and used exceptions for interrupts and such. Bad idea. Even when I was not throwing any exceptions, it was impacting my main loop a lot. Something like this was my main loop

try{
 CheckInterrupts();
 *(uint32_t*)&op_cache=ReadDword(cCS,eip);
 (this->*Opcodes[op_cache[0]])();
 //operate on the this class with the opcode functions in this class
 eip=(uint16_t)eip+1;

}
//eventually, handle these and do CpuInts...
catch(CpuInt_excp err){
 err.code&=0x00FF;
 switch(err.code){

The overhead of containing that code in a try block made the exception functions the 2 of the top 5 users of CPU time.

That, to me is expensive

Earlz
ouch. that is a big impact.
sean riley
Yes, it really is. When I profiled it, I was executing very simple code that never threw an exception(except for at the end) yet try and catch still require the stack to be unwound. And to clafiy I mean the two exception functions(there are two with gcc) was one of the top 5 functions listed sorted by amount of consumed CPU time
Earlz
Remember, for each stack frame, you have to call all the local object destructors and destroy local memory. Each frame is going to consume a non-negligible amount of memory. That's why they should be reserved for "exceptional" conditions.
Chris Kaminski
+3  A: 

There's no reason why exceptions in C++0x should be either faster or slower than in C++03. And that means their performance is completely implementation-dependant. Windows uses completely different data structures to implement exception handling on 32-bit vs 64-bit, and on Itanium vs x86. Linux isn't guaranteed to stick with one and just one implementation either. It depends. There are several popular ways to implement exception handling, and all of them have advantages and downsides.

So it doesn't depend on language (c++03 vs 0x), but on compiler, runtime library, OS and CPU architecture.

jalf
+2  A: 

I basically think the wrong question has been asked.
What is the cost of exception is not usefull, more usefull is the cost of exceptions relative to the alternative. So you need to measure how much exceptions cost and compare that to returning error codes >>>AND<<< checking the error codes at each level of the stack unwind.

Also note using exceptions should not be done when you have control of everything. Within a class returning an error code is probably a better technique. Exceptions should be used to transfer control at runtime when you can not determine how (or in what context) your object will be utilised at runtime.

Basically it should be used to transfer control to higher level of context where an object with enough context will understand how to handlle the exceptional situation.

Given this usage princimple we see that exceptions will be used to transfer control up multiple levels in stack frame. Now consider the extra code you need to write to pass an error code back up the same call stack. Consider the extra complexity then added when error codes can come from multiple different directions and try and cordinate all the different types of error code.

Given this you can see how Exceptions can greatlly simplify the flow of the code, and you can see the enhirit complexity of the code flow. The question then becomes weather exceptions are more expensive than the complex error conditions tests that need to be performed at each stack frame.

The answer as always depends (do both profile and use the quickist if that is what you need).

But if speed is not the only cost.
Maintainability is a cost that can be measured. Using this metric of cost Exceptions alway win as they ultimately make the constrol flow of the code to just the task that needs to be done not the task and error control.

Martin York
"Now consider the extra code you need to write to pass an error code back" My numpty benchmark attempts to simulate this (see the "returner" function's recursive call). I haven't checked whether the compiler optimises it. So my estimate of 13us for an exception caught 3 levels up could be an over-estimate. My 'throw' loop certainly is proportionally much slower than the 'return error' loop. Of course that's before any real work is added. I'd appreciate any suggestions for how to make my "returner" loop more accurately reflect "check return code and bail out" error-handling.
Steve Jessop
In particular, "returner" could well be slower in the *success* case, depending which way the compiler has chosen to emit the branch. You often don't hear very much from the "exceptions! Yuck! Glacially slow!" crowd about how they're introducing additional branches (compared with exception-based error-handling) into the case of normal, successful operation.
Steve Jessop