Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cost of throwing C++0x exceptions

What are the performance implications of throwing exceptions in C++0x? How much is this compiler dependent? This is not the same as asking what is the cost of entering a try block, even if no exception is thrown.

Should we expect to use exceptions more for general logic handling like in Java?

like image 778
sean riley Avatar asked Jun 19 '09 16:06

sean riley


People also ask

Is it expensive to throw exceptions?

Since throwing and handling exceptions is expensive, we shouldn't use it for normal program flows. Instead, as its name implies, exceptions should only be used for exceptional cases. The complete source code can be found over on GitHub.

Are C++ exceptions expensive?

As a rule of thumb, exception handling is extremely cheap when you don't throw an exception. It costs nothing on some implementations. All the cost is incurred when you throw an exception: that is, “normal code” is faster than code using error-return codes and tests. You incur cost only when you have an error.

Can you throw exceptions in C?

C doesn't support exceptions. You can try compiling your C code as C++ with Visual Studio or G++ and see if it'll compile as-is. Most C applications will compile as C++ without major changes, and you can then use the try... catch syntax.

Why are exceptions so expensive?

So we clearly see there is an extra cost for exception handling that increases the deeper the stack trace goes. This is because when an exception is thrown the runtime needs to search up the stack until it hits a method than can handle it. The further it has to look up the stack, the more work it has to do.


2 Answers

#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.

like image 176
Steve Jessop Avatar answered Sep 20 '22 20:09

Steve Jessop


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.

like image 30
Brian Neal Avatar answered Sep 18 '22 20:09

Brian Neal