Exception(al?) performance

Read on for an exception performance review with Visual C++ and x86 vs. the new x86_64 ABI.

Test setup

All tests were compiled using Visual C++ 8.0 in the default release mode. The host system was a Turion64 with 1.8 GHz running Windows XP x64. Here are the raw performance timings:

Base-line performance without any kind of exception handling
ABI Time (in sec.)
x86 0.281
x86_64 0.156

Obviously, the x86_64 ABI is already better in this simple test-case, let's so how well it performs when it comes to exception handling.

Throwing exception

Let's start off with the unusual case - we'll throw hundreds of thousands of exceptions in tight loops, rethrow them in recursive functions and catch them at the highest level. After that, we'll only throw at the deepest recursion level and capture at the top-most level.

Worst-case: Exceptions thrown at deepest level, captured and rethrown inside the function, captured in main
ABI Time (in sec.)
x86 47.25
x86_64 208.234

Really horrible, isn't it? From roughly 200 times up to 1000 times slower. Note however that in this example, 12.5 million exceptions have been throw, of which 10 million have been rethrown again and 2.5 million exceptions have been caught somewhere.

Exceptions thrown at deepest level, not handled inside the function, captured in main
ABI Time (in sec.)
x86 12
x86_64 23.469

Much much better already, again, 2.5 million exceptions thrown and captured. Obviously the stack unwinding is fast, but the x86 ABI is able to find the right handler more than twice as fast as the x86_64.

Not throwing exception

Let's take a look what happens when we handle exceptions basically everywhere but if we don't throw them.

No exceptions thrown, captured and rethrown inside the function, captured in main
ABI Time (in sec.)
x86 0.484
x86_64 0.218

This is an interesting result, isn't it? When no exception is throw, the performance is nearly equal to the completely exception-free version.

Best-case: No exceptions thrown, not handled inside the function, captured in main
ABI Time (in sec.)
x86 0.421
x86_64 0.203

This is probably the usual case. High-level code is catching exceptions which might be thrown somewhere in the low level code. Actually the situation exceptions have been primary designed for ;) . And here, the x86_64 ABI really pays off. Seems those folks did a good job, the exception handling version became merely 25% slower, while the x86 slowed down by 50%. Please note that the test function was rather pathetic, a single fmul/fdiv and an add. In real-world code, exception handling won't slow things down that much (except you start adding exception handling to high-performance code inside the tightest loops), I've observed slowdowns in the range of 1-2% on larger functions.

Conclusion

Exception performance is not as bad as many think, and most importantly, it's getting better with the new x86_64 ABI. Finally you pay for the exceptions your throw and not for those you don't throw, and this is how it was meant to be from the beginning.

Comments

Comments powered by Disqus