【问题标题】:Dealing with very large Lists on x86在 x86 上处理非常大的列表
【发布时间】:2015-07-18 00:10:41
【问题描述】:

我需要处理大量浮点数,但我在 x86 系统上遇到了内存限制。我不知道最终长度,所以我需要使用可扩展类型。在 x64 系统上,我可以使用 <gcAllowVeryLargeObjects>

我当前的数据类型:

List<RawData> param1 = new List<RawData>();
List<RawData> param2 = new List<RawData>();
List<RawData> param3 = new List<RawData>();

public class RawData
{
    public string name;
    public List<float> data;
}

paramN 列表的长度很短(当前为 50 或更低),但数据可以是 10m+。当长度为 50 时,我在 1m 数据点以上达到内存限制 (OutOfMemoryException),当长度为 25 时,我在 2m 数据点以上达到限制。 (如果我的计算是正确的,那就是 200MB,加上名称的大小,加上开销)。我可以使用什么来提高此限制?

编辑:我尝试使用最大内部列表大小为 1 List<List<float>>,这在一定程度上增加了限制,但仍然没有达到我想要的程度。 p>

Edit2:我尝试将 List> 中的块大小减小到 8192,我得到了大约 2.3m 元素的 OOM,任务管理器读取了大约 1.4GB 的进程。看起来我需要减少数据源和存储之间的内存使用量,或者更频繁地触发 GC - 我能够在具有 4GB RAM 的 PC 上的 x64 进程中收集 10m 数据点,IIRC 该进程从未超过 3GB

Edit3:我将我的代码压缩为仅处理数据的部分。 http://pastebin.com/maYckk84

Edit4:我查看了 DotMemory,发现我的数据结构确实占用了我正在测试的设置大约 1GB(50ch * 3 参数 * 2m 事件 = 300,000,000 个浮点元素) .我想我需要在 x86 上限制它,或者在我获取数据时弄清楚如何以这种格式写入磁盘

【问题讨论】:

  • 你的问题是......?
  • hitting memory limits 就像得到OutOfMemoryException 之类的东西?而 200MB 是您期望每个 List 中的每个项目占用多少?
  • 列表容量调整算法在达到限制时将持有数组的大小加倍。这可以证明是非常低效的。是否可以预测您的任何列表的最终长度,从而在施工中提供容量?如果您的任何列表在没有修改的情况下停留了相当长的时间,您应该考虑使用.TrimExcess(),但要注意.TrimExcess 之后的单个添加会导致容量翻倍。
  • 您需要使用一种数据结构,将其数据存储在非纯连续数据中(List&lt;T&gt; 在内部使用数组)。您可能希望创建一个自定义数据结构,在现有数组填满时创建新数组,将它们像链表一样菊花链。 .Net StringBuilder 从 .Net 4.0 或 4.5 开始执行此操作,因此您可以查看其源代码作为示例。
  • 您的代码示例没有充分说明确切的问题。通常,您可能会在内存不足之前达到List&lt;T&gt; 的大小限制,您可以通过创建数据结构来解决此问题,例如List&lt;List&lt;T&gt;&gt;(即列表列表)。但是在 x86 上,您将始终被严格限制在相对较小的数据量(3GB 是理论上的最大值,但实际上实际限制可以低至 1.2-1.4GB)。如果您想要一个实际的答案,请提供a good, minimal, complete code example 可靠地重现问题。

标签: c# .net winforms


【解决方案1】:

首先,在 x86 系统上,内存限制是 2GB,而不是 200MB。我相信 你的问题比这更棘手。您有激进的 LOH(大对象堆)碎片。
CLR 对小型和大型对象使用不同的堆。如果对象的大小大于 85,000 字节,则该对象很大。 LOH 是一个非常棘手的东西,它不急于将未使用的内存返回给操作系统,并且在碎片整理方面很差。
.Net List 是 ArrayList 数据结构的实现,它将元素存储在数组中,数组的大小是固定的;当数组被填充时,将创建两倍大小的新数组。阵列随着数据量的持续增长是 LOH 的“饥饿”场景。
因此,您必须使用量身定制的数据结构来满足您的需求。例如。块列表,每个块都足够小,不会进入 LOH。这是小原型:

public class ChunkedList
{
    private readonly List<float[]> _chunks = new List<float[]>();
    private const int ChunkSize = 8000;
    private int _count = 0;       

    public void Add(float item)
    {            
        int chunk = _count / ChunkSize;
        int ind = _count % ChunkSize;
        if (ind == 0)
        {
            _chunks.Add(new float[ChunkSize]);
        }
        _chunks[chunk][ind] = item;
        _count ++;
    }

    public float this[int index]
    {
        get
        {
            if(index <0 || index >= _count) throw new IndexOutOfRangeException();
            int chunk = index / ChunkSize;
            int ind = index % ChunkSize;
            return _chunks[chunk][ind];
        }
        set
        {
            if(index <0 || index >= _count) throw new IndexOutOfRangeException();
            int chunk = index / ChunkSize;
            int ind = index % ChunkSize;
            _chunks[chunk][ind] = value;
        }
    }
    //other code you require
}

ChunkSize = 8000 每个块只需要 32,000 字节,所以它不会进入 LOH。 _chunks 只有在收集大约 16,000 个块时才会进入 LOH,即收集中的元素超过 1.28 亿个(约 500 MB)。

UPD 我对上面的示例进行了一些压力测试。操作系统为 x64,解决方案平台为 x86。 ChunkSize 为 20000。

第一:

var list = new ChunkedList();
for (int i = 0; ; i++)
{
    list.Add(0.1f);
}

OutOfMemoryException 在 ~324,000,000 个元素处引发

第二:

public class RawData
{
    public string Name;
    public ChunkedList Data = new ChunkedList();
}

var list = new List<RawData>();
for (int i = 0;; i++)
{
    var raw = new RawData { Name = "Test" + i };
    for (int j = 0; j < 20 * 1000 * 1000; j++)
    {
        raw.Data.Add(0.1f);
    }
    list.Add(raw);
}

OutOfMemoryException 在 i=17, j~12,000,000 时引发。成功创建 17 个 RawData 实例,每个实例 2000 万个数据点,总计约 3.52 亿个数据点。

【讨论】:

  • @HungPV,这只是一个展示如何组织内存的原型。无论如何,实现可编辑性肯定会很痛苦(想象一下在 100 万个元素的集合中删除项目,删除 1 个操作,移动元素的 500k 操作;LinkedList 不利于内存占用和局部性;可能是复杂的多级系统分块和错位图是一种方式)。但是,我认为 OP 不需要编辑集合,它似乎是用于数值分析的原始数据的简单存储
  • @MalikDrako,注意块是float[ ],而不是浮动。在 List 的标准实现中没有块,只有不断增长的数组的初始容量。我已经对上面的实现进行了压力测试,并且在 ChunkSize = 20000 的情况下,我在 x86 配置下以 ~324m 个元素接收了 OOM。可以尝试使用上面的 ChunkedList 实现将 RawData 中的 List&lt;float&gt; data 切换为 ChunkedList data 吗?
  • @MalikDrako,下一个候选人是其他收藏。 pendingDataRcv 有多大?此外,如果行为是可重现的,您可以在 OOM 之前暂停程序执行,并使用内存分析器(例如 jetbrains.com/dotmemory)查看堆结构以及哪些对象保留内存。
  • @MalikDrako,所以找到了瓶颈。我建议您扩展 RawData,使其能够将浮点数据保存到临时文件并重新加载 - 在这种情况下,您可以简单地使用 BinaryReader/BinaryWriter。除此之外,这个案子似乎已经结案了:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-02
  • 2012-07-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-03
相关资源
最近更新 更多