Прежде чем я начал проект, я написал простой тест, чтобы сравнить производительность ConcurrentBag от (System.Collections.Concurrent) относительно блокировки и списков. Я очень удивлен, что ConcurrentBag более чем в 10 раз медленнее блокировки с помощью простого списка. Насколько я понимаю, ConcurrentBag работает лучше всего, когда читатель и писатель - это один и тот же поток. Однако я не думал, что производительность будет намного хуже, чем традиционные блокировки.
Я проверил тест с двумя Parallel для записи петель и чтения из списка/пакета. Однако сама запись показывает огромную разницу:
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] - Я удалил Задачи, потому что я не нуждаюсь в них здесь (у полного кода было другое задание)
[EDIT] Большое спасибо за ответы. Мне сложно выбрать "правильный ответ", поскольку, похоже, это сочетание нескольких ответов.
Как отметил Майкл Голдштейн, скорость действительно зависит от данных. Дарин отметил, что для ConcurrentBag должно быть больше споров, и Parallel.For не обязательно запускает одинаковое количество потоков. Один момент, который нужно убрать, - не делать ничего, что вам не нужно для внутри замка. В приведенном выше случае я не вижу, что я что-то делаю внутри блокировки, за исключением того, что присваиваю значение переменной 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);