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

C# 如何避免在使用标准集合和存储数百万项时分配额外空间,c#,.net,performance,base-class-library,C#,.net,Performance,Base Class Library,我使用标准集合,如List、HashSet和Dictionary来存储10万个条目,最多可存储100万个条目。特定于用例的是,一开始设置为巨大的项增长非常缓慢,并且在内存中停留很长时间。因此,我面临的问题(尽管LOH分配/碎片化是另一个问题)是,这些集合消耗大量内存,因为它们的内部逻辑每次用完可用空间时都会将分配的内存加倍(实际上,它会加倍并查找最近的素数)。在我的例子中,添加很少,保留所有额外的内存是浪费的 以下是我如何处理列表的简化版本: public static void Add_Gro

我使用标准集合,如List、HashSet和Dictionary来存储10万个条目,最多可存储100万个条目。特定于用例的是,一开始设置为巨大的项增长非常缓慢,并且在内存中停留很长时间。因此,我面临的问题(尽管LOH分配/碎片化是另一个问题)是,这些集合消耗大量内存,因为它们的内部逻辑每次用完可用空间时都会将分配的内存加倍(实际上,它会加倍并查找最近的素数)。在我的例子中,添加很少,保留所有额外的内存是浪费的

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

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);
}
public static void Add\u GrowSlow([NotNull]此列表列表,T项,int growStep)
{
如果(list==null)抛出新的ArgumentNullException(nameof(list));
如果(步骤10000)
{
列表容量=计数+增长步长;
}
}
列表。添加(项目);
}
但是我不知道如何在没有思考的情况下处理HashSet/Dictionary。你能建议一些方法或收集来避免这样的问题吗?我查看了PowerCollections,但没有找到解决此问题的方法

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

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

class Dictionary<TKey, TValue>
{
    // ...
    protected virtual void Resize() {...}
}
类哈希集
{
// ...
受保护的虚拟空间增量容量(){…}
}
类词典
{
// ...
受保护的虚拟void Resize(){…}
}

我不知道为什么他们从一开始就没有在BCL做这件事。虚拟调用的成本与重新分配和复制数据(当集合调整大小时发生)相比是微不足道的。我是否应该创建拉取请求?…:)

看看这篇文章,我会从另一个角度来探讨:你真的需要所有的项目都留在内存中吗?或者更像是你只需要一个特定的工作集就足够长的时间?是否可以将项目数据存储在数据库中并进行计算/排序/选择。。。有了SQL(或者NoSQL),工作集应该留在内存中,因为我们正在对它进行实时计算。问题不在于工作集的大小,而在于我提到的集合。@TomSchardt这篇文章不错,但更多的是关于LOH和数组的。正如我所提到的,List造成的麻烦较少,而且我们不面临LOH碎片问题,但是……使用
List
Dictionary
构造函数的
capacity
参数提前预留足够的空间,而不必加倍。不要增量增长,只要立即吃掉所有的内存
HashSet
没有这样的特性;考虑使用没有值的字典(或者像Bloom Filter这样的高级数据结构,取决于您使用它的原因)。无论内存是否翻倍,成长都是昂贵的,因为东西不可避免地会被复制;如果不够好,可以考虑专用结构,如分组列表。你可能得自己写点东西——坦斯塔夫。