提高集合的添加性能

时间:2014-05-15 12:15:16

标签: c# performance collections

我正在实施我的收藏:

SpecialCollection类:

public class SpecialCollection<TId, TName, TValue> : Dictionary<CompositeKey<TId, TName>, TValue>
    {

        #region Private fileds

        private Dictionary<CompositeKey<TId, TName>, TValue> _baseDictionary = null;
        private ReaderWriterLockSlim _readWriteLockSlim = new ReaderWriterLockSlim();

        #endregion

        #region Constructors

        public SpecialCollection()
        {
            _baseDictionary = new Dictionary<CompositeKey<TId, TName>, TValue>();
        }

        #endregion

        public void Add(CompositeKey<TId, TName> compositeKey, TValue value)
        {
            _readWriteLockSlim.EnterWriteLock();

            try
            {
                _baseDictionary.Add(compositeKey, value);
            }
            catch (ArgumentNullException ex)
            {
                throw ex;
            }
            catch (ArgumentException ex)
            {
                throw ex;
            }
            finally
            {
                _readWriteLockSlim.ExitWriteLock();
            }
        }
}

CompositeKey类:

public struct CompositeKey<TId, TName> : IEquatable<Tuple<TId, TName>>
    {
        public TId Id;
        public TName Name;

        public CompositeKey(TId id, TName name)
        {
            Id = id;
            Name = name;
        }

        public override bool Equals(object obj)
        {
            if (obj == null)
                return false;

            if (this.GetType() != obj.GetType())
                return false;

            return AreEqual(this, (CompositeKey<TId, TName>)obj);
        }

        public bool Equals(CompositeKey<TId, TName> other)
        {
            return AreEqual(this, other);
        }

        private static bool AreEqual(CompositeKey<TId, TName> a, CompositeKey<TId, TName> b)
        {
            if (!a.Id.Equals(b.Id))
                return false;

            if (!a.Name.Equals(b.Name))
                return false;

            return true;
        }

        public static bool operator == (CompositeKey<TId, TName> a, CompositeKey<TId, TName> b)
        {
            return AreEqual(a, b);
        }

        public static bool operator != (CompositeKey<TId, TName> a, CompositeKey<TId, TName> b)
        {
            return !AreEqual(a, b);
        }

        public override int GetHashCode()
        {
            return Id.GetHashCode() ^ Name.GetHashCode();
        }

        public bool Equals(Tuple<TId, TName> other)
        {
            throw new NotImplementedException();
        }
    }

我遇到了与表现有关的问题。例如,向我的集合添加10000个元素需要9745毫秒。但是向ConcurrentDictionary添加10000个元素需要4965毫秒。

如果将30000个元素添加到我的集合中,则需要花费大量时间 - 大约40000毫秒。

我不知道如何提高性能:(如果有可能,请告诉我如何改善我的收藏的性能?可能是,与CompositeKey类相关的性能?

编辑:

我测试了这样的表现:

ConcurrentDictionary<CompositeKey<int, int>, int> cd = new ConcurrentDictionary<CompositeKey<int, int>, int>();
            SpecialCollection<int, int, int> sc = new SpecialCollection<int, int, int>();

            Stopwatch sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < 10000; i++)
            {
                cd.TryAdd(new CompositeKey<int, int>(i, i), i);
            }
            sw.Stop();
            Console.WriteLine(sw.ElapsedMilliseconds);

            sw.Start();
            for (int i = 0; i < 10000; i++)
            {
                sc.Add(new CompositeKey<int, int>(i, i), i);

            }
            sw.Stop();
            Console.WriteLine(sw.ElapsedMilliseconds);

谢谢!

2 个答案:

答案 0 :(得分:5)

我自己测试过,性能几乎没有差异。鉴于您的测试代码,性能差异可能是因为您不会在测试之间重置秒表。正如the documentation所说:

  

当秒表实例测量多个间隔时,Stop方法相当于暂停经过时间测量。随后对Start的调用将恢复从当前经过时间值开始测量时间。使用“重置”方法清除秒表实例中的累计已用时间。

只需在测试之间添加sw.Reset()

在我的测试中,我确实使用lock关键字代替ReaderWriterLockSlim来获得非常小的性能提升,但它可能是随机的。

答案 1 :(得分:1)

ReaderWriterLockSlim#EnterWriteLock似乎比简单的C#监视器锁慢得多。由于ConcurrentDictionary使用简单锁(与并发级别参数一样多),因此这可能是性能问题的根源。

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        private static object lock1 = new object();
        private static ReaderWriterLockSlim lock2 = new ReaderWriterLockSlim();

        public static int DoLock1(int value)
        {
            lock (lock1)
                return value;
        }

        public static int DoLock2(int value)
        {
            lock2.EnterWriteLock();
            try
            {
                return value;
            }
            finally
            {
                lock2.ExitWriteLock();
            }

        }


        static void Main(string[] args)
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < 10000000; i++)
            {
                DoLock1(i);
            }
            sw.Stop();
            Console.WriteLine(sw.ElapsedMilliseconds);

            sw.Reset();
            sw.Start();
            for (int i = 0; i < 10000000; i++)
            {
                DoLock2(i);

            }
            sw.Stop();
            Console.WriteLine(sw.ElapsedMilliseconds);
            Console.ReadLine();
        }
    }
}