Почему ConcurrentBag такой медленный в .Net (4.0 )? Я делаю это неправильно?

Прежде чем я начал проект, Я написал простой тест для сравнения производительности ConcurrentBag из (System.Collections.Concurrent) относительно блокировки и списков. Я очень удивлен, что ConcurrentBag более чем в 10 раз медленнее, чем блокировка с помощью простого списка. Насколько я понимаю, ConcurrentBag работает лучше всего, когда читатель и писатель находятся в одном потоке. Однако я не думал, что его производительность будет намного хуже, чем у традиционных блокировок.

Я провел тест с двумя параллельными циклами for для записи и чтения из списка / пакета. Однако запись сама по себе показывает огромную разницу:

private static void ConcurrentBagTest()
   {
        int collSize = 10000000;
        Stopwatch stopWatch = new Stopwatch();
        ConcurrentBag<int> bag1 = new ConcurrentBag<int>();

        stopWatch.Start();


        Parallel.For(0, collSize, delegate(int i)
        {
            bag1.Add(i);
        });


        stopWatch.Stop();
        Console.WriteLine("Elapsed Time = {0}", 
                          stopWatch.Elapsed.TotalSeconds);
 }

На моем компьютере это занимает от 3 до 4 секунд по сравнению с 0,5 - 0,9 секунды этого кода:

       private static void LockCollTest()
       {
        int collSize = 10000000;
        object list1_lock=new object();
        List<int> lst1 = new List<int>(collSize);

        Stopwatch stopWatch = new Stopwatch();
        stopWatch.Start();


        Parallel.For(0, collSize, delegate(int i)
            {
                lock(list1_lock)
                {
                    lst1.Add(i);
                }
            });

        stopWatch.Stop();
        Console.WriteLine("Elapsed = {0}", 
                          stopWatch.Elapsed.TotalSeconds);
       }

Как я уже упоминал, выполнение одновременных операций чтения и записи не помогает параллельному тесту пакетов. Я что-то делаю не так или эта структура данных очень медленная?

[EDIT] - Я удалил Задачи, потому что они мне здесь не нужны (Полный код имел другое чтение задачи)

[РЕДАКТИРОВАТЬ] Большое спасибо за ответы. Мне трудно выбрать «правильный ответ», так как он, кажется, представляет собой смесь нескольких ответов.

Как указал Майкл Гольдштейн, скорость действительно зависит от данных. Дарин указал, что должно быть больше конкуренции за то, чтобы ConcurrentBag был быстрее, а за Parallel.For не обязательно запускать одинаковое количество потоков. Один момент, который следует учесть, - не делать того, что вам не нужно внутри замка. В приведенном выше случае я не вижу, что делаю что-либо внутри блокировки, за исключением того, что могу присвоить значение временной переменной.

Кроме того, шестизначные переменные указали, что количество запущенных потоков также может повлиять на результаты, хотя я попытался запустить исходный тест в обратном порядке, и ConcurrentBag все еще работал медленнее.

Я провел несколько тестов с запуском 15 задач, и результаты, помимо прочего, зависели от размера коллекции. Однако ConcurrentBag работал почти так же или даже лучше, чем блокировка списка, до 1 миллиона вставок. Более 1 миллиона, Вот код, который я запустил:

        int collSize = 1000000;
        object list1_lock=new object();
        List<int> lst1 = new List<int>();
        ConcurrentBag<int> concBag = new ConcurrentBag<int>();
        int numTasks = 15;

        int i = 0;

        Stopwatch sWatch = new Stopwatch();
        sWatch.Start();
         //First, try locks
        Task.WaitAll(Enumerable.Range(1, numTasks)
           .Select(x => Task.Factory.StartNew(() =>
            {
                for (i = 0; i < collSize / numTasks; i++)
                {
                    lock (list1_lock)
                    {
                        lst1.Add(x);
                    }
                }
            })).ToArray());

        sWatch.Stop();
        Console.WriteLine("lock test. Elapsed = {0}", 
            sWatch.Elapsed.TotalSeconds);

        // now try concurrentBag
        sWatch.Restart();
        Task.WaitAll(Enumerable.Range(1, numTasks).
                Select(x => Task.Factory.StartNew(() =>
            {
                for (i = 0; i < collSize / numTasks; i++)
                {
                    concBag.Add(x);
                }
            })).ToArray());

        sWatch.Stop();
        Console.WriteLine("Conc Bag test. Elapsed = {0}",
               sWatch.Elapsed.TotalSeconds);
42
задан xpda 16 January 2013 в 18:05
поделиться