Sunday, 29 November 2015

ConcurrentDictionary or Dictionary with lock

You may already know that there are 2 types of dictionary classes available in .NET framework - Dictionary and ConcurrentDictionary.

ConcurrentDictionary is possibly the right choice in cases when you have to maintain a static dictionary in an application where parallel access to the dictionary is possible. Surely you can do the same with a Dictionary instance but then you have to write lock statements around dictionary operation statements.

How about comparing the performance overhead between the two approaches. I wrote a simple utility that performs random read/write/delete operations on the two types and measure any difference. To my surprise, I did not see any big difference. Both approaches showed similar numbers :)

        private static bool shouldStop = false;
        private static object lockObj = new object();
        private static Dictionary dict = new Dictionary();
        private static ConcurrentDictionary dict2 = new ConcurrentDictionary();
        private static Random r = new Random();
        private static string[] OperationsList = new string[11]
        {
            "R", "W", "R", "W", "D", "R", "R", "R", "R", "W", "D"
        };

        static void Main(string[] args)
        {
            dict.Add("A", "B");
            dict2.GetOrAdd("A", "B");
            Console.WriteLine("Starting...");
            Task t = new Task(RunDictionaryTest);
            t.Start();
            Console.ReadKey();
            shouldStop = true;
            Console.WriteLine("Stopping...");
        }

        private static void RunDictionaryTest()
        {
            Action a = () =>
            {
                Console.WriteLine("Running...");
                while (!shouldStop)
                {
                    var number = r.Next(0, 10);
                    ////DoRandomDictionaryTryGetOperation(OperationsList[number]);
                    DoRandomDictionaryOperationWithContainsKey(OperationsList[number]);

                    ////DoRandomConcurrentDictionaryOperation(OperationsList[number]);
                    ////Thread.Sleep(10);
                }
            };

            Task[] tasks = new Task[100];
            for (int i = 0; i < 100; i++)
            {
                tasks[i] = new Task(a);
            }

            for (int i = 0; i < 100; i++)
            {
                tasks[i].Start();
            }

            Task.WaitAll(tasks);
        }

        private static void DoRandomConcurrentDictionaryOperation(string operationName)
        {
            string val;
            Stopwatch sw = new Stopwatch();
            sw.Start();
            switch (operationName)
            {
                case "R":
                    if (dict2.TryGetValue("Key", out val))
                    {
                        Console.WriteLine("Read:" + val);
                    }
                    else
                    {
                        Console.WriteLine("No Read");
                    }
                    break;
                case "W":
                    val = Guid.NewGuid().ToString();
                    string val2 = dict2.AddOrUpdate("Key", val, (k, v) => val);
                    if (!val.Equals(val2))
                    {
                        Console.WriteLine("no write");
                    }
                    break;
                case "D":
                    if(!dict2.TryRemove("Key", out val))
                    {
                        Console.WriteLine("no delete");
                    }
                    break;
            }
            sw.Stop();
            Console.WriteLine("Time taken" + sw.ElapsedMilliseconds.ToString());
        }

        private static void DoRandomDictionaryTryGetOperation(string operationName)
        {
            string val;
            Stopwatch sw = new Stopwatch();
            sw.Start();
            switch (operationName)
            {
                case "R":
                    lock (lockObj)
                    {
                        if (dict.TryGetValue("Key", out val))
                        {
                            Console.WriteLine("Read:" + val);
                        }
                        else
                        {
                            Console.WriteLine("No Read");
                        }
                    }
                    break;
                case "W":
                    lock (lockObj)
                    {
                        if (dict.TryGetValue("Key", out val))
                        {
                            Console.WriteLine("no write");
                        }
                        else
                        {
                            dict.Add("Key", Guid.NewGuid().ToString());
                        }
                    }
                    break;
                case "D":
                    lock (lockObj)
                    {
                        if (dict.TryGetValue("Key", out val))
                        {
                            dict.Remove("Key");
                        }
                        else
                        {
                            Console.WriteLine("no delete");
                        }
                    }
                    break;
            }
            sw.Stop();
            Console.WriteLine("Time taken" + sw.ElapsedMilliseconds.ToString());
        }

        private static void DoRandomDictionaryOperationWithContainsKey(string operationName)
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            switch (operationName)
            {
                case "R":
                    lock (lockObj)
                    {
                        if (dict.ContainsKey("Key"))
                        {
                            Console.WriteLine("Read:" + dict["Key"]);
                        }
                        else
                        {
                            Console.WriteLine("No Read");
                        }
                    }
                    break;
                case "W":
                    lock (lockObj)
                    {
                        if (dict.ContainsKey("Key"))
                        {
                            Console.WriteLine("no write");
                        }
                        else
                        {
                            dict.Add("Key", Guid.NewGuid().ToString());
                        }
                    }
                    break;
                case "D":
                    lock (lockObj)
                    {
                        if (dict.ContainsKey("Key"))
                        {
                            dict.Remove("Key");
                        }
                        else
                        {
                            Console.WriteLine("no delete");
                        }
                    }
                    break;
            }
            sw.Stop();
            Console.WriteLine("Time taken" + sw.ElapsedMilliseconds.ToString());
        }

No comments:

Post a Comment