Performance with using exceptions in C++ game programming

Started by
11 comments, last by Hodgman 6 years, 10 months ago

In the past, books stated that exception handling was slow for C++ and game dev. The books suggested to call std::nothrow when allocating memory, for example, and then using an if condition to see if the pointer is null or not. These days, I'm not sure. My assumption is it's fast these days. Has there been a best practice for using exception handling these days for games?

Advertisement

On some older consoles, C++ exceptions either were not supported at all, or were not supported by default with a huge recommendation not to enable support for them with a compiler flag as it would be very detrimental to performance.

e.g. on Wii you had this slow CPU with a tiny instruction cache. Enabling support for exceptions resulted in your compiled code being way larger (even if you never throw or catch anything!!), because it has to generate all the automatic-stack-unwinding code... and this meant that you'd end up with way more instruction-cache misses, which meant your game ran slower (even if you never throw or catch anything!!). That goes against the C++ mantra of only paying a cost when you use a feature... So in game-dev, many studios have outright banned the use of exceptions in C++ for all time. I'm one of those people - I use them freely in C#, but I pretend that they don't exist in C++.

The typical implementation of C++ exception handling on x86 is pretty bloated. The typical implementation on x86-64 is waaaay better, but still super bloated compared to the normal ways of passing around information.

I would only recommend using them if you actually need to throw a bit of information from one function to another that is far, far up the call-stack (and automatically unwind the call-stack in the process). If you're only going to be returning information one level up the call-stack, use pass-by-reference or return values like normal.

Interesting. I guess I'll continue to avoid them then unless it's necessary. Thanks for the thoughts.

Since C++11 functions not throwing exceptions and marked as such (noexcept) should run with no penalties.

but I pretend that they don't exist in C++

Interesting. I'm so far from the console world...

So I suppose you never use STL neither on consoles ? You might also avoid to use references (ie a dynamic_cast that fails on a reference should throw an exception). Or do you still disable exceptions with a compiler flag ? That can also be annoying when using middlewares...

You'll find that almost all C++ game middleware will avoid exceptions too, or even just expose a C API for ABI stability reasons :) Lots of users will simply regard middleware that uses exceptions as being "broken", so you'll find it gets "fixed" pretty quickly if they want to sell it to gamedevs. any non-broken middleware should also allow the user to supply function pointers for memory allocation, threaded execution and file system access. i.e. middleware that uses the default 'new' allocator is broken.

Dynamic cast is a code smell and almost has no good use cases... Plus C++ RTTI (which dynamic cast relies on) has an abhorrent implementation so is another feature that most gamedevs will pretend doesn't exist.

The std namespace also used to be very flaky across compilers so old codebases would've banned it. It's fine these days, but a lot of companies already have their own vector/map/etc so stick with their own replacements.
Using std along with a compiler option to disable exceptions is also fine... It just turns every exceptional situation into undefined behaviour, so you better add lots of your own assertions that prove these conditions cannot occur. Likewise, using std without any catch statements puts you in the same boat. In my experience, it's a pretty common boat to be in :)

I'm not really sure if dropping support for exceptions in current era is good idea.

Exceptions make code cleaner(for ex. you don't need to pass error code via method return codes or arguments), but they need to be used in exceptional situations(a lot of people abuse use of exceptions and try to hide some high level logic inside of catch block).

About performance, in x64 you don't pay any cost when calling function that may throw, all the cost is moved in case when function actually throws and you need to handle exceptions. But as i said, this should occur in exceptional situations(ex. missing file that should be there, problem with connection etc) and mostly at this time you don't care about performance.

I can't speak about consoles(i've never worked on one), but i bet their differ between architectures, as well in x86 land you pay small cost for every function call as compiler generates code for stack unwinding.

In the 10 years I've been in the industry, exceptions have been disabled on every single C++ project. Legacy code didn't use them, library code doesn't use them, game engines don't use them, C libraries can't use them, and most programmers in the industry don't know them well enough to make good use of them anyway.

The implementation of exceptions in C++ has never been particularly good anyway, not just in terms of performance but also in terms of the hoops you have to jump through to make your code fully exception-safe, so this hasn't been as much of a loss as people might think.

Thank you for the details.

I'll start to deviate a bit from the original topic...

Has anyone here heard about ISO C++ SG14 ? This is the study group from the C++ ISO standard which is studying how C++ could be improved for the game industry (among several others).

Here are some links in case you might be interested:

https://isocpp.org/blog/tag/sg14

https://groups.google.com/a/isocpp.org/forum/#!forum/sg14

What do people here think about this ? Do you think works like this might imply enough changes in the console hardware, or in the C++ mechanisms to allow such 'broken' things in gaming in a near/far future ?

To the extent where they fix things we already use, then yes. For example, most engines roll their own RTTI. If standard RTTI becomes just as effective and efficient, then I can see engines moving over to use that.

To the extent where they fix things we learned to work around or live without, such as exceptions, then no. I don't think anyone is going to retrofit exceptions into existing engines.

You'll find that almost all C++ game middleware will avoid exceptions too
*cough* https://raw.githubusercontent.com/KhronosGroup/Vulkan-Hpp/master/vulkan/vulkan.hpp

Sorry, I couldn't resist :)

But on a more serious note, to the OP: exception handling is not inherently bad, or slow. Exceptions on consoles suck because they use shitty 10-12 year old compilers. Exceptions under Win32 are slow if you don't use the Microsoft compiler because SEH Exceptions for 32-bit Windows are patented (so the other compilers default to using setjump/longjump which is insane).

Exception handling under Win64 is (almost) zero overhead execution-time wise (not binary size wise, unluckily) unless you actually throw. Go ahead and time it, if calling a non-optimized-out small function some million times takes, say, 4.53 +/- 0.01 seconds, then calling the same function with exception handling will take 4.53+/- 0.01 seconds. Only if you actually throw, things look different. But throwing is an exceptional thing, so that is alright.

More important than the fact that exceptions are slow (they are not) is the fact that it is impossible to predict how long the handling will take. It can be anywhere from a dozen cycles to milliseconds (if, for example, a destructor flushes and closes files). Depending on the situation, and in particular depending on your realtime-requirements, this uncertainity may be the one thing that makes them forbidding.

This topic is closed to new replies.

Advertisement