Перед тем, как начать проект, я написал простой тест для сравнения производительности ConcurrentBag из (System.Collections.Concurrent) относительно блокировки и списков.Я чрезвычайно удивлен, что ConcurrentBag более чем в 10 раз медленнее, чем блокировка с помощью простого List.Из того, что я понимаю, ConcurrentBag работает лучше всего, когда читатель и писатель находятся в одном потоке.Тем не менее, я не думал, что его производительность будет намного хуже, чем у традиционных блокировок.
Я провел тест с двумя параллельными циклами для записи и чтения из списка / пакета.Тем не менее, запись сама по себе показывает огромную разницу:
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);
}
Как я уже говорил, одновременное чтение и запись не помогают при одновременном тестировании пакета.Я делаю что-то не так или эта структура данных просто очень медленная?
[РЕДАКТИРОВАТЬ] - Я удалил Задачи, потому что они мне здесь не нужны (полный код читал другую задачу)
[Редактировать] Большое спасибо за ответы.Я с трудом выбираю «правильный ответ», так как кажется, что это сочетание нескольких ответов.
Как отметил Майкл Гольдштейн, скорость действительно зависит от данных.Дарин отметил, что для того, чтобы ConcurrentBag работал быстрее, должно быть больше конфликтов, и Parallel.For не обязательно запускает одинаковое количество потоков.Один момент, который нужно отнять, - не делать ничего, что вам не нужно внутри замка 1016 *.В приведенном выше случае я не вижу, что я делаю что-либо внутри блокировки, за исключением того, что я могу присвоить значение переменной temp.
Кроме того, sixlettervariables указали, что число потоков, которые работают, также можетвлияют на результаты, хотя я пытался запустить исходный тест в обратном порядке, и 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);