C# - ConcurrentBag 与 List 的性能比较

Leo*_*rdo 6 .net c# concurrent-collections

前言:我问这个只是因为我没有环境(数据集足够大+计算能力)以可靠的方式对其进行测试。

问题:给定一个并发包,加载了数十亿个项目,由单个线程访问/使用,它的性能是否类似于List?换句话说,对 a 的枚举是否比对 aConcurrent Bag更具有表演性List<T>

Dou*_*las 9

ConcurrentBag<T>将不可避免地比List<T>. 尽管您只能从单个线程访问它,但该结构仍然需要有适当的机制来防止并发访问出现时出现竞争风险的可能性。

如果您将开始枚举之前从单个线程加载集合,则可以通过使用ConcurrentBag(IEnumerable<T>)构造函数来避免性能开销,而不是通过其Add方法单独添加每个项目。

ConcurrentBag<T>为枚举提供“即时快照”语义;其GetEnumerator方法见备注。当您ConcurrentBag<T>foreach循环访问时,它首先将其整个内容复制到一个普通的List<T>,然后对其进行枚举。每次在循环中使用它时,这都会产生大量的性能开销(计算和内存方面)。

如果您的情况是您的列表将由多个线程填充,但随后仅由一个线程读取,那么您应该List<T>在编写完成后立即将其转换为 a 。


Eiv*_*ver 6

数十亿件物品和 List 或 Concurrent 包?那是“不行”。

就性能而言,试试这个来测试添加:(随意修改它以测试其他操作)

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConcurrentBagTest
{
    // You must compile this for x64 or you will get OutOfMemory exception
    class Program
    {
        static void Main(string[] args)
        {
            ListTest(10000000);
            ListTest(100000000);
            ListTest(1000000000);
            ConcurrentBagTest(10000000);
            ConcurrentBagTest(100000000);

            Console.ReadKey();

        }


        static void ConcurrentBagTest(long count)
        {
            try
            {
                var bag = new ConcurrentBag<long>();
                Console.WriteLine($"--- ConcurrentBagTest count = {count}");
                Console.WriteLine($"I will use {(count * sizeof(long)) / Math.Pow(1024, 2)} MiB of RAM");
                Stopwatch stopwatch = new Stopwatch();
                stopwatch.Start();
                for (long i = 0; i < count; i++)
                {
                    bag.Add(i);
                }
                stopwatch.Stop();
                Console.WriteLine($"Inserted {bag.LongCount()} items in {stopwatch.Elapsed.TotalSeconds} s");
                Console.WriteLine();
                Console.WriteLine();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }

        static void ListTest(long count)
        {
            try
            {
                var list = new List<long>();
                Console.WriteLine($"--- ListTest count = {count}");
                Console.WriteLine($"I will use {(count * sizeof(long)) / Math.Pow(1024, 2)} MiB of RAM");
                Stopwatch stopwatch = new Stopwatch();
                stopwatch.Start();
                for (long i = 0; i < count; i++)
                {
                    list.Add(i);
                }
                stopwatch.Stop();
                Console.WriteLine($"Inserted {list.LongCount()} items in {stopwatch.Elapsed.TotalSeconds} s");
                Console.WriteLine();
                Console.WriteLine();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我的输出:

--- ListTest count = 10000000
I will use 76,2939453125 MiB of RAM
Inserted 10000000 items in 0,0807315 s


--- ListTest count = 100000000
I will use 762,939453125 MiB of RAM
Inserted 100000000 items in 0,7741546 s


--- ListTest count = 1000000000
I will use 7629,39453125 MiB of RAM
System.OutOfMemoryException: Array dimensions exceeded supported range.

--- ConcurrentBagTest count = 10000000
I will use 76,2939453125 MiB of RAM
Inserted 10000000 items in 1,0744069 s


--- ConcurrentBagTest count = 100000000
I will use 762,939453125 MiB of RAM
Inserted 100000000 items in 11,3976436 s
Run Code Online (Sandbox Code Playgroud)

使用 CPU: Intel Core i7-2600 @ 3.4 GHz,

使用内存:16 GB

另请查看此答案以了解限制。