WWW.XYZW.DE The homepage of Dierk "Chaos" Ohlerich  
Home Releases Codes
Types FPU intrinsics Input Lag Iterate & Delete Load & Save Memory Leaks About Exceptions AGP Writes

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.