Last active
May 11, 2022 08:33
-
-
Save AArnott/6666dd68e1686130d467e970fdd68011 to your computer and use it in GitHub Desktop.
The .NET Concurrent collections are often used as a thread-safe version of the ordinary collections. But they are *far* slower and heavier on GC pressure than simply adding a `lock` around use of the ordinary collections. Avoid the concurrent collections except in highly concurrent scenarios where you see high lock contention and therefore may b…
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Concurrent; | |
using System.Collections.Generic; | |
using System.Diagnostics; | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
var ordinary = RunTest(OrdinaryDictionaryRun); | |
var concurrent = RunTest(ConcurrentDictionaryRun); | |
Console.WriteLine($"Concurrent time: {concurrent.Elapsed}, GC pressure: {concurrent.BytesAllocated}"); | |
Console.WriteLine($"Ordinary time: {ordinary.Elapsed}, GC pressure: {ordinary.BytesAllocated}"); | |
} | |
private static void OrdinaryDictionaryRun() | |
{ | |
var dictionary = new Dictionary<int, int>(); | |
for (int i = 0; i < 10000; i++) | |
{ | |
lock (dictionary) | |
{ | |
dictionary.TryAdd(i, 0); | |
} | |
} | |
for (int i = 0; i < 10000; i++) | |
{ | |
lock (dictionary) | |
{ | |
dictionary.Remove(i); | |
} | |
} | |
} | |
private static void ConcurrentDictionaryRun() | |
{ | |
var concurrentDictionary = new ConcurrentDictionary<int, int>(); | |
for (int i = 0; i < 10000; i++) | |
{ | |
concurrentDictionary.TryAdd(i, 0); | |
} | |
for (int i = 0; i < 10000; i++) | |
{ | |
concurrentDictionary.TryRemove(i, out int _); | |
} | |
} | |
private static (TimeSpan Elapsed, long BytesAllocated) RunTest(Action action) | |
{ | |
GC.Collect(); | |
long memoryBefore = GC.GetAllocatedBytesForCurrentThread(); | |
var timer = Stopwatch.StartNew(); | |
action(); | |
timer.Stop(); | |
long memoryAfter = GC.GetAllocatedBytesForCurrentThread(); | |
return (timer.Elapsed, memoryAfter - memoryBefore); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
On my machine, the output is: