Jump to content

  • Log In with Google      Sign In   
  • Create Account


C++ - do you use STL in your game?


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
33 replies to this topic

#1 codeToad   Members   -  Reputation: 142

Like
0Likes
Like

Posted 13 May 2012 - 05:09 PM

I heard that the STL is too slow for use in a high-performance game. I've been told that if you want to use the same coding techniques as a AAA game, you have to make custom classes for all your linked lists, trees, etc., rather than just using LinkedList<MyClass> (or whatever the STL syntax is).

Is this true?

My game will be so simple that the performance will not really matter. I'm doing this for the programming experience.

Sponsor:

#2 japro   Members   -  Reputation: 887

Like
4Likes
Like

Posted 13 May 2012 - 06:14 PM

Claims that STL is "too slow" are utter nonsense. Especially std::vector is identical in performance to a "raw dynamic array". You will have a REALLY hard time writing faster containers than the ones in good STL implementations. Now there might be the case that your data has a special structure that you can abuse, in that case you can think about rolling your own.

#3 Servant of the Lord   Crossbones+   -  Reputation: 17144

Like
6Likes
Like

Posted 13 May 2012 - 06:16 PM

I use the STL in my (2D RPG) game. The only performance critical part of my code is streaming the map from file around the player; I didn't find the standard library to be any slowdown, at least, not more than my own code was slowing things down. The real critical issue of speed that I had come up against was all the new-ing and delete-ing calls, so I found the greatest optimization for me was to re-use the dynamic memory in the really performance intensive areas. But everywhere else I don't worry about either dynamic memory or the STL.

Another issue was from me using boost::shared_ptr() stupidly... In general, the greatest slowdowns I encounter are from me mis-using libraries, not from the libraries themselves being slow. For example, if you are push_back()ing 400 elements in a vector without first calling reserve(), you are doing it wrong. Posted Image
The standard library is better optimized than I can do on my own. My occasional poor usage of the standard library is what causes slowdowns.

Just use the library intelligently, and don't prematurely optimize, and you'll be fine. When you do encounter a slowdown, profile, find out where the bottleneck is, and optimize that area. If, in that situation, you find a STL class to be the bottleneck (unlikely, but possible), then in that one area of code roll something faster, but continue to use the standard library everywhere else.

(FWIW, I'm a hobbyist trying to go independant working on 2D games, not a industry veteran working on the latest triple-A pixel-fest FPS)

It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.

[Fly with me on Twitter] [Google+] [My broken website]

All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.                                                                                                                                                       [Need free cloud storage? I personally like DropBox]

Of Stranger Flames - [indie turn-based rpg set in a para-historical French colony] | Indie RPG development journal


#4 jbadams   Senior Staff   -  Reputation: 17247

Like
12Likes
Like

Posted 13 May 2012 - 06:23 PM

Just to get a nit-pick out of the way first, the library you're talking about is not the STL, it's the C++ Standard Library -- although the former term is so often used as to make them almost interchangeable, and you'll likely never run into a situation where it leads to confusion.


As to your actual question, my recommendation would be that implementing your own containers and algorithms can be a great learning experience, and one that every programmer would benefit from tackling at some point, but that you should then go right back to using the well-tested and very capable versions provided by the standard library in your actual code.



Yes, it is true that the standard library (or parts thereof) are not always used in professional development. This is due to a number of reasons, and some of the valid ones include lack of a good implementation (or any implementation) for a particular target platform and performance concerns. Some of the invalid ones include NIH syndrome, inexperienced programmers, or acting on rumoured poor performance without actually testing the suitability for their own usage. Writing and using your own versions when those good reasons don't apply to you likely won't provide a particularly valuable experience however.

Some other reasons I recommend using the standard library:
  • It's standard, and therefore (if used appropriately) clearly communicates the intentions and usage of your code to other programmers. Other programmers will be experienced with these containers and be able to follow your code without having to examine the implementations of your containers or algorithms.
  • The performance concerns are often over-stated, and unless you know they actually apply to your situation and have the experience to write a good alternative you probably won't get any benefit. Using the standard library extensively may also allow you to get a good grip on what limitations do actually exist.
  • Some studios do use the standard library, and it's also valuable in non-games programming.
  • Some studios will improve the standard library's performance (by providing custom allocators, etc.) rather than rolling their own version, in which case your experience would remain relevant.
  • Making use of the standard library will more easily allow you to finish your own bug-free and acceptably performant projects in a reasonably amount of time -- would you prefer a portfolio with a number of polished and bug-free games, or with a lesser number of potentially buggy games that use your own implementations?
  • Some studios will use their own implementations, such as EASTL for example. Given these libraries are quite often intended as replacements for the standard library, experience with using the standard library will give you an excellent starting point for being able to quickly learn the proper usage of these alternatives if necessary.

TL;DR:
Write your own versions as an educational experience, but use the standard library in your actual projects.


Hope that's helpful! Posted Image

#5 codeToad   Members   -  Reputation: 142

Like
1Likes
Like

Posted 13 May 2012 - 06:58 PM

Thanks, guys. I got some experience writing my own linked lists and trees in the past, so I think I'm going to go with the collections provided by the C++ standard library.

#6 Cornstalks   Crossbones+   -  Reputation: 6966

Like
4Likes
Like

Posted 13 May 2012 - 07:34 PM

One reason AAA games may not use the C++ Standard Library is because of poor compiler support for certain standard language features, such as exceptions or templates. Some compilers for some consoles don't support these things too well, and so an alternate will be developed that doesn't use (or avoids using too much) exceptions or templates to work around the compiler's lacking implementation.

Another reason might be because the developers are targeting a specific operating system, and that operating system has some custom functions that allow the developers do something very specific (like memory-mapping files, for example). The developers may rewrite parts of the C++ Standard Library to take advantage of such specific functionality that's not exposed in the C++ Standard Library.

I'm just giving a couple of examples for when one might be justified in rewriting parts of the C++ Standard Library. If you're rewriting it (or parts of it), you need to have a very specific goal in mind and a very specific problem you're solving/working around. A naive rewrite is worse than stupid (unless doing it solely for educational purposes).

I haven't worked on a AAA game, but I've worked on some industrial-level software that sells for several thousands of dollars, and we use the C++ Standard Library (I haven't dug into the speed critical parts, but I think those are mostly done in assembly or SSE instructions where data structures aren't the bottleneck and the C++ Standard Library doesn't have algorithms for what we're doing). Use it.

I think jbadams summed up my opinions and views very well though.
[ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]

#7 themean   Members   -  Reputation: 859

Like
0Likes
Like

Posted 14 May 2012 - 01:35 AM

The big problem of stl is such that his implementation on different compilers are not the same, but this don't must stop you to using him
Boost containers are alternative of stl
You aways can typedef the container
typedef std::vector<Unit> UnitArray;
And if you have performance problem you aways can change typedef with your own container
typedef MyVector<Unit> UnitArray;
The only requirement is such that your container must have all methods of stl container equivalent

Edited by themean, 14 May 2012 - 01:37 AM.


#8 Zoomulator   Members   -  Reputation: 269

Like
0Likes
Like

Posted 14 May 2012 - 03:48 AM

I agree with jbadams post with a few additions.

Two things to keep in mind.

First, there's a few people out there who aren't aware of the iterator debugging in visual studio's implementation of the STL (standard template library) containers. This causes a major slowdown in debug builds, and release code are often tens of times faster without it! Declare NDEBUG, or some _NO_ITERATOR_DEBUG (I think it was) to get rid of it. You can find the details on msdn if need be. It's a good safety net though that can really catch nasty bugs.

Second of all: all STL and standard library features are generalized to allow for a one-fits-all solution. This is generally a good thing, and they've implemented it in such a great way so that you can even assign your own memory allocators to avoid that potential slowdown too, which was mentioned before. Having them take memory from a pre-allocated pool greatly improves performance for the linked containers.

..But, generalized is always generalized, which means that you can't optimize it for your particular case and may run a bit slower because of it. You'd still have to be a damn nifty coder to make it better and as stable as the STD library.

Saying that, STL containers is my first choice, always. Most reasons stated by jbadams already.

#9 rip-off   Moderators   -  Reputation: 7660

Like
2Likes
Like

Posted 14 May 2012 - 05:50 AM

I heard that the STL is too slow for use in a high-performance game.

Any performance issues that AAA companies find with the Standard C++ Library is that it's general nature doesn't allow them to do certain things. For instance, there isn't a way to tell std::vector<> to use a given piece of memory easily. The "allocator" parameter for standard library templates are a little underbaked and often don't give enough control to the client programmer.

Thus, the problems are of a design nature, not the actual implementation. The implementation of the standard containers tends to be very high quality. Most programmers are not going to beat the performance of classes like std::vector if they stick to the interface it outlines. In fact, many underexperienced programmers are likely to produce code that is algorithmically poorer, and thus will suffer heavily in real use.

I've been told that if you want to use the same coding techniques as a AAA game, you have to make custom classes...

It makes no sense to apply the "coding techniques" of a massive, professional team to your small personal projects.

... your linked lists, trees, etc., rather than just using LinkedList<MyClass> (or whatever the STL syntax is).

If you're worried about performance, you should strongly consider not using linked lists at all. They are almost always going to be slower than dynamic arrays on modern machines with real data.

I'm doing this for the programming experience.


I believe there is great value in implementing the standard containers yourself, as a learning exercise, in particular if you have a reliable source to get some feedback from. However, I would not recommend using such an implementation for a real project.

#10 l0calh05t   Members   -  Reputation: 644

Like
1Likes
Like

Posted 14 May 2012 - 06:40 AM

If you're worried about performance, you should strongly consider not using linked lists at all. They are almost always going to be slower than dynamic arrays on modern machines with real data.


Well, that would depend on how it is used. If you need to insert or remove items somewhere in the middle instead of just at the end (vector) or at the beginning or the end (deque) a linked list it practically guaranteed to be faster. Use the proper container for a given algorithm, taking into account the algorithmic performance (in this case O(1) vs O(n) insertion).

#11 turch   Members   -  Reputation: 590

Like
0Likes
Like

Posted 14 May 2012 - 07:16 AM

I think a lot of flak that the standard library gets comes from misuse, because most classes offer functionality they are completely unsuited for (such as insert in vector). Programmers throw them around willy-nilly without thinking about which structure is best for the intended usage. This leads to lists being used for memory that needs to be contiguous, which leads to programmers claiming that the stantard library is slow.

Edited by turch, 14 May 2012 - 07:17 AM.


#12 rip-off   Moderators   -  Reputation: 7660

Like
4Likes
Like

Posted 14 May 2012 - 07:27 AM

If you need to insert or remove items somewhere in the middle instead of just at the end (vector) or at the beginning or the end (deque) a linked list it practically guaranteed to be faster.


It depends. If the order is irrelevant, "swap and pop" is very fast.

Use the proper container for a given algorithm, taking into account the algorithmic performance (in this case O(1) vs O(n) insertion).


The cache performance, not the algorithmic performance, dominates modern hardware. Jumping around memory using a linked list is worst case behaviour from the cache's perspective. Algorithmic analysis assumes that accessing memory costs the same regardless of how it is done.

In most real programs, insertions and deletions are "rare" and iteration is common. Speeding up insertion and deletion, while making iteration slower, will often have a much larger cost in the long run. When the data set is small, the performance implications of copying it all around is negligable. When the data set gets large, cache tends to dominate.

This blog post quotes a slide from Bjarne Stroustroup illustrating some of these ideas. Unfortunately the code isn't included and I have no time to search for it now.

Again, this is a general rule but not absolute. I used the qualifying terms "real program" and "almost always" for a reason. There are sweet spots where linked lists make more sense. In particular, linked lists have a (more or less) stable running speeds, while vector only has amortised running speed, with potentially large spikes around reallocations and deletions. Though linked lists aren't immune to spikes. Carefully loaded linked lists might behave nicely with the cache, but the "wrong" set of insertions and deletions might radically change their performance profile.

Edited by rip-off, 14 May 2012 - 07:32 AM.


#13 l0calh05t   Members   -  Reputation: 644

Like
1Likes
Like

Posted 14 May 2012 - 07:35 AM

It depends. If the order is irrelevant, "swap and pop" is very fast.


If it is irrelevant, yes.

The cache performance, not the algorithmic performance, dominates modern hardware. Jumping around memory using a linked list is worst case behaviour from the cache's perspective. Algorithmic analysis assumes that accessing memory costs the same regardless of how it is done.


True up to some constant factor. There is always a break even point at which the algorithmically better container will be faster. Of course that point might be way beyond what you'll ever need, heck, even bubble sorts can be useful for small, almost-sorted lists/vectors. In the end, profiling is the only way to find out what works best, and in that case the STL is great because due to the consistent interfaces it is easy to swap container types.

#14 Hodgman   Moderators   -  Reputation: 27668

Like
3Likes
Like

Posted 14 May 2012 - 07:50 AM

The cache performance, not the algorithmic performance, dominates modern hardware. Jumping around memory using a linked list is worst case behaviour from the cache's perspective. Algorithmic analysis assumes that accessing memory costs the same regardless of how it is done.

QFE. This is why designing your data-structures to fit into compact blobs of bytes is so important.
If you come up with compact "file-formats for memory", which can use linked-lists or offsets or arrays internally, like below, then you'll actually be making proper use of your CPU. The best algorithmic structure is secondary and must be designed to fit the primary constraint of the working-set size.
char* data = (char*)malloc(640*1024);//640KiB should be enough for anybody
vector<Foo,42>& fooVec = *new(data) vector<Foo,42>();
data += sizeof(fooVec);

The above is the reason game engine dev's might avoid STL containers - because the STL enforces the interface of random new/delete calls on your memory allocator. You can supply custom allocators to the STL, but they have to follow it's interface.
In contrast, many game-systems use allocation techniques such as stacks and scopes, which have a completely different interface and logical rules for allocating/deallocating. It's only in this situation, where you can't make std::vector use your game's allocator that you should write your own (which is probably much simpler and less featureful).

N.B. if I was writing a game where systems were already using new/delete, then using std::vector/etc would be the obvious (standard) choice. It's only when working in an environment with a completely different memory allocation strategy that you need to abandon the standard containers.

Edited by Hodgman, 14 May 2012 - 07:54 AM.


#15 mdwh   Members   -  Reputation: 822

Like
0Likes
Like

Posted 14 May 2012 - 08:00 AM

The cache performance, not the algorithmic performance, dominates modern hardware. Jumping around memory using a linked list is worst case behaviour from the cache's perspective. Algorithmic analysis assumes that accessing memory costs the same regardless of how it is done.

In most real programs, insertions and deletions are "rare" and iteration is common. Speeding up insertion and deletion, while making iteration slower, will often have a much larger cost in the long run. When the data set is small, the performance implications of copying it all around is negligable. When the data set gets large, cache tends to dominate.

I don't think anyone would disagree that vectors would be used more often, but the argument is that there are still real world cases where linked lists are better for performance. In some cases, order is important, for example.

This blog post quotes a slide from Bjarne Stroustroup illustrating some of these ideas. Unfortunately the code isn't included and I have no time to search for it now.

But this is a case that also includes the time to search linearly for the insertion point. It also only shows a single insertion/deletion AFAICT - what about an algorithm with a large number of insertions?

Again, this is a general rule but not absolute. I used the qualifying terms "real program" and "almost always" for a reason. There are sweet spots where linked lists make more sense.

Yes, I think this is the point Posted Image Your original posts said one should not use lists at all, not that vectors are usually better.

(Also there are cases where neither make a difference for performance, but using a list fits better with writing readable code due to the nature of the performance, so using a vector in some belief that it'll be faster is just premature optimisation.)

Edited by mdwh, 14 May 2012 - 08:01 AM.

http://erebusrpg.sourceforge.net/ - Erebus, Open Source RPG for Windows/Linux/Android
http://homepage.ntlworld.com/mark.harman/conquests.html - Conquests, Open Source Civ-like Game for Windows/Linux

#16 l0calh05t   Members   -  Reputation: 644

Like
0Likes
Like

Posted 14 May 2012 - 08:08 AM

The above is the reason game engine dev's might avoid STL containers - because the STL enforces the interface of random new/delete calls on your memory allocator. You can supply custom allocators to the STL, but they have to follow it's interface.


The interface allows for enough flexibility in most cases. E.g. pooled allocators etc. should all be possible.

Since we are already on the topic of cache coherence: Instead of arrays of compact structs, structs of arrays can be even better. And for optimal performance you may need arrays of structures of arrays... caches are a bitch ;)

#17 Hodgman   Moderators   -  Reputation: 27668

Like
0Likes
Like

Posted 14 May 2012 - 08:18 AM

The above is the reason game engine dev's might avoid STL containers - because the STL enforces the interface of random new/delete calls on your memory allocator. You can supply custom allocators to the STL, but they have to follow it's interface.

The interface allows for enough flexibility in most cases. E.g. pooled allocators etc. should all be possible.

In most cases for whom? I do actually agree with that sentence, for most people, but when talking about high-performance game engines, "general purpose" allocation (i.e. new/delete) is entirely inadequate for a lot of purposes. For starters it's a singleton, which is asking for trouble...
If it were adequate, you wouldn't have game dev's avoiding the STL because it doesn't work with their stack/scope/ring allocators linked to above.

Since we are already on the topic of cache coherence: Instead of arrays of compact structs, structs of arrays can be even better. And for optimal performance you may need arrays of structures of arrays... caches are a bitch ;)

And lists and maps and trees too... as long as they're designed to fit in the constraints.

Edited by Hodgman, 14 May 2012 - 08:48 AM.


#18 Antheus   Members   -  Reputation: 2393

Like
0Likes
Like

Posted 14 May 2012 - 08:32 AM

The interface allows for enough flexibility in most cases. E.g. pooled allocators etc. should all be possible.


STL doesn't allow for pooled allocators due to underspecified allocator semantics. It works for specific implementations and code will be fully standard-compliant, but will mysteriously break on some compilers.

STL basically says: valid implementation may be stateful or stateless.


Linked lists in practice also suffer from another problem - search. There are two cases:
- iteration/additions/removal dominate - GC-collected vector wins
- search dominates - map/hashmap wins

Even for things like browser DOM or UI in general, one will frequently search for a group of objects fitting a criteria, requiring full sweep over entire structure with multiple results or operations.

Linked lists make sense only when the cost of copying is disproportionate to other operations or where reallocation cannot be performed at all, such as disk-based data or things like URL. In all other cases they lose even at algorithmic level. And even in these cases, bulk operations are often used (b-tree or other form of pre-computed index, sitemaps for web sites).

#19 rip-off   Moderators   -  Reputation: 7660

Like
0Likes
Like

Posted 14 May 2012 - 08:38 AM

Your original posts said one should not use lists at all, not that vectors are usually better.


Please quote where I said that.

#20 L. Spiro   Crossbones+   -  Reputation: 12240

Like
0Likes
Like

Posted 14 May 2012 - 09:28 AM

The cache performance, not the algorithmic performance, dominates modern hardware. Jumping around memory using a linked list is worst case behaviour from the cache's perspective. Algorithmic analysis assumes that accessing memory costs the same regardless of how it is done.

QFE. This is why designing your data-structures to fit into compact blobs of bytes is so important.
If you come up with compact "file-formats for memory", which can use linked-lists or offsets or arrays internally, like below, then you'll actually be making proper use of your CPU. The best algorithmic structure is secondary and must be designed to fit the primary constraint of the working-set size.
char* data = (char*)malloc(640*1024);//640KiB should be enough for anybody
vector<Foo,42>& fooVec = *new(data) vector<Foo,42>();
data += sizeof(fooVec);

The above is the reason game engine dev's might avoid STL containers - because the STL enforces the interface of random new/delete calls on your memory allocator. You can supply custom allocators to the STL, but they have to follow it's interface.
In contrast, many game-systems use allocation techniques such as stacks and scopes, which have a completely different interface and logical rules for allocating/deallocating. It's only in this situation, where you can't make std::vector use your game's allocator that you should write your own (which is probably much simpler and less featureful).

N.B. if I was writing a game where systems were already using new/delete, then using std::vector/etc would be the obvious (standard) choice. It's only when working in an environment with a completely different memory allocation strategy that you need to abandon the standard containers.

Glad you mentioned it before I had to be the party pooper.

In contrast with many who replied above me, I wouldn’t touch STL with a 3-inch pole.
EASTL: Because Electronic Arts may make crap games, but they still know what they are doing.
Real games crave certain allocation disciplines. I even outlined stack allocators in my most recent tutorial. These things improve my performance by factors you can’t even believe, and this is typical of game development. Games need this kind of performance.

But it is not just that. While some claim that iterators are not slower than the alternatives, the reality is that this is largely compiler-dependent. Some (most) compilers always perform range checks on array access unless a specific macro is defined. While that macro (NDEBUG) is easy to define, it still doesn’t actually mean that all of your STL components will stop using that array-access check. Some might still be checking array access even with that defined.

The reason so many deviate from STL is:
#1: Everything mentioned by EASTL.
#2: Even if you think you are in control, you are not. You have no idea what is happening under the wheels. Iterations could be as fast as array access on one machine, but 3 times as long on another machine.

Making STL classes that are as fast as those provided by the most optimal STL vendors? I honestly have to wonder if that is so hard. I had no problems making faster implementations (one for POD, one for objects), and there does not seem to be any factor that outweighs overall usage efficiency. That is, by knowing how I intend to use a certain object, one efficiency major can be used over another to guarantee optimal use of custom vectors (etc.)

Simply put, I would not touch STL with a 3-inch pole.


L. Spiro

Edited by L. Spiro, 15 May 2012 - 05:13 AM.

It is amazing how often people try to be unique, and yet they are always trying to make others be like them. - L. Spiro 2011
I spent most of my life learning the courage it takes to go out and get what I want. Now that I have it, I am not sure exactly what it is that I want. - L. Spiro 2013
I went to my local Subway once to find some guy yelling at the staff. When someone finally came to take my order and asked, “May I help you?”, I replied, “Yeah, I’ll have one asshole to go.”
L. Spiro Engine: http://lspiroengine.com
L. Spiro Engine Forums: http://lspiroengine.com/forums




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS