1

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

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 个对象(我可能会超过或低于,不确定)字典会使用大量内存来调整其中已有的许多键/值对的大小。这是一个好的解决方案还是我最好不要使用初始容量对其进行初始化。谢谢。

编辑:相关但不相同 - .NET 通用字典是否应该初始化为容量等于它将包含的项目数?

4

4 回答 4

4

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

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

        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 =)

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

于 2012-08-10T19:20:55.930 回答
3

不要担心小东西。像这样的字典不会使用大量内存,因此让它自行调整大小也不会占用大量内存。真正的存储是键和数据的对象,字典只包含对它们的引用。在 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 个桶,远远超出您的最大估计。调整大小也不是很昂贵,几微秒。这就是为什么安德烈看不出有什么不同。

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

于 2012-08-10T20:12:40.913 回答
1

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

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

内存将为您估计的项目数量保留 - 这将发生在Dictionary.

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

于 2012-08-10T19:17:23.147 回答
1

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

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

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

于 2016-01-04T18:15:25.233 回答