Measuring Exception Handling Overhead in C++

Overhead of exception handling in D

I cannot speak about D or any of its compilers, but I can tell you some about C++, Windows, and the Visual Studio compiler. This might help you understand roughly how D does things.

First, exception handling on 32- and 64-bit machines is different. The x86 ABI (prolog/epilog, unwinding, calling convention) is more lax, so the compiler and the program itself must do more work. The x86-64 ABI is stricter and the OS plays a bigger role, making it easier for the program itself to work with exceptions. If you're running D on Windows, then it likely uses SEH (structured exception handling) like C++ does.

Again, all of my answers below relate to Windows, C++, and Visual Studio.

What if I write no exception handling
code?

x86/x86-64: There is no cost for that method.

What if I do, but no exceptions are
ever thrown?

x86: There is a cost even when exceptions aren't thrown. Exception handling information is pushed into the TIB (thread information block) such as the initial scope and the function-specific exception handler. In order to know what objects to destruct and what handlers to search, a scope variable is maintained. This scope variable is updated as you enter try blocks and construct stack objects that have destructors.

x86-64: Because of the stricter rules, there is no extra code (or very very minimal). This is a big advantage over x86.

What if I do, and exception are
thrown?

On either x86 or x86-64, there will definitely be a hit. Exceptions should be exceptional though. Do not use them for regular control flow. Only use them to signal truly exceptional, unexpected events. Essentially, you should never have to worry about the cost of exceptions. Even if they took 2 seconds, you shouldn't care, because they should only happen when everything is going south anyway.

With that said, throwing exceptions on x86-64 is more expensive than throwing them on x86. The x86-64 architecture is optimized around the case of no exceptions being thrown, which, ideally, is nearly all the time.


Big picture:

  • I can't see propagating error codes being materially faster than exception handling, especially on x64 platforms.
  • I doubt you will ever find exception handling being a problem unless you're abusing exceptions.
  • If you're unsure, you should measure the performance of your code.

run-time penalty of C++ try blocks

The answer, as usually, is "it depends".

It depends on how exception handling is implemented by your compiler.

If you're using MSVC and targeting 32-bit Windows, it uses a stack-based mechanism, which requires some setup code every time you enter a try block, so yes, that means you incur a penalty any time you enter such a block, even if no exception is thrown.

Practically every other platform (other compilers, as well as MSVC targeting 64-bit Windows) use a table-based approach where some static tables are generated at compile-time, and when an exception is thrown, a simple table lookup is performed, and no setup code has to be injected into the try blocks.

What is the real overhead of try/catch in C#?

I'm not an expert in language implementations (so take this with a grain of salt), but I think one of the biggest costs is unwinding the stack and storing it for the stack trace. I suspect this happens only when the exception is thrown (but I don't know), and if so, this would be decently sized hidden cost every time an exception is thrown... so it's not like you are just jumping from one place in the code to another, there is a lot going on.

I don't think it's a problem as long as you are using exceptions for EXCEPTIONAL behavior (so not your typical, expected path through the program).

Performance when exceptions are not thrown (C++)

Please see my detailed response to a similar question here.

Exception handling overhead is platform specific and depends on the OS, the compiler, and the CPU architecture you're running on.

For Visual Studio, Windows, and x86, there is a cost even when exceptions are not thrown. The compiler generates additional code to keep track of the current "scope" which is later used to determine what destructors to call and where to start searching for exception filters and handlers. Scope changes are triggered by try blocks and the creation of objects with destructors.

For Visual Studio, Windows, and x86-64, the cost is essentially zero when exceptions are not thrown. The x86-64 ABI has a much stricter protocol around exception handling than x86, and the OS does a lot of heavy lifting, so the program itself does not need to keep track of as much information in order to handle exceptions.

When exceptions occur, the cost is significant, which is why they should only happen in truly exceptional cases. Handling exceptions on x86-64 is more expensive than on x86, because the architecture is optimized for the more common case of exceptions not happening.



Related Topics



Leave a reply



Submit