比较并发字典与Dictionary + Locks的性能

时间:2019-07-15 16:56:09

标签: c# .net performance dictionary

我想在多线程环境中测量并发字典与字典+锁的性能。因此,我创建了自己的<int,int[]>类型的SyncDict类。只要存在键匹配,它就会向其自身添加int[]数组值,并在更新值时使用ReaderWriterLockSlim锁定整个字典。

我通过并发字典复制了代码,主要使用AddOrUpdate()方法。

整个控制台应用程序代码都可以在这里找到https://dotnetfiddle.net/1kFbGy,只需将代码复制粘贴到控制台应用程序中即可运行。它不会运行小提琴

在使用相同的输入运行两个代码之后,我发现运行时间有相当大的差异。例如,在我的计算机上进行一次特定的运行,并发字典花费了4.5秒,而SyncDict花费了不到1秒。

我想知道任何解释上述运行时间的想法/建议。我在这里做错什么吗?

 class SyncDict<TKey>
    {
        private ReaderWriterLockSlim cacheLock;
        private Dictionary<TKey, int[]> dictionary;
        public SyncDict()
        {
            cacheLock = new ReaderWriterLockSlim();
            dictionary = new Dictionary<TKey, int[]>();
        }

        public Dictionary<TKey, int[]> Dictionary
        {
            get { return dictionary; }
        }

        public int[] Read(TKey key)
        {
            cacheLock.EnterReadLock();
            try
            {
                return dictionary[key];
            }
            finally
            {
                cacheLock.ExitReadLock();
            }
        }

        public void Add(TKey key, int[] value)
        {
            cacheLock.EnterWriteLock();
            try
            {
                dictionary.Add(key, value);
            }
            finally
            {
                cacheLock.ExitWriteLock();
            }
        }

        public AddOrUpdateStatus AddOrUpdate(TKey key, int[] value)
        {
            cacheLock.EnterUpgradeableReadLock();
            try
            {
                int[] result = null;
                if (dictionary.TryGetValue(key, out result))
                {
                    if (result == value)
                        return AddOrUpdateStatus.Unchanged;
                    else
                    {
                        cacheLock.EnterWriteLock();
                        try
                        {
                            Parallel.For(0, value.Length,
                            (i, state) =>
                            {
                                result[i] = result[i] + value[i];
                            });
                        }
                        finally
                        {
                            cacheLock.ExitWriteLock();
                        }
                        return AddOrUpdateStatus.Updated;
                    }
                }
                else
                {
                    Add(key, value);
                    return AddOrUpdateStatus.Added;
                }
            }
            finally
            {
                cacheLock.ExitUpgradeableReadLock();
            }
        }

        public void Delete(TKey key)
        {
            cacheLock.EnterWriteLock();
            try
            {
                dictionary.Remove(key);
            }
            finally
            {
                cacheLock.ExitWriteLock();
            }
        }

        public enum AddOrUpdateStatus
        {
            Added,
            Updated,
            Unchanged
        };
    }

1 个答案:

答案 0 :(得分:1)

your test存在多个问题。

1)您正在用约150.000个不同的键填充字典,所有键都具有相同的值。

2)所有条目的共享值是30.000个整数的数组,并且您在对AddOrUpdate的调用的一半处更新了它的每个元素。但这仅在您测试ConcurrentDictionary时发生。在SyncDict测试中,条件if (result == value) return AddOrUpdateStatus.Unchanged会跳过所有更新(因为该值是共享的)。

3)您正在使用不同的随机输入来输入两个测试。

4)您正在使用Parallel.For循环更新数组,而同时已经在外部Parallel.For循环中,从而使工作负载过分并行。

5)在调用方法AddOrUpdate时,您忽略了以下事实:updateValueFactory函数以线程不安全的方式被调用,并且由于多个AddOrUpdate是同步执行的并且该值是共享的,您正在破坏该值的状态。

  

在锁之外调用updateValueFactory委托,以避免在锁下执行未知代码可能引起的问题。
  ConcurrentDictionary.AddOrUpdate Method

我建议您修改测试以反映ConcurrentDictionary类的预期用途。