具有初始容量的字典

时间:2012-08-10 19:09:31

标签: .net vb.net

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

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?

4 个答案:

答案 0 :(得分:3)

这是一个很好的问题。我没有搜索过,但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 =)

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

答案 1 :(得分:3)

不要担心小东西。像这样的字典不会占用大量内存,因此调整大小本身也不会占用大量内存。真实存储是密钥和数据的对象,字典只包含对它们的引用。在32位模式下每个条目8个字节,因此只有4000 x 8 +一些开销= 32千字节。

此外,您传递的容量用于计算字典中的散列桶数。其总是等于或大于您指定的容量的素数。从该数组中挑选素数(从参考源复制):

    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个桶,远远超过了您的最大估计值。调整大小也不是很昂贵,几微秒。这就是安德烈看不出差异的原因。

除了推理之外,还有正确的方法。测量。任何人都可以衡量。

答案 2 :(得分:1)

  

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

如果您超过估算的容量,字典只会“调整大小”。

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

现在,容量与实际尺寸之间存在差异。容量是字典可以保留的项目数,而不会调整内部存储的大小。大小是字典中存储的实际项目的数量(即添加到其中的项目)。

答案 3 :(得分:-1)

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

在这种情况下,内存重新分配可以生成&#34;(小)空洞&#34;在内存空间,这可能导致内存碎片。有一段时间即使内存仍然很大,因为碎片太多,那么你的应用程序可能会遇到“内存不足”的问题。条件。

这个观察结果是真实的.Net 4.5.1我相信是我上次测试/观察到这一点。如果较新的框架版本具有更好的垃圾收集器,其中内存压缩正在以合适的频率进行,因此,减轻碎片问题或使其成为次要的事情,那么它将无关紧要。