【问题标题】:Dictionary with initial capacity具有初始容量的字典
【发布时间】:2012-08-10 19:09:31
【问题描述】:

我读到如果可以估计大小,用初始容量初始化字典可能会带来更好的性能。

Dim MyDataTable As New DataTable

'Fill MyDataTable from Database

Dim CapacityEstimate As integer = MyDataTable.Rows.Count
Dim MyDictionary As New Dictionary(Of String, MyObjectType)(CapacityEstimate)

'Fill the Dictionary independent of data table

CapacityEstimate 变量只是字典应包含的键/值对数量的估计值(通常在 2500 到 7000 的范围内)。因此,如果我估计它是 4000 并最终得到 4010 个对象(我可能会超过或低于,不确定)字典会使用大量内存来调整其中已有的许多键/值对的大小。这是一个好的解决方案还是我最好不要使用初始容量对其进行初始化。谢谢。

编辑:相关但不相同 - Should a .NET generic dictionary be initialised with a capacity equal to the number of items it will contain?

【问题讨论】:

    标签: .net vb.net


    【解决方案1】:

    这是个好问题。我没有四处搜索,但 Oded 的回答对我来说似乎不错。

    但是,让我们在其上运行一个概念性的微基准测试:

            Dictionary<string, int> FixedCapacity = new Dictionary<string, int>(4000);
            Dictionary<string, int> NotFixedCapacity = new Dictionary<string, int>();
    
            Stopwatch stopWatch = new Stopwatch();
    
            stopWatch.Start();
    
            for (int i = 0; i < 5000; i++)
            {
                FixedCapacity.Add(i.ToString(), i);
            }
    
            stopWatch.Stop();
    
            Console.WriteLine(string.Format("Fixed initial capacity: {0} ms", stopWatch.ElapsedMilliseconds));
    
            stopWatch.Reset();
    
            stopWatch.Start();
    
            for (int i = 0; i < 5000; i++)
            {
                NotFixedCapacity.Add(i.ToString(), i);
            }
    
            stopWatch.Stop();
    
            Console.WriteLine(string.Format("Not Fixed initial capacity: {0} ms", stopWatch.ElapsedMilliseconds));
    
            Console.ReadLine();
    

    结果:

    Fixed initial capacity: 1ms
    Not Fixed initial capacity: 1ms
    

    这是另一个很好的答案,IMO =)

    免责声明:不,这不是一个完整的基准测试程序,我只是在一台机器上测量框架的“默认”行为。我已经手动运行了几次并得到了相同的结果,即使它不在循环中。如果您有更好的基准测试工具,请对其进行测试并将结果粘贴到此处。

    【讨论】:

    • @pst 我相信对于概念性步骤来说已经足够了。然而,你有什么建议?
    【解决方案2】:

    不要担心小事。像这样的字典不会使用大量内存,因此让它自行调整大小也不会占用大量内存。真正的存储是键和数据的对象,字典只包含对它们的引用。在 32 位模式下,每个条目 8 个字节,因此只有 4000 x 8 + 一些开销 = 32 KB。

    此外,您传递的容量用于计算字典中哈希桶的数量。它始终是等于或大于您指定的容量的素数。从这个数组中挑选素数(从参考源复制):

        internal static readonly int[] primes = {
            3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, 293, 353, 431, 521, 631, 761, 919,
            1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591,
            17519, 21023, 25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523, 108631, 130363, 156437,
            187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263,
            1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559, 5999471, 7199369};
    

    因此,如果您通过 4000,那么您实际上将获得 4049 个桶,这是下一个最大的素数。因此,超过 4010 不会产生影响。如果它确实需要调整大小,那么它的容量会加倍。因此,单次调整大小已经产生 8419 个桶,远远超出您的最大估计。调整大小也不是很昂贵,几微秒。这就是安德烈看不出区别的原因。

    除了推理之外,这是正确的方法。措施。任何人都可以测量。

    【讨论】:

      【解决方案3】:

      字典是否会使用大量内存来调整其中已有的许多键/值对的大小

      只有在您超过估计的容量时,字典才会“调整大小”。

      内存将为您估计的项目数量保留 - 这将发生在 Dictionary 的构造函数中。

      现在,容量和实际大小之间存在差异。容量是在不调整内部存储大小的情况下字典可以容纳多少项目。大小是存储在字典中实际的项目数(即添加到其中的项目数)。

      【讨论】:

      • 我在哪里暗示实际大小和容量是相同的
      • @user1556110 - 我的答案不只是为你自己 - 它适用于所有阅读问题的人。
      【解决方案4】:

      我知道这可能会迟到,但无论如何,这对任何阅读本文的人来说可能仍然有价值。最好将容量设置为某个已知值的原因是为了防止重新分配。在高度繁忙的 24x7 服务/应用程序中,内存利用率是全面/极端条件,您可能希望通过将容量设置为已知大小或某个平均/估计大小来防止内存重新分配来避免增加额外压力。

      这种情况下的内存重新分配会在内存空间中产生“(小)洞”,从而导致内存碎片。有时即使内存仍然很大,但由于碎片过多,您的应用可能会遇到“内存不足”的情况。

      这个观察结果在 .Net 4.5.1 之前是正确的,我相信这是我最后一次测试/观察到这一点的时候。如果较新的框架版本具有更好的垃圾收集器,其中内存压缩以适当的频率完成,从而减轻碎片问题或使这成为一件小事,那么这并不重要。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-02-15
        • 1970-01-01
        • 2013-09-13
        • 1970-01-01
        • 2010-09-23
        • 2013-11-14
        • 2023-03-24
        相关资源
        最近更新 更多