Performance: Whidbey generic Queue didn't get hit with the same performance stick as the generic Stack

I think I've narrowed down the new Whidbey generic Stack class to using a linked array Performance: Linked Arrays's now and later. I figured, hey, they must have used the same features in the Queue right? Not quite, in fact the generic queue can actually be slower in some cases. You really have to understand how the old collections worked and how the new ones work. When testing the old collections you had two seprate code-paths that are important. First you have to test the collections with boxing, and that means using a value-type, such as int. Then you test them without boxing using a reference type. Just passing in an object will do for this second test. Generics should have the same performance no matter what we use, so we can just test a single code-path there (or both for completeness).

That gives us three tests, but we'll add three stack tests as well to show how the performance bars are completely different for the two collections.

Testing 100 values for 1000000 iterations
Generic Queue Usage: 00:00:11.6868048
Generic Stack Usage: 00:00:05.0773008
Queue Usage (int): 00:00:11.9672080
Queue Usage (Object): 00:00:10.6953792
Stack Usage (int): 00:00:08.5322688
Stack Usage (Object): 00:00:06.0286688

That is pretty amazing stuff. The Stack is definitely showing clear signs of being improved in terms of performance. I've already shown that in earlier articles, but the percentages between the various tests are important because you'd expect the Queue to share some of that attention to performance. Testing 100 values (enqueue and dequeue) the generic queue has slightly better performance than the boxed integer Queue... What? Hell, it should get quite a bit faster right? Even worse, the old Queue when using reference types is actually faster than the generic version. Something changed. Not sure what, but it is possible to use LinkedArray structures again to make the Queue almost as fast as the Stack. I say almost, because there are some extra features in the Queue, extra checks as you were, that take more time to resolve. As an example, you can see a multi-segment queue implementation of the Enqueue method. Each item in the linked list of arrays is of size growthSize, and we use this to allocate more arrays as needed rather than growing this value and creating increasingly larger arrays as we advance.

public void Enqueue( T value ) {
    if ( tail == growthSize ) {
        if ( tailList.Next == null ) {
            tailList.Next = new LinkedArray<T>( growthSize, null );
        }
        tailList = tailList.Next;
        tail = 0;
    }

    tailList.Data[tail++] = value;
}

Thats just the start of a performant collection, but you can start to see where the operations required are just about the same as those required for the stack, really bringing this collection into the same league in terms of performance. MS must have some data that people use stacks more than queues?

Published Thursday, September 30, 2004 10:25 PM by Justin Rogers

Comments

Friday, October 01, 2004 9:44 AM by Dan Golick

# re: Performance: Whidbey generic Queue didn't get hit with the same performance stick as the generic Stack

Well a few minutes with reflector convinced me that generic queue and generic stack use
T[] _array;
No linked-array.
I think the linked array is a great way to avoid re-allocating and copying the array as the size of the stack or queue grows.

I think you performance tests are dominated by this array copying.

If you have an application where you are tuning for performance and you know the upper bound on the size of the collection the best thing you can do is set the capacity (perhaps in the constructor).

If you re-run your performance tests with the capacity set in advance you'll see some remarkable improvements.

I'm surprised you are seeing such poor performance in the generic queue relative to the stack this may have to do with your testing methodology. Of course the queue has to do more. It must maintain a tail and a head and manage wrap-around. Nonetheless I think you have some problem such as reallocating the queue each time and not the stack.
Friday, October 01, 2004 5:46 PM by Justin Rogers

# re: Performance: Whidbey generic Queue didn't get hit with the same performance stick as the generic Stack

Array copying is definitely a performance hit. That really doesn't account for why the Stack comes so darn close to the LinkedArray implementation in terms of performance. Currently it is marked on my list of things that don't make sense. I'll run this down some other time.

Setting a capacity is definitely intuitive, but you often have an idea of your upper bound, but on the average your actual usage is much smaller. There are two metrics here, memory vs speed. You'd like an algorithm that reduces your memory usage and maximizes your speed. I generally like to use lower bounds when setting my capacity, especially with something like the linked array queue. Why? Well, with the linked array, whatever size we set that growth factor to in the beginning, we'll continue to add more segments of that size as we go along. In my tests I tend to use 10 as the growth factor, so that queues of length 100 have 10 segments, 1000 have 100 segments. That is a lot of segments. If I knew my lower bound was in the range of 100 and my upper bound in the thousdands, it would be more beneficial to set that initial growth factor to something around 100 or 200. I minimize the use of space and maximize the performance.

Well, I think at this point we are assuming that we both have the same versions of Whidbey. That may not be the case and could lead to some nastily confusing results. I feel pretty secure in why the Queue is soo much slower, they are simply doing far more array work than should be necessary. If the Reflector tool is correct, they are also making the mistake of *clearing* the array as elements are removed by setting them to T.default. I can't see where this solves any problems, especially since T.default can be an actual valid program value (0 or null).
Friday, October 07, 2011 3:26 AM by chi flat iron outlet

# re: Performance: Whidbey generic Queue didn't get hit with the same performance stick as the generic Stack

You made some decent points there.. Wow! what an idea ! What a concept ! Beautiful .. Amazing Agreeable A rise in An increase in.

Leave a Comment

(required) 
(required) 
(optional)
(required)