Domanda

I am implementing my collection:

SpecialCollection class:

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 class:

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();
        }
    }

I faced with a question related to performance. For example, the adding of 10000 elements to my collection takes 9745 milliseconds. But the adding of 10000 elements to ConcurrentDictionary takes 4965 milliseconds.

If add 30000 elements to my collection, it takes a lot of time - about 40000 milliseconds.

I have no idea how to improve the performance :( Could you please to tell how can I improve the performance of my collection if it is possible? May be, the performance related to CompositeKey class?

Edit:

I tested the performance like this:

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);

Thanks!

È stato utile?

Soluzione

I tested this myself, and there is almost no difference in performance. Given your test code, it's likely that the difference in performance was because you don't reset your stopwatch in between tests. As the documentation says:

When a Stopwatch instance measures more than one interval, the Stop method is equivalent to pausing the elapsed time measurement. A subsequent call to Start resumes measuring time from the current elapsed time value. Use the Reset method to clear the cumulative elapsed time in a Stopwatch instance.

So simply add an sw.Reset() between tests.

In my tests, I did manage to get a very small performance improvement by using the lock keyword instead of a ReaderWriterLockSlim, though it may have been random.

Altri suggerimenti

ReaderWriterLockSlim#EnterWriteLock seems a lot slower than a simple C# monitor lock. Since ConcurrentDictionary uses simple locks (as many as the concurrency level parameter), this may be the source of your performance problem.

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();
        }
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top