tags:

views:

1197

answers:

6

Which STL container would fit my needs best? I basically have a 10 elements wide container in which I continually push_back new elements while pop_front ing the oldest element (about a million time).

I am currently using a std::deque for the task but was wondering if a std::list would be more efficient since I wouldn't need to reallocate itself (or maybe I'm mistaking a std::deque for a std::vector?). Or is there an even more efficient container for my need?

P.S. I don't need random access

+2  A: 

A queue is probably a simpler interface than a deque but for such a small list, the difference in performance is probably negligible.

Same goes for list. It's just down to a choice of what API you want.

lavinio
But I was wondering if the constant push_back were making the queue or deque reallocate themselves
Gab Royer
std::queue is a wrapper around another container, so a queue wrapping a deque would be less efficient than a raw deque.
John Millikin
For 10 items, performance is most likely going to be such a tiny issue, that "efficiency" might better be measured in programmer-time than code-time. And the calls from queue to deque by any decent compiler optimizations would be down to nothing.
lavinio
@John: I'd like you to show me a set of benchmarks demonstrating such a performance difference. It is no less efficient than a raw deque. C++ compilers inline very aggressively.
jalf
KTC
+15  A: 

Check out std::queue. It wraps an underlying container type, and the default container is std::deque.

Mark Ransom
I doubt wrapping a deque with a queue will have better performance characteristics than just using a deque.
John Millikin
Do you think it'll have worse, then? The point is that a queue is the FIFO structure to use in C++. It provides the correct interface, and let's you choose which container it adapts to, so if list ends up being better, it's as simple as saying "use a list", and no other code changes. [http://www.cplusplus.com/reference/stl/queue/]
GMan
The point I meant to make with my first question was: you don't know. You can guess, but it's unlikely you'll see any performance difference. Inlining will remove the "shell", so it's *like* using a `list`, `deque` or whatever, performance-wise, but there is in fact that "shell" making sure you use the structure correctly.
GMan
std::queue is merely a thin wrapper around an underlying data type, which hides some unneeded operations. It's useful in some circumstances, but if performance is a priority then every extra layer should be eliminated.
John Millikin
Every extra layer *will* be eliminated by the compiler. By your logic, we should all just program in assembly, since the language is just a shell that gets in the way. The point is to use the correct type for the job. And `queue` is that type. Good code first, performance later. Hell, most performance comes out of using good code in the first place.
GMan
And to add on to that, a `deque` will probably have better cache performance, because those 10 items will probably fit into one allocation.
GMan
Sorry to be vague - my point was that a queue is exactly what the question was asking for, and the C++ designers thought deque was a good underlying container for this use case.
Mark Ransom
"Good code first, performance later" -- obviously, the OP has already written a solution using deque, and found its performance lacking (hence, this question). Using a ring buffer is an appropriate solution.
John Millikin
There's nothing in this question to indicate that he found performance lacking. Many beginners are constantly asking about the most effiicent solution to any given problem, regardless of whether their current solution performs acceptably or not.
jalf
@John, if he found the performance lacking, stripping away the shell of safety `queue` provides wouldn't increase performance, like I've been saying. You suggested a `list`, which will probably perform worse.
GMan
@jalf If he had not encountered a performance issue, he would not have asked the question. @GMan I suggested a ring buffer.
John Millikin
That's true, but it's not what we're arguing. You said "std::list would be the best STL container -- doubly-linked lists are a textbook solution to your problem." Which is false.
GMan
How is it false? A list is the most obvious solution, and therefore, the best. If performance is an issue, he should use a circular queue.
John Millikin
Did you read my answer? A deque is a better choice in every way. And what does "obvious" mean and what good do you think is going to come out of using such a subjective word in an argument? I think a list and deque are equally "obvious" choices, so then I say, "well, deque has better allocation and cache performance". I think that makes deque the "obvious" choice."Obvious is best' in itself is not sound. Newton's law of gravity was "obvious", now look at it.
GMan
The thing about std::queue<> is that if deque<> isn't what you want (for perfomance or whatever reason), it's a one-liner to change it to use a std::list as the backing store - as GMan said way back. And if you really want to use a ring buffer instead of a list, boost::circular_buffer<> will drop right in... std::queue<> is almost definitely the 'interface' that should be used. The backing store for it can be changed pretty much at will.
Michael Burr
+2  A: 

Why not std::queue? All it has is push_back and pop_front.

eduffy
+5  A: 

I continually push_back new elements while pop_front ing the oldest element (about a million time).

A million is really not a big number in computing. As others have suggested, use a std::queue as your first solution. In the unlikely event of it being too slow, identify the bottleneck using a profiler (do not guess!) and re-implement using a different container with the same interface.

anon
Well the thing is, it is a big number as what I want to do is supposed to be real time. Although you are right that I should have used a profiler to identify the cause...
Gab Royer
The thing is, I'm not really used to using a profiler (we've used gprof a bit in one of our classes but we really didn't go in depth...). If you could point me to some ressources, I would greatly appreciate! PS. I use VS2008
Gab Royer
@Gab: Which VS2008 do you have (Express, Pro...)? Some come with a profiler.
sbi
@Gab Sorry, I don't use VS any more so can't really advise
anon
@Sbi, I have the pro edition thanks to MSDNAA.
Gab Royer
@Sbi, from what I'm seeing, it's only in the team system edition (which I have access to). I'll look into this.
Gab Royer
+18  A: 

Since there are a myriad of answers, you might be confused, but to summarize:

Use a std::queue. The reason for this is simple: it is a FIFO structure. You want FIFO, you use a std::queue.

It makes your intent clear to anybody else, and even yourself. A std::list or std::deque does not. A list can insert and remove anywhere, which is not what a FIFO structure is suppose to do, and a deque can add and remove from either end, which is also something a FIFO structure cannot do.

This is why you should use a queue.

Now, you asked about performance. Firstly, always remember this important rule of thumb: Good code first, performance last.

The reason for this is simple: people who strive for performance before cleanliness and elegance almost always finish last. Their code becomes a slop of mush, because they've abandoned all that is good in order to really get nothing out of it.

By writing good, readable code first, most of you performance problems will solve themselves. And if later you find your performance is lacking, it's now easy to add a profiler to your nice, clean code, and find out where the problem is.

That all said, std::queue is only an adapter. It provides the safe interface, but uses a different container on the inside. You can choose this underlying container, and this allows a good deal of flexibility.

So, which underlying container should you use? We know that std::list and std::deque both provide the necessary functions (push_back(), pop_front(), and front()), so how do we decide?

First, understand that allocating (and deallocating) memory is not a quick thing to do, generally, because it involves going out to the OS and asking it to do something. A list has to allocate memory every single time something is added, and deallocate it when it goes away.

A deque, on the other hand, allocates in chunks. It will allocate less often than a list. Think of it as a list, but each node can hold multiple nodes. (Of course, I'd asuggest that you really learn how it works.)

So, with that alone a deque should perform better, because it doesn't deal with memory as often. Mixed with the fact you're handling data of constant size, it probably won't have to allocate after the first pass through the data, whereas a list will be constantly allocating and deallocating.

A second thing to understand is cache performance. Going out to RAM is slow, so when the CPU really needs to, it makes the best out of this time by taking a chunk of memory back with it, into cache. Because a deque allocates in memory chunks, it's likely that accessing an element in this container will cause the CPU to bring back the rest of the container as well. Now any further accesses to the deque will be speedy, because the data is in cache.

This is unlike a list, where the data is allocated one at a time. This means that data could be spread out all over the place in memory, and cache performance will be bad.

So, considering that, a deque should be a better choice. This is why it is the default container when using a queue. That all said, this is still only a (very) educated guess: you'll have to profile this code, using a deque in one test and list in the other to really know for certain.

But remember: get the code working with a clean interface, then worry about performance.

John raises the concern that wrapping a list or deque will cause a performance decrease. Once again, he nor I can say for certain without profiling it ourselves, but chances are that the compiler will inline the calls that the queue makes. That is, when you say queue.push(), it will really just say queue.container.push_back(), skipping the function call completely.

Once again, this is only an educated guess, but using a queue will not degrade performance, when compared to using the underlying container raw. Like I've said before, use the queue, because it's clean, easy to use, and safe, and if it really becomes a problem profile and test.

GMan
Down voted. I wonder who did that... if you could give me one good reason for it, I'd be surprised.
GMan
+1 for starting with good code and refining later -- it's always easier to make right code fast than it is to make fast code right.
Steve Gilham
+1 - and if it turns out that boost::circular_buffer<> has the best performance, then just use that as the underlying container (it also provides the required push_back(), pop_front(), front(), and back()).
Michael Burr
+1 nicely explained
seg.server.fault
Accepted for explaining it in details (which is what I needed, thanks for taking the time). As for the good code first performance last, I have to admit that's one of my biggest default, I always try to do thing perfectly on first run... I did write the code using a deque first tough, but since the thing wasn't performing as well as I thought (it is supposed to be almost real-time), I guessed I should improve it a bit. As Neil also said, I really should have used a profiler though... Although I'm happy to have made these mistakes now while it doesn't really matter. Thanks a lot all of you.
Gab Royer
A: 

Where performance really matters, check out the Boost circular buffer library.

Emile Cormier