Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

Are exceptions used widely in game engine design or it is more preferable using pure if statements? For example with exceptions:

try {
    m_fpsTextId = m_statistics->createText( "FPS: 0", 16, 20, 20, 1.0f, 1.0f, 1.0f );
    m_cpuTextId = m_statistics->createText( "CPU: 0%", 16, 20, 40, 1.0f, 1.0f, 1.0f );
    m_frameTimeTextId = m_statistics->createText( "Frame time: 0", 20, 20, 60, 1.0f, 1.0f, 1.0f );
    m_mouseCoordTextId = m_statistics->createText( "Mouse: (0, 0)", 20, 20, 80, 1.0f, 1.0f, 1.0f );
    m_cameraPosTextId = m_statistics->createText( "Camera pos: (0, 0, 0)", 40, 20, 100, 1.0f, 1.0f, 1.0f );
    m_cameraRotTextId = m_statistics->createText( "Camera rot: (0, 0, 0)", 40, 20, 120, 1.0f, 1.0f, 1.0f );
} catch ... {
    // some info about error
}

and with ifs:

m_fpsTextId = m_statistics->createText( "FPS: 0", 16, 20, 20, 1.0f, 1.0f, 1.0f );
if( m_fpsTextId == -1 ) {
    // show error
}
m_cpuTextId = m_statistics->createText( "CPU: 0%", 16, 20, 40, 1.0f, 1.0f, 1.0f );
if( m_cpuTextId == -1 ) {
    // show error
}
m_frameTimeTextId = m_statistics->createText( "Frame time: 0", 20, 20, 60, 1.0f, 1.0f, 1.0f );
if( m_frameTimeTextId == -1 ) {
    // show error
}
m_mouseCoordTextId = m_statistics->createText( "Mouse: (0, 0)", 20, 20, 80, 1.0f, 1.0f, 1.0f );
if( m_mouseCoordTextId == -1 ) {
    // show error
}
m_cameraPosTextId = m_statistics->createText( "Camera pos: (0, 0, 0)", 40, 20, 100, 1.0f, 1.0f, 1.0f );
if( m_cameraPosTextId == -1 ) {
    // show error
}
m_cameraRotTextId = m_statistics->createText( "Camera rot: (0, 0, 0)", 40, 20, 120, 1.0f, 1.0f, 1.0f );
if( m_cameraRotTextId == -1 ) {
    // show error
}

I heard that the exceptions are a bit slower than ifs and I shouldn't mix exceptions with if-checking method. However with exceptions I have more readable code than with tons of ifs after every initialize() method or something similar, though they are sometimes too heavy for just one method in my opinion. Are they ok for game development or better to stick with simple ifs?

share|improve this question

5 Answers 5

up vote 39 down vote accepted

Short answer: read Sensible Error Handling 1, Sensible Error Handling 2, and Sensible Error Handling 3 by Niklas Frykholm. Actually, read all the articles on that blog, while you're at it. Won't say I agree with everything, but most of it is gold.

Don't use exceptions. There are a plethora of reasons. I will list the major ones.

They can indeed be slower, though that is minimized quite a bit on newer compilers. Some compilers support "zero overhead exceptions" for code paths that don't actually trigger exception (although that's a bit of a lie, since there's still extra data that exception handling requires, bloating your executable/dll size). The end result though is that yes, using exceptions is slower, and in any performance critical code paths, you should absolutely avoid them. Depending on your compiler, having them enabled at all may add overhead. They always always bloat code size, rather significantly in many cases, which can severely affect performance on today's hardware.

Exceptions make code much more fragile. There's an infamous graphic (which I sadly can't find right now) that basically just shows on a chart the difficulty of writing exception-safe code vs exception-unsafe code, and the former is a significantly larger bar. There are simply a lot of little gotchas with exceptions, and many many ways to write code that looks exception safe but really isn't. Even the entire C++11 committee goofed on this one and forgot to add an important helper functions for using std::unique_ptr correctly, and even with those helper functions, it takes more typing to use them than not to and most programmers won't even realize what's wrong if they don't.

More specifically for the games industry, some consoles' vendor-supplied compilers/runtimes outright don't support exceptions fully, or even don't support them at all. If your code uses exceptions, may you still in this day and age have to rewrite portions of your code to port it to new platforms. (Unsure if that's changed in the 7 years since said consoles were released; we just don't use exceptions, have them disabled in the compiler settings even, so I don't know that anyone I talk to has even checked recently.)

The general line of thinking is pretty clear: use exceptions for exceptional circumstances. Use them when your program gets into a "I have no idea what to do, maybe hopefully someone else does, so I'll throw an exception and see what happens." Use them when no other option makes sense. Use them when you don't care if you accidentally leak a little memory or fail to clean up a resource because you messed up the use of proper smart handles. In all other cases, don't use them.

Regarding code like your example, you have several other ways to fix the problem. One of the more robust -- though not necessarily most ideal in your simple example -- is to lean towards monadic error types. That is, createText() can return a custom handle type rather than an integer. This handle type has accessors for updating or controlling the text. If the handle is put into an error state (because createText() failed) then further calls to the handle simply fail silently. You can also query the handle to see if it has errored, and if so, what the last error was. This approach has more overhead than other options, but it's fairly solid. Use it in cases when you need to perform a long string of operations in some context where any single operation could fail in production, but where you don't/can't/won't check for errors after every call.

An alternative to implementing monadic error handling is to, rather than using custom handle objects, make the methods on the context object gracefully deal with invalid handle ids. For example, if createText() returns -1 when it fails, then any other calls to m_statistics that take one of those handles should gracefully exit if -1 is passed in.

You can likewise put the error printing inside the function that is actually failing. In your example, createText() likely has way more information about what went wrong, so it'll be able to dump a more meaningful error to the log. There's little benefit in this case to pushing the error handling/printing out to callers. Do that when the callers have need to customize the handling (or use dependency injection). Note that having an in-game console that can pop up whenever an error is logged is a good idea and helps here, too.

The best option (already presented in the linked series of articles above) for calls that you don't expect to fail in any sane environment -- like the simple act of creating text blobs for a statistics system -- is to just have the function that failed (createText in your example) abort. You can be reasonably sure that createText() is not going to fail in production unless something is totally wonked out (e.g., the user deleted font data files, or for some reason only has 256MB of memory, etc.). In many of these cases, there isn't even a good thing to do when failure does happen. Out of memory? You might not even be able to make an allocation necessary to create a nice GUI panel to show the user the OOM error. Missing fonts? Makes it hard to display errors to the user. Whatever's wrong, a crash handler can pop up a message to the user, offer to send the log file in to the developer, and hint that the user should probably reinstall the software since it's quite possibly missing some important component.

Just crashing is totally fine so long as you (a) log the error to a log file and (b) only do it on errors that aren't caused by regular user actions.

I wouldn't say the same thing at all for many server apps, where availability is critical and watchdog monitoring is not sufficient, but that is quite different than game client development. I would also strongly steer you clear of using C/C++ there as the exception handling facilities of other languages tend not to bite like C++'s since they are managed environments and don't have all the exception-safety problems that C++ has. Any performance concerns are also mitigated as servers tend to be focusing more on parallelism and throughput than minimum latency guarantees like the game clients. Even action game servers for shooters and the like can function quite well when written in C#, for example, since they're rarely pushing the hardware to its limits like FPS clients tend to do.

share|improve this answer
1  
+1. In addition there is possibility to add atribute like warn_unused_result to function in some compilers which allows to catch non-handled error code. –  Maciej Piechotka Dec 29 '12 at 15:01
    
Came here seeing C++ in the title, and was totally sure monadic error handling wouldn't already be here. Good stuff! –  Adam Dec 29 '12 at 17:48
    
Thanks Sean for sharing that great article. –  Maik Semder Dec 29 '12 at 23:28
    
what about places where performance is not critical and you are mostly aiming for a fast implementation? for example when you are implementing game logic? –  Ali.S Jan 2 '13 at 11:38
1  
@Gajoo: for game logic, exceptions just make the logic harder to follow (as it makes all code harder to follow). Even in Pythnn and C# exceptions are rare for game logic, in my experience. for debugging, the hard assert is usually way more convenient. Break and stop at the exact moment something goes wrong, not after unwinding large portions of the stack and losing all kinds of contextual information due to exception throwing semantics. For debugging logic, you want to build tools and info/edit GUIs, so designers can inspect and tweak. –  Sean Middleditch Jan 2 '13 at 17:17

The old wisdom of Donald Knuth himself is:

"We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil."

Print this as a big poster and hang it out everywhere whenever you do serious programming.

So even if try/catch is a little slower then I would use it by default:

  • The code must be as readable and understandable as possible. You might write the code once but you will read it many more times for debugging this code for debugging other code, for understanding, for improvement, ...

  • Correctness over performance. Doing the if/else stuff correctly is not trivial. In your example it is done incorrectly, because not one error can be shown but multiple. You have to use cascaded if/then/else.

    -Easier to use consistently: Try/catch embraces a style where you don't need to check for errors every line. On the other hand: One missing if/else and your code might wreck havoc. Of course only in unreproducible circumstances.

So the last point:

I heard that the exceptions are a bit slower than ifs

If have heard so about 15 years ago. But I did not hear so from credible sources in the near past. Compiler might have improved or whatever.

The main point is: Do no premature optimization. Do it only, if you can proof by benchmark that the code at hand is the tight inner loop of some heavily used code and that switching style improves performance considerably. This is the 3% case.

share|improve this answer
10  
I will -1 this to infinity. I can’t fathom the harm that this Knuth quote has done to developer brains. Considering whether to use exceptions isn’t premature optimisation, it is a design decision, an important design decision that has ramifications far beyond performance. –  Sam Hocevar Dec 29 '12 at 13:26
3  
@SamHocevar: I'm sorry, I don't get your point: The question is about the possible performance hit when using exceptions. My point: Don't think about it, the hit is not that bad (if any) and other stuff is much more important. Here you seem to agree by adding "design decision" to my list. OK. On the other hand you say Knuth's quote is bad implying that premature optimization is not bad. But this is exactly what happened here IMO: The Q does not think about architecture, design or different algorithms, only about exceptions and their performance impact. –  A.H. Dec 29 '12 at 14:55
2  
I would disagree with clarity in C++. The C++ have unchecked exceptions while some compilers implement checked return values. As a result you have code that looks clearer but may have hidden memory leak or even put code in undefined state. Also third-party code might not be exception safe. (The situation is different in Java/C# which have checked exceptions, GC, ...). As of design decision - if they don't cross API points the refactoring from/to each style can be done semi-automatically with perl one-liners. –  Maciej Piechotka Dec 29 '12 at 15:08
1  
@SamHocevar: "The question is about what is preferable, not what is faster." Re-read the last paragraph of the question. The only reason he is even thinking about not using exceptions is because he thinks they might be slower. Now if you think that there are other concerns the OP didn't take into account, feel free to post them or upvote those who did. But the OP is very clearly focused on the performance of exceptions. –  Nicol Bolas Dec 29 '12 at 16:09
2  
@A.H. - if you're going to quote Knuth then don't forget the rest of it (and IMO the most important part): "Yet we should not pass up our opportunities in that critical 3%. A good programmer will not be lulled into complacency by such reasoning, he will be wise to look carefully at the critical code; but only after that code has been identified." All too often one sees this as an excuse to not optimize at all, or to try justify code being slow in cases where performance is not an optimization but is in fact a fundamental requirement. –  Darth Satan Jan 2 '13 at 12:44

There has already been a really great answer to this question but I would like to add some thoughts about code readability and error recovery in your particular case.

I believe your code could actually look like this:

m_fpsTextId = m_statistics->createText( "FPS: 0", 16, 20, 20, 1.0f, 1.0f, 1.0f );
m_cpuTextId = m_statistics->createText( "CPU: 0%", 16, 20, 40, 1.0f, 1.0f, 1.0f );
m_frameTimeTextId = m_statistics->createText( "Frame time: 0", 20, 20, 60, 1.0f, 1.0f, 1.0f );
m_mouseCoordTextId = m_statistics->createText( "Mouse: (0, 0)", 20, 20, 80, 1.0f, 1.0f, 1.0f );
m_cameraPosTextId = m_statistics->createText( "Camera pos: (0, 0, 0)", 40, 20, 100, 1.0f, 1.0f, 1.0f );
m_cameraRotTextId = m_statistics->createText( "Camera rot: (0, 0, 0)", 40, 20, 120, 1.0f, 1.0f, 1.0f );

No exceptions, no ifs. The error reporting can be done in createText. And createText can return a default texture ID that doesn’t require you to check the return value, so that the rest of the code works just as well.

share|improve this answer

Many people claim that Boost is bad for game development, but I recommend you look at "Boost.optional" it will make your ifs example a little nicer

share|improve this answer

There's a nice talk about error handling by Andrei Alexandrescu, I used a similar approach to his without exceptions.

share|improve this answer
    
I feel like this answer would be more useful if the link was summarized in case it ever goes away. –  Josh Petrie Jan 6 '13 at 19:37

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.