如何在使用标准集合和存储数百万个项目时避免额外的空间分配

时间:2017-09-06 07:10:31

标签: c# .net performance base-class-library

我正在使用List<等标准集合。 T>,HashSet< T>和词典< TKey,TValue>存储100万件物品,高达百万件。特定于用例的是,在开始时设置为巨大的项目变得非常慢并且在内存中保留很长时间。所以我面临的问题(尽管LOH分配/碎片是另一个问题)是这些集合消耗了大量内存,因为它们的内部逻辑在每次用完可用空间时将分配的内存加倍(实际上它是双倍和看起来最近的素数)。在我的情况下,添加很少,保留所有额外的内存是浪费。

以下是我如何处理List的简化版本:

public static void Add_GrowSlow<T>([NotNull] this List<T> list, T item, int growStep)
{
    if (list == null) throw new ArgumentNullException(nameof(list));
    if (growStep <= 0)
        throw new ArgumentOutOfRangeException(nameof(growStep));

    var count = list.Count;
    if (list.Capacity == count)
    {
        if (count > 10000)
        {
            list.Capacity = count + growStep;
        }
    }

    list.Add(item);
}

但我不知道如何在没有反射的情况下处理HashSet / Dictionary。您能否建议任何方式或收集以避免此类问题?我查看了PowerCollections,但没有找到解决此问题的方法。

更新:我想澄清一下我想得到什么样的答案:nuget包的名称或链接到带有源代码的文章,其中集合可以控制它们的增长方式,实施。因为我的问题的明显解决方案是从BCL复制源代码并使那些现有方法受到虚拟保护:

class HashSet<T>
{
    // ...
    protected virtual void IncreaseCapacity() {...}
}

class Dictionary<TKey, TValue>
{
    // ...
    protected virtual void Resize() {...}
}

我不知道为什么他们从一开始就没有在BCL这样做。与重新分配和复制数据相比,虚拟调用的成本无关紧要。可能是我应该创建拉取请求?... :)

0 个答案:

没有答案