The HIGH cost of ConcurrentBag in .NET 4.0
I got some strange results when using concurrent collections, so I decided to try to track it down, and wrote the following code:
var count = ?; var list = new List<object>(count); var sp = Stopwatch.StartNew(); for (int i = 0; i < count; i++) { list.Add(new ConcurrentBag<int>()); } sp.Stop(); Console.WriteLine("{0} {2} items in {1:#,#;;0}ms = {3:#,#;;0}ms per item", sp.Elapsed, sp.ElapsedMilliseconds, count, sp.ElapsedMilliseconds / count);
And then I started to play with the numbers, and it is not good.
- 10 items in 2ms = 0ms per item
This is incredibly high number, you have to understand. Just to compare, List<int> takes 8 ms to create 100,000 items.
Let us see how it works when we use more of this.
- 100 items in 5ms = 0ms per item
- 1,000 items in 37ms = 0ms per item
- 10,000 items in 2,319ms = 0ms per item
Note the numbers, will you?
1,000 items in 37 ms, but 10,000 items? 2.3 seconds!
- 20,000 items in 21,331ms = 1ms per item
And doubling the amount took ten times as long?
- 25,000 items in 32,588ms = 1ms per item
And at this point, I stopped trying, because I didn’t have the patience.
Note that the other concurrent collection, ConcurrentStack, ConcurrentQueue and ConcurrentDictionary do not suffer from the same problem.
I contacted Microsoft about this, and this is already resolved in .NET 4.5. The underlying issue was that ThreadLocal, which ConcurrentBag uses, didn’t expect to have a lot of instances. That has been fixed, and now can run fairly fast.

Comments
Comment preview