ConcurrentDictionary<> performance at a single thread misunderstanding?

concurrentdictionary addorupdate
concurrentdictionary tryupdate
concurrentdictionary tryremove
concurrentdictionary performance
concurrentdictionary vs dictionary
concurrentdictionary remove
concurrentdictionary getoradd
concurrentdictionary async

Related brief info:

AFAIK , The concurrent stack, queue, and bag classes are implemented internally with linked lists. And I know that there is much less contention because each thread is responsible for its own linked list. Any way , my question is about the ConcurrentDictionary<,>

But I was testing this code :(single thread)

Stopwatch sw = new Stopwatch();
sw.Start();

    var d = new ConcurrentDictionary < int,  int > ();
    for(int i = 0; i < 1000000; i++) d[i] = 123;
    for(int i = 1000000; i < 2000000; i++) d[i] = 123;
    for(int i = 2000000; i < 3000000; i++) d[i] = 123;
    Console.WriteLine("baseline = " + sw.Elapsed);

sw.Restart();

    var d2 = new Dictionary < int, int > ();
    for(int i = 0; i < 1000000; i++)         lock (d2) d2[i] = 123;
    for(int i = 1000000; i < 2000000; i++)   lock (d2) d2[i] = 123;
    for(int i = 2000000; i < 3000000; i++)   lock (d2) d2[i] = 123;
    Console.WriteLine("baseline = " + sw.Elapsed);

sw.Stop();

Result : (tested many times, same values (+/-)).

baseline = 00:00:01.2604656
baseline = 00:00:00.3229741

Question :

What makes ConcurrentDictionary<,> much slower in a single threaded environment ?

My first instinct is that lock(){} will be always slower. but apparently it is not.

How to: Add and Remove Items from a ConcurrentDictionary , ConcurrentDictionary is thread-safe collection class to store key/value pairs. It internally uses locking to provide you a thread-safe class. It provides different  ConcurrentDictionary<TKey,TValue>() Initializes a new instance of the ConcurrentDictionary<TKey,TValue> class that is empty, has the default concurrency level, has the default initial capacity, and uses the default comparer for the key type.

Well, ConcurrentDictionary is allowing for the possibility that it can be used by multiple threads. It seems entirely reasonable to me that that requires more internal housekeeping than something which assumes it can get away without worrying about access from multiple threads. I'd have been very surprised if it had worked out the other way round - if the safer version were always faster too, why would you ever use the less safe version?

ConcurrentDictionary in C#, It depends on what you mean by thread-safe. From MSDN - How to: Add and Remove Items from a ConcurrentDictionary:. ConcurrentDictionary is one of five collection classes introduced in .NET 4.0. ConcurrentDictionary is thread-safe collection class to store key/value pairs.

The ConcurrentDictionary<> creates an internal set of locking objects at creation (this is determined by the concurrencyLevel, amongst other factors) - this set of locking objects is used to control access to the internal bucket structures in a series of fine-grained locks.

In a single threaded scenario, there would be no need for the locks, so the extra overhead of acquiring and releasing these locks is probably the source of the difference you're seeing.

Concurrent Dictionary Correct Usage, All public and protected members of ConcurrentDictionary<TKey, TValue> are thread-safe and may be used concurrently from multiple threads. How fast is ConcurrentDictionary? I tested its lookup performance. In the simple benchmark, 2000 string keys are assigned to ints. Then the key "100" is looked up and timed.Benchmark. Result: The ConcurrentDictionary was almost twice as slow as the regular Dictionary. The overhead in ConcurrentDictionary was small. Note: I was expecting it to

ConcurrentDictionary vs. Dictionary

In general, use a System.Collections.Concurrent.ConcurrentDictionary in any scenario where you are adding and updating keys or values concurrently from multiple threads. In scenarios that involve frequent updates and relatively few reads, the ConcurrentDictionary generally offers modest benefits. In scenarios that involve many reads and many updates, the ConcurrentDictionary generally is significantly faster on computers that have any number of cores.

In scenarios that involve frequent updates, you can increase the degree of concurrency in the ConcurrentDictionary and then measure to see whether performance increases on computers that have more cores. If you change the concurrency level, avoid global operations as much as possible.

If you are only reading key or values, the Dictionary is faster because no synchronization is required if the dictionary is not being modified by any threads.

Link: https://msdn.microsoft.com/en-us/library/dd997373%28v=vs.110%29.aspx

ConcurrentDictionary Is Not Always Thread-Safe, Example. These 2 programs compare ConcurrentDictionary and Dictionary when adding keys and values. You can see they are about the same length in code. ConcurrentDictionary<TKey, TValue> is designed for multithreaded scenarios. You do not have to use locks in your code to add or remove items from the collection. You do not have to use locks in your code to add or remove items from the collection.

UPDATE: The latest ConcurrentDictionary has improved significantly and offers the same performance profile as my implementation with more features so I recommend you use that instead :)

I just wrote about my lock-free thread-safe copy-on-write dictionary implementation here:

http://www.singulink.com/CodeIndex/post/fastest-thread-safe-lock-free-dictionary

It is very fast for quick bursts of writes and lookups usually run at 100% standard Dictionary speed without locking. If you write occasionally and read often, this is the fastest option available.

It's currently append-only since I used it for caching but if there's popular demand then I might add removal methods on it too.

ConcurrentDictionary is slow because it takes out read/write locks on every operation. Read/write locks are even slower than normal locks but it allow multiple readers without blocking.

My implementation gives maximum read performance by removing the need for any read locks under normal circumstances while updates aren't being made to the dictionary. The trade-off is that the dictionary needs to be copied and swapped after updates are applied (this is done on a background thread) but if you don't write often or you only write once during initialization then the trade-off is definitely worth it.

C# ConcurrentDictionary (TryAdd, GetOrUpdate), ConcurrentDictionary handles multiple threads. This type from the System.​Collections.Concurrent namespace allows multiple threads to access a Dictionary  ConcurrentDictionary<TKey,TValue> provides several convenience methods that make it unnecessary for code to first check whether a key exists before it attempts to add or remove data. The following table lists these convenience methods and describes when to use them.

C# ConcurrentDictionary Use, ConcurrentDictionary<TKey, TValue> is heavily used in thread-safe scenarios (​memory cache, temp storage, etc.). There are times when you  Concurrent Dictionary<TKey,TValue>.Values Property Definition. Namespace: System.Collections.Concurrent Add and Remove Items from a ConcurrentDictionary; Is this

Proposal: Add an atomic ToArray + Clear method to , The ConcurrentDictionary is a dictionary that allows you to add, fetch and remove items in a thread-safe way. Note: 1. The ConcurrentDictionary type resides in  The ConcurrentDictionary is a great option if it fulfills all your thread-safety needs. If it's not, in other words of you are doing anything slightly complex, a normal Dictionary + lock may be a better option.

C# ConcurrentDictionary with Examples, In this article you will learn about concurrent collections and ConcurrentDictionary in detail including how to use it efficiently in software  Array 2.5 ns/inc Dictionary (@Ani) 27.5 ns/inc Dictionary (Simple) 37.4 ns/inc SortedDictionary 192.5 ns/inc ConcurrentDictionary 79.7 ns/inc And that's the code. Note that ConcurrentDictionary.TryAddOrUpdate is three times slower than Dictionary's TryGetValue + indexer's setter. And the latter is ten times slower than Array.

Comments
  • First, are you running your tests in release mode without the debugger attached? (i.e. "Run without debugging"). Second, "each thread is responsible for its own linked list" is incorrect. If you want to see how these things are implemented, download the library source code from referencesource.microsoft.com/netframework.aspx
  • I'm surprised that you're surprised. There is an overhead associated with ensuring that all operations are atomic. When the operation itself is so blindingly simple (in the case of Dictionary it's only setting a single array value and almost nothing else) that overhead can be a lot in comparison. When compared to non-trivial operations, the overhead is more likely to be negligible.
  • @JimMischel books.google.co.il/…
  • That's only true of ConcurrentBag, which is a quite different thing than the other collections you mentioned. A ConcurrentQueue, for example, couldn't be implemented in the same way, because then it wouldn't be FIFO. And ConcurrentStack wouldn't be LIFO.
  • I imagine your reasoning was, "I could either do this myself using lock or use ConcurrentDictionary, which was written by experts. Since the experts also had the option to use locks, surely their implementation will be equal to or better than mine." Surprisingly, a lot of the advanced approaches (e.g., lock-free algorithms) actually have more overhead than standard locks, but their throughput is higher under heavy load due to better handling of contention. See also Reed Copsey's answer to another question.
  • Aren't volatile writes are by default ?
  • @RoyiNamir no, in general writes are atomic by default (except for struct types > pointer size)
  • I was reading here (igoro.com/archive/volatile-keyword-in-c-memory-model-explained) that --- in C# all writes are volatile (unlike say in Java), regardless of whether you write to a volatile or a non-volatile field.
  • @RoyiNamir I chatted with Igor and he says indeed that particular line is incorrect with respect to ARM. He did recently write 2 articles for MSDN magazine which he says are more up to date and account for ARM msdn.microsoft.com/en-us/magazine/jj863136.aspx msdn.microsoft.com/en-us/magazine/jj883956.aspx
  • @RoyiNamir it's a particular processor type like x86 and amd64/x64. Most tablets run on ARM processors (iPad, Surface, etc ...)
  • Perhaps the degree of difference surprised him?
  • @Servy: A better comparison would be between a tank and a sports car. A tank is more robust, but will generally be slower...