2
\$\begingroup\$

I have overhead, several times, that templates should not be used on video game consoles; however, I never heard why, or understood why. Why shouldn't templates be used on video game consoles, and if they are to be used, what are some ways to avoid the negativity?

\$\endgroup\$

4 Answers 4

8
\$\begingroup\$

There's nothing really specific to consoles that make templates uniquely a bad choice on them. They can certainly be used as a force for good, in appropriate situations. And different people have different reasons to disparage templates. Here are some of the reasons that come to my mind:

Templates can bloat compile times and executable sizes. When you include a standard library header, under traditional C++ compilation models every single file that needs to include that header will end up parsing and instantiating all the templates in it. And that instantiated copy of the template is stored in every object file that uses it. This is less of a big deal in modern times, thanks to our tools—precompiled headers can speed up compilation for massive headers, and linkers can remove duplicate instantiations. Modern compilers can even take separate instantiations that have identical code (vector<A*> and vector<B*>, for instance) and merge them. But templates can still bloat executable sizes, and can lead to absurdly long compilation times. (This may be one of the cases that is more specific to consoles: the compilers used on consoles can often be less mature than the ones used for PC development.)

Templates are often overused, and lead to overgeneralized code. So you're thinking of writing a vector class. What design choices are you going to make? Well… we're going to need at least 2-vectors, 3-vectors, and 4-vectors. Mostly float vectors, but we'll probably use a few int vectors, and I can't say for sure that we won't need doubles in some cases. So let's make this a vector<T, Size>. Now how about matrices? Well, that's easy: matrix< vector<float, 4>, 4 >. Now one of the most fundamental types in your engine is one of the most complex. Sure, you've got a lot of "power" behind it, but if 99% of the code uses 4x4 float matrices and 4-vectors of floats, you've actually come out behind, because you don't have simple obvious code for the primary case. And if you want to make your vector<float, 4> use SIMD intrinsics, for example, suddenly you have to partially specialize, and write a full 4-vector of floats anyway.

Templates make your error messages incomprehensible. Both compilation errors and linker errors become orders of magnitude more fun to sort through when templates are involved. As a bonus, templates needing to be in headers (sans explicit instantiation) means that any change to a template means that every piece of code that uses that template needs to be recompiled when it changes. Sure, you can compile only one file that uses the template to speed up your compile-test-fix cycle, but you can't be sure that you've caught all the errors with only one instantiation of the template, since many template errors are deferred to the point where they're instantiated.

Templates purport to be future-proof, but rarely are. Properly designing a template to be future-proof is extremely difficult, and by some metrics impossible without being prescient. The standard library containers and algorithms are very carefully designed and are a cut above most templates, but even the standard library is discouraged in most console game engines. Standard library containers have immensely complicated implementations, don't support alignment well, use exceptions everywhere, are difficult to get to work with external allocators, and are difficult to debug. And they're working in a problem space that is usually cited as a good reason to use templates (container types).

Templated code is complicated. Doing the "right thing" when making a template leads to extremely complicated code, as anyone who has browsed a C++ standard library implementation knows. And complicated code is harder to understand, harder to optimize, harder to change, and atrophies more quickly. Templates are huge part of the language with very, very weird corner cases (pop quiz: what's strange about ADL with template functions with explicitly provided template arguments?). Simplicity is huge.

I've probably missed some and overstated others. There are good reasons to use templates, even in console games, but they are frequently (and for good reason) approached with an attitude that starts with suspicion and may eventually work around to grudging acceptance if good reason is provided. But the average console game simply isn't generic enough for templates to be a necessity (console games typically aren't libraries of code, for instance, expected to be used out-of-house), and there are very good reasons to avoid them.

\$\endgroup\$
4
  • 7
    \$\begingroup\$ Immature compilers are the worst thing to have to deal with. However, some of your points are just plain over-engineering. There's nothing specific to templates about your matrix example- it could be equally stated in terms of anything else. Stating that the Standard lib is discouraged as if it's some kind of argument is meaningless, too. Plenty of the downsides you've listed are quite specific to them and not any other template code. Fundamentally, I don't feel that your arguments contain (much) that's true of templates in general, instead of some suboptimal of them. \$\endgroup\$
    – DeadMG
    Commented Apr 1, 2012 at 9:11
  • \$\begingroup\$ This post is intended to be a recitation of the reasons I've heard for templates not being used. It's of course entirely possible to avoid code/compilation bloat, to use templates in exactly the right cases, to have a compiler with reasonable error messages, to make templates that are exactly future-proof enough, and to make simple template code. They're a popular feature on a popular language, they aren't entirely worthless. But they're a large hammer, and once you know how to use them, everything looks like a nail. \$\endgroup\$ Commented Apr 1, 2012 at 16:27
  • 1
    \$\begingroup\$ Powerful abstraction tools tend to apply to most problem sets :P But there are definitely limits and applying them where they're not appropriate is certainly one of those limitations. \$\endgroup\$
    – DeadMG
    Commented Apr 1, 2012 at 21:27
  • \$\begingroup\$ Abstracting over data is an incredibly powerful tool that applies to most problem sets. Abstracting over type is less so, especially in concrete problems. Templates are fundamentally a tool for simplifying code, so the key litmus test for them is simply to check that the process of writing code gets simpler when templates are used. \$\endgroup\$ Commented Apr 2, 2012 at 5:18
5
\$\begingroup\$

The answer to your question is one thing, but I think you probably meant something slightly different, so I'll answer both.

Using Templates

There is absolutely no reason to avoid templates on a console any more than anywhere else.

Templates by definition only really affect compile time, not runtime. There is an opportunity for templates to be abused in such a way as to result in bloated machine code, as John Calsbeek's answer describes in some detail. However, nothing is console specific about any of the potential problems with templates.

To put it bluntly, your choice to use a template or not should be 100% based on whether a template is the correct way to solve the design problem you're working on. The target platform is entirely irrelevant. There are numerous AAA engines targeting consoles that make heavy use of templates, in fact.

The problems that are solved well by templates are any pieces of code you write that need to be generic to types. Containers are an excellent place to use templates. Utility code that applies to many types -- algorithms, smart pointers/handles, and so on -- are excellent places to use templates. Even some template meta-programming can come in massively useful for handling script binding, serialization, editor GUI property sheet binding, messaging, and even network packet formation (done right, these techniques will have zero or very near-zero runtime overhead and again, being on a console or not will just not matter).

Standard Template Library

The C++ STL is a somewhat different matter to templates in general. What you probably heard was that the STL should be avoided on consoles. In fact, even many PC games avoid much of the STL as well. While the STL is a very good library for general purpose programming, it has various weaknesses for games.

The STL containers are often poor fits for games as they're designed to be rather broadly applicable rather than targeted at specific needs. The std::vector container and maybe the std::deque are acceptable as-is, but the majority of the other containers are problematic.

Many implementations of std::map for instance are basic binary trees, and your worst case times with them hence don't suit real-time applications like games. You're usually better off writing your own red-black tree container since that way you can know for sure you're getting a data structure with an acceptable worst-case lookup/insertion/removal time.

The worst offender of the containers is the std::unordered_map (hash table), whose implementation is defined by the standard to have various properties which are not really all that useful in most real code I've seen (including all game code I've seen). Those properties basically mandate a suboptimal implementation. In particular, std::unordered_map is required to not invalidate references to objects on insert/removal, which means that the map cannot use open-addressing, and hence is going to have sloppy memory usage. The implementation is designed under the assumption that you might be storing expensive to move/copy objects, but in games you'll very rarely be storing anything besides numbers or cheap pointers/handles in these kinds of data structures.

Algorithms like std::sort are implemented in ways that work well in general cases, but which again have abysmal worst-case performance, and honestly just unacceptable average-case performance for the kinds of things that games sort. Most games will only very rarely sort strings or such, but will sort plain integers very often. Hence using a radix sort algorithm is quite likely going to be a much better choice.

The normal STL implementations also tend to make Debug builds abysmally slow due to the abstract nature of their implementation and the excessive debugging checks. EA has an in-house STL implementation that goes out of its way to avoid every unnecessary method call in its internal implementation, so that even in non-inlined debug builds the containers will remain at least reasonably fast. They submitted a paper to the C++ committe on the topic (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2271.html) and apparently published it to GitHub (https://github.com/paulhodge/EASTL).

The allocator model of the STL is also a pain (for games or any other application), and games in general want to have very explicit control over memory. Doubly so for consoles. While this can be worked around with the STL allocator model, it's so painful and obtuse and difficult to use correctly that most games end up using custom container libraries that have non-STL-compliant (but vastly superior for their needs) allocator APIs.

So, it may be desirable to avoid using any normal STL implementation for console games. Using a template library that is written with games, real-time simulation, and constrained platforms' needs in mind is a very different proposition, however.

\$\endgroup\$
9
  • 2
    \$\begingroup\$ "There is absolutely no reason to avoid templates on a console any more than anywhere else." Consoles tie you to specific old compilers that may (or rather, do) have template-related bugs and misfeatures you can't fix. \$\endgroup\$
    – user744
    Commented Apr 1, 2012 at 14:53
  • \$\begingroup\$ When you talk about std::sort, are you talking about a standard-mandated suboptimal implementation, or about a specific implementation, say in msvcrt or libstdc++? \$\endgroup\$ Commented Apr 1, 2012 at 16:37
  • \$\begingroup\$ Also, note that the part of EASTL that is public is a limited chunk (50%-60%). \$\endgroup\$ Commented Apr 1, 2012 at 16:42
  • \$\begingroup\$ @JohnCalsbeek: What "standard-mandated suboptimal implementation" are you talking about? Where are you getting this stuff from? Do you have links to something that says that the C++ standard mandates a "suboptimal implementation" of std::sort? \$\endgroup\$ Commented Apr 2, 2012 at 4:54
  • \$\begingroup\$ @NicolBolas No, sorry, I've heard absolutely nothing of the sort. I was asking the question because the claim that "algorithms like std::sort are implemented that work well in general cases, but … have abysmal worst-case performance" seems very strange to make as a general claim, and I wanted to check to see if the standard mandated something that meant the worst-case performance would be worse than usual. \$\endgroup\$ Commented Apr 2, 2012 at 5:11
2
\$\begingroup\$

John mentioned some interesting points but I'd like to clarify some points as I feel some points are biased.

Compile time

I agree that templates can increase compile times. Sometimes it becomes bad enough that you have to take some measures to keep the compile times down (inherit from base code [in a private namespace] that uses void* for data types which the derived template classes then cast to the appropriate type).

You can also force the template to instantiate. This is really useful if the template code is in a library, because then you can instantiate the most common types when compiling the library (which is compiled once) which are ready to use when using said code. Another benefit is that you can now have template code in your source files and not the headers (assuming you know all the types that the code is going to work with... this is usually true with game code as we almost always know what we are working with... it is a closed system). This is great when you know about the types but still want genericity because otherwise you may end up using costly runtime techniques.

As a bonus, templates needing to be in headers

I disagree with this. For one, the above template instantiation will negate that. Another technique is to split up your functions just like you with a header and source file but instead name the source file inl. Commonly, the inl file is included in the header which, after much experience with long compile times and recompile after every small change, I believe is bad practice for all but trivial template classes. What you should do is include the header in the inl file, so it looks exactly like a cpp file, and then include the inl file in your source files. This will reduce the compile times drastically.

Overuse

Templates, like any other technique can be overused. You get a hammer, everything becomes a nail. Although I don't see anything particularly wrong with the Matrix code John mentions.

Error Messages

I agree with John here, that the error messages become hard to read. I also think that that should not be the reason for you to not use templates where they make sense. The error messages from the standard containers are very hard to decipher because of the absolutely awful names that they have chosen. For proof, try to follow the standard vector header and then try to read EASTL's vector header. There is a world of a difference. Next, try EASTL in your code and force an error. The errors no longer seem as daunting as they usually are.

Standard containers

Don't use them (use EASTL instead). Most of the mentioned problems with containers disappear just by doing this. The common reason for not using standard containers is that games generally do not use exception safety and then there is alignment, both of which are solved by using EASTL.

Templated code is complicated

Yes it is. Then again, so is C++ to a programmer who just started using it. There are some corner cases, and some weird areas (not so weird when you understand why it is like that - e.g. when to use typename) but that is any language. C++ template meta-programming is a compile time turing complete language (sublanguage). It is extremely powerful and thus requires quite a bit of learning to do it right.

Static Polymorphism

You can avoid polymoprhism and its runtime disadvantages (memory usage, cache misses by extra indirections) by using templates and the curiously recurring template pattern.

Conclusion

Many will recommend to use templates when they make sense. That is a vague statement. How do you know when it makes sense? Only when you practice it will you start learning when it makes sense and when you went overboard.

Any relatively complex feature of a language goes through the same process. How do you know when to use composition over inheritance or vice versa? There are some rules but mostly it is experience. If you don't use it, you won't gain experience.

My suggestion is that if you are a younger developer (not age wise, but experience wise) then don't shy away from using techniques just because of the bad stigma attached to them. You will not learn how to use the techniques properly down the road, which is very important.

I will give you one last example about multiple inheritance and the diamond problem. I never used multiple inheritance because I was told that there is almost always an alternative and multiple inheritance has quite a few complex issues that result is bad code. Were they right? Yes! But should I have never used them? No! Because once I used it, I ran into issues that made me understand the language even more and the technique even more. Multiple inheritance has its uses (especially in meta-programming) and now I know where to use them.

\$\endgroup\$
0
1
\$\begingroup\$

While I have not heard the 'do not use templates on a console', I can maybe give a personal experience as to why people may say this.

Working on a very memory limited console, you of course want the compiler to strip out unused code to compact the exe as small as possible. This actually gave us issues on one platform where the compiler would improperly strip out (or just never compile to begin with) pieces of the templates used for the objects. It did not naturally see that those methods were needed so when the run time would execute, it would crash.

We fixed the issue, but this did cause us a bit of code bloat as we had to forcibly declare the template classes. This added a bit more to the code but it ensured that the code we did need would still be there but allow us to still run code stripping as a compilation option.

Hope this helps.

\$\endgroup\$
1
  • \$\begingroup\$ Interesting point, did not run into this issue myself, but will keep this in mind +1 \$\endgroup\$
    – Samaursa
    Commented Apr 1, 2012 at 15:29

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .