When it comes to the use of C++ exceptions, there are exceptionally strong feelings in the programming community. Some programmers are adamant that exceptions should be avoided at all costs. Others feel that exceptions are far and away the best method for handling errors.
Game programmers have very specific needs, and one of those needs is performance. Using C++ exception handling (CEH) has a performance implication. Of course, CEH has a variety of implications that also have to be taken into consideration, from code readability to exception safety.
I have the following general recommendations that apply whether you’re writing games, production tools, or any other software for that matter:
- Use Resource Acquisition Is Initialization (RAII), whether you use CEH or not. RAII guarantees that things that need to get cleaned up will get cleaned up, whether you take an early out from a function or whether an exception is thrown. RAII as a metaphor is extremely powerful and essential to creating robust code.
- If you use CEH, use it for triggering exceptional conditions only. Throwing is expensive — typically orders of magnitude more expensive than returning an error code.
- Never ever throw from C++ destructors, swap()-style routines, deallocation functions or other cleanup functions. See Exceptional C++ item 16 for details.
- Whether you use CEH or not, your entire team should understand exception handling in general. Read Herb Sutter’s excellent Exceptional C++ series. As Herb puts it: “‘Exception-unsafe’ and ‘poor design’ go hand in hand.” The more you understand about how exception handling works, the better C++ you will write — whether you use exceptions or not!”
- Use common sense and knowledge of your team’s strengths no matter what choice you use. New hires rarely know about CEH.
For professional game developers, I also stand by the following recommendations:
- Learn and understand the performance implications of CEH for your current compiler and platform(s). Generate code with and without CEH and examine the resulting assembly. You cannot have a rational conversation about the cost of either throwing or handling exceptions without this knowledge. For example, see this examination of the cost of CEH on 32 and 64-bit Windows.
- Consider using CEH in debug mode only and disabling CEH for retail builds, particularly on game consoles. Why? It’s better to crash (which should be extremely rare) than to have the overhead of exceptions (which often has a penalty even if you never throw). I’m aware of one popular game that disabled CEH, thereby eliminating a significant chunk of exception handling code that was generated by the compiler, which in turn enabled the game to have a better cache usage, which in turn led to a noticeable performance improvement in a typical game frame. On Xbox 360, CEH is off by default for new projects.
- In some cases, you may be using libraries that throw exceptions. You generally have three options: 1) keep CEH turned on and handle the exceptions appropriately, 2) turn CEH off and crash when an exception is thrown, or 3) wrap the library in a C++ exception handling layer but compile the rest of the game with CEH off.