如何高效地管理大量数据(高度数据)并替换这个巨大的数组?

5

我需要能够快速查找这些数据,并且需要访问所有的数据。不幸的是,我还需要节省内存(多个数据可能会导致OutofMemoryExceptions)。

short[,,] data = new short[8000,8000,2];

我已经尝试过以下方法:
  • 尝试了不规则数组 - 仍然遇到了内存问题
  • 尝试将其分成更小的数组 - 仍然会出现内存问题
  • 唯一的解决方法是使用内存映射文件有效地映射这些数据,还是有其他方法可以做到这一点?

3
new short[8000,8000,2] 的大小只有 256,000,000 字节。你确定内存中没有其他对象导致这些异常吗? - Jeffrey L Whitledge
你是正确的 - 还有其他事情正在发生 - 这仅仅是一个累积问题,而且对我来说似乎是最大的罪魁祸首,所以我想从这里开始。 - Darren
1
该数组中的数据是否稀疏?(即,您可能不需要在256,000,000个位置中大多数位置存储值?) - Michael Petito
5个回答

6

那么一个数据库怎么样?毕竟它们就是为此而生的。

我建议您看一些NoSQL数据库。根据您的需求,还有可以复制部署或链接到应用程序的内存数据库[显然也可能遇到同样的内存问题]和数据库。

我不想手动处理存储细节,而内存映射文件正是某些数据库(至少是MongoDB)在内部正在执行的操作。因此,实质上,您将会编写自己的数据库,并且编写数据库并不是一件简单的事情--即使您缩小了使用案例。

Redis或Membase听起来是解决您问题的合适选择。就我所知,两者都能够为您管理RAM利用率,即按需从磁盘读取数据并在RAM中缓存数据以实现快速访问。当然,您的访问模式将在此处发挥作用。

请记住,建立这些数据库需要付出大量的努力。根据维基百科,Zynga正在使用MembaseRedis由VMWare赞助。


1
然而,通用数据库的速度将比数组索引慢几个数量级。如果这些数据正在(接近)实时处理中,或者处理需要快速访问数组中的任何元素,则可能不希望使用数据库。 - Michael Petito
1
好的,请建议一个内存数据库,它可以比单个数组查找(或10个操作或100个操作)更快地回答查询。不要忘记一般数据库处理结构化查询,并且仅比较字符串涉及多个数组查找。 - Michael Petito
1
迈克尔,楼主甚至没有说终极性能是关键要求。即使是这样:如果它不适合内存,那就不适合内存,对吧?你是想告诉我们,一整个数据库软件类型都没有存在的权利吗? - mnemosyn
1
@mnemosyn:不,我喜欢数据库,而且你是对的,如果它不适合 RAM,那就不适合。但是数据库并不能解决所有问题。即使您的数据库可以在100微秒内回答每个查询,也需要超过20分钟来查询示例中的每个元素。速度不太快。如果数据正在经过一些数字分析,则访问次数可能会更高。 - Michael Petito
1
@mnemosyn:如果您可以分批处理数据,那么我认为OP永远不会在内存中拥有如此大的数组。这个例子让我觉得需要对整个数据集进行随机(也许是重复的)访问以完成数据处理工作。 - Michael Petito
显示剩余12条评论

1

你确定你需要一直访问所有数据吗?或者你可以只加载部分数据,进行处理,然后再转到下一个部分吗?

如果只是高度数据,你能否使用 mip-mapping 或 LoD 表示来减少数据量?这两种方法都可以让你保留低分辨率的数据,直到你需要加载更高分辨率的数据块。

你的机器上有多少可用内存?你使用的操作系统是什么?是 64 位的吗?

如果你正在进行内存/处理密集型操作,是否考虑将这些部分实现为 C++,以便更好地控制这些事情?

如果不知道你的系统的一些具体信息和你正在处理的数据是什么,我们很难提供更进一步的帮助...?


无论操作系统如何,你的限制是700-900MB - 你可以用一个简单的测试应用程序来验证。我不确定我是否能获得MIP-Mapping或LOD表示 - 这不是为了可视化 - 它是为了两个目的 - 视线计算,所以我需要它精确,并检查实体在该位置的移动性基于地面类型。 - Darren

0

如果您正在使用此数据进行数字计算,我不建议使用传统的关系型数据库。我怀疑您在这里遇到的问题并不是数据本身的大小,而是 .NET 中已知的一个问题,称为大对象堆碎片。如果您经常分配这些缓冲区后遇到问题(即使它们应该被垃圾回收),那么这很可能是罪魁祸首。您最好的解决方案是预先分配尽可能多的缓冲区并重复使用它们,以防止重新分配和随后的碎片化。


在这种情况下,锁定对象堆(LOH)的碎片问题是否仍然会影响锯齿状数组? - Michael Petito
@Michael,根据链接的文章,放置到LOH中的截止点是~10,000个元素,所以这将取决于如何构造交错数组。假设您没有在最后一个维度上使用交错数组(对于仅有2个元素来说是浪费),则在给定示例中,您将拥有8,000个包含16,000个元素的数组,这些数组都将分配在LOH上。它可以被重构为每个包含8,000个元素的16,000个数组,这可能不在LOH上。 - Dan Bryant
是的,非常接近了...但我认为一个包含16,000个shorts的数组不会进入LOH,因为它只有32K。 10K元素指南可能源于每个元素的8字节指针的成本,同一篇文章引用了85K的LOH阈值。 - Michael Petito

0

你是如何与这个大型多维数组进行交互的?你使用递归吗?如果是的话,请确保你的递归方法是通过引用传递参数,而不是通过值传递。

顺便说一下,你是否需要同时访问100%的数据?处理大量数据的最佳方式通常是通过流或某种读取器对象。尝试按段处理数据。我有一些处理几十GB数据的过程,由于我是通过SqlDataReader以流的方式读取数据,所以它可以在很少的内存中处理。

简而言之:看看你如何在函数调用之间传递数据(O(ref)),也许可以使用流式处理模式来处理较小的数据块。

希望对你有所帮助!


0

.NET将shorts存储为32位值,即使它们只包含16位。因此,您可以通过使用int数组并使用位操作将int解码为两个shorts来节省一倍的空间。

然后,您几乎拥有了存储此类数组的最有效方法。然后您可以:

  1. 使用64位机器。然后,您可以分配大量内存,如果您的RAM用完,操作系统会自动将数据分页到磁盘上(确保您有足够大的交换文件)。然后,您可以使用8 TERA字节的数据(如果您有足够大的磁盘)。

  2. 手动使用文件IO或使用内存映射从磁盘中读取需要的数据部分。


你有没有在现实生活中尝试过在一台合理的机器上分配数TB的数据?仅仅19GB,我的机器似乎几乎停止了(8GB RAM,256GB SSD)。一个天真的分配在一个巨大的数组中甚至不能在我的机器上分配一个1GB的数组。 - mnemosyn
你是如何分配1GB的数组的?我可以分配一个2GB的数组,这是.NET的限制。但是我可以分配几个2GB的数组。请注意,他的数组只有256MB,或者按照我的建议使用128MB。当然,实际使用那些内存会比访问RAM慢得多,因为虚拟内存并不能像魔术一样给你快速的TB级RAM,它只能给你像RAM一样的TB级磁盘存储。但是如果数据无法放入内存中,你该怎么办呢?使用虚拟内存系统要比通过数据库更快。 - Jules
还有,为什么你会因为我认为你的答案不是正确的方法,因为它比使用虚拟内存系统更慢、更费力而对我的回答进行负评?这不是stackoverflow应该运作的方式。 - Jules
再说一遍:你真的做过吗?我写过几个内存要求高的科学应用程序,我知道它们所涉及的问题。你的陈述简直是误导性的 - 在大多数情况下不起作用。此外,如果你分割数组,你还必须管理哪些数据驻留在哪个数组中。阅读有关IMDB的文章,你会发现这些东西确实有存在的理由,而且事情并不像你想象的那么简单。 - mnemosyn
是的,我有。实际上,我今天刚在处理这个问题。我实际上存储的是大量的浮点数数组而不是shorts,但问题是一样的。再次问一下,你是如何分配你的1GB数组的?除非你做错了什么(比如分配了一个4GB的数组),否则你应该能够在.NET中分配它。我也使用过内存数据库。它们对许多事情都很好用。存储大量的数组不是其中之一,因为这正是操作系统虚拟内存系统的使用场景,而不是数据库的使用场景。此外,他不必拆分他的数组,因为他的数组本来就比2GB小。 - Jules

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接