About Exceptions
The Problem
Many people take for granted that C++ exception handling is for free unless
an exception is thrown. Some even think that exceptions can be used as an
alternative control structure, a more structural replacement for the feared
goto. Unfortunatly both is not true.
Overhead when throwing
If you are using exceptions for local control flow, you should be aware
that awful things happen when an exeption is thrown. I am talking about things
beyond your imagination. In the case of Visual C++, some things deep inside the
Windows Operating System are involved. I am shure
you have no idea about stack unwinding and the fs-register if you use exceptions
for local control, if you knew you wouldn't. So I don't even try to explain.
Just imagine thousands of assembler instructions have to be executed even in
the simplest case.
But those who have read
the book correctly
remember that it's a really bad idea to throw exceptions on a regular basis.
Modern implementations tend to make the thrown case more and more expensive
in order to optimise the main path. But still there is some overhead we
should be aware. I will discuss the implementation of Visual C++ in more detail:
Runtime-Library
Exception handling requires a lot of Runtime-Library support. So if
you are targeting extremly small executables that don't use the
standard C/C++ runtime (like 64k intros), you can't use Exception Handling.
Per-Function overhead
Most compilers generate some extra info on the stack to enable exeption
handling. This is included in every function that calls a function that might
throw. In other words: every function that calls another non-inlined function.
Global optimisations can detect which function don't throw exeptions, but
I wouldn't count on that working.
In the case of the visual studio, the code looks like this:
; prolog
push exceptiontable
mov eax, DWORD PTR fs:__except_list
push eax
mov DWORD PTR fs:__except_list, esp
; epilog
mov ecx, DWORD PTR __$EHRec$[esp+116]
mov DWORD PTR fs:__except_list, ecx
The use of the FS register should make you shiver.
The "exceptiontable" will increase the
size of the executable a bit, but it's stored in a different segment so
it woun't pollute the cache.
This does not sound so bad, but it will make small functions a lot less
attractive. Very small functions should be inlined anyway, but you want
that your code fit's in the instruction cache, so you should not inline
too aggressivly outside the inner loops. A coding style with many
small functions suffers a lot from this.
Overhead for preparing the cleanup
If you use objects on the stack that have constructors and destructors,
the compiler has to add some code for cleaning up in case something failed.
Visual C++ does this by regulary updating a status variable. If an exception
occours, the exception handler uses this state and the per-functon
exception table mentioned above to call the right destructors.
Lost optimisation opportunities
Each time you call a function, the compiler needs to assume that an
exception might be thrown. This includes that the compiler need to store
every variable hold in a register before the function call, even if the
compiler knows that the function called does not have access to that object.
This is very bad. Not only that more instructions are executed, those
instructions are very expensive: memory stores.
|