Menu Close

Simple Tips to improve C# ConcurrentDictionary performance

ConcurrentDictionary is a thread-safe version of Dictionary<k,v>. To achieve concurrency, hidden locks are wrapped in many operations that could easily be overlooked and cause performance impact. Some of the common examples are:

  • .Count
  • .Keys
  • .Value

Tip #0 prefer Dictionary<>

Limit the number of ConcurrentDictionary instances, because it uses more memory than Dictionary<K,V>. If there are no thread safe requirements, do not use it.

Tip #1 use GetOrAdd

When adding a value to the dictionary, do not check if value is already existed in the dictionary, use .GetOrAdd instead.

if (!_concurrentDictionary .TryGetValue(cachedInstanceId, out _privateClass))
{
        _privateClass = new PrivateClass();
        _concurrentDictionary.TryAdd(cachedInstanceId, _privateClass);
}

Above code defeats the purpose of ConcurrentDictionary. The correct usage should be:

 _privateClass = _concurrentDictionary.GetOrAdd(new PrivateClass());

Tip #2 set concurrencyLevel

The default constructor for ConcurrentDictionary uses concurrency level of 4 * number of cores of your machine. Often in cloud computing this is an overkill and has performance impact. Use a smaller number as concurrency level for your need instead. 

public ConcurrentDictionary (int concurrencyLevel, int capacity);

Tip #3 Keys and Values are expensive

ConcurrentDictionary.Keys and .Values are expensive; not only because they take a lock but also these operations need to build a list object. A good practice is to avoid using them, when you can use enumeration on KeyValuePairs instead.

Tip #4 use ContainsKey before lock operation

if (this._concurrentDictionary.TryRemove(itemKey, out value))
{
      // some operations
}

simply adding a non-thread safe containskey on top of this operation will greatly improve the performance

if (this._concurrentDictionary.ContainKey(itemKey))
{
       if (this._concurrentDictionary.TryRemove(itemKey, out value))
       {
             // some operations
       }
}

Tip #5 avoid .Count

Count operation in ConcurrentDictionary is expensive. what you can do is to wrap the object and have a lock free count.

Interlocked.Increment(ref this.count);
//or
Interlocked.Decrement(ref this.count);

Leave a Reply

Your email address will not be published.