使用“非常非常”的大型数组

我需要使用非常大的小型数组(int或float数组),我只在具有大量ram的机器上定位X64,物理内存在我的场景中永远不会成为问题。 在查看gcAllowVeryLargeObjects的doc时,我注意到了这一点:

•对于字节数组和单字节结构数组,任何单个维度的最大索引为2,147,483,591(0x7FFFFFC7),对于其他类型,最大索引为2,146,435,071(0X7FEFFFFF)。

现在我的问题是我实际上“需要”使用比这更大的数组,这里适当的解决方法是什么? 创建数组或其他抽象数组?

知道我主要需要顺序访问这些数组(从不随机读取,但通常不同的段通过不同的线程顺序读取,可能一次100多个线程)我最好的选择是什么?

我可能需要保存最多65 536 000 000个元素或更多元素的数组。

如果你真的必须打破数组长度限制,那么你必须将数组拆分成适当大小的块。 您可以将这些块包装在一个具有适当语义的容器中,就像James McCaffrey在一段时间之前发表博客的BigArrayOfLong对象一样。 还有很多其他人喜欢它。

基本思想是使用锯齿状数组来分配您将要使用的空间。 请注意,多维数组不会给你带来任何好处,因为它仍然是一个单独的对象,而锯齿状数组是一个较小的数组数组,每个数组都是它自己的对象(可能不是连续的)。

这是一个非常简单(而不是特别优化)的实现:

public class HugeArray : IEnumerable where T : struct { public static int arysize = (Int32.MaxValue >> 4) / Marshal.SizeOf(); public readonly long Capacity; private readonly T[][] content; public T this[long index] { get { if (index < 0 || index >= Capacity) throw new IndexOutOfRangeException(); int chunk = (int)(index / arysize); int offset = (int)(index % arysize); return content[chunk][offset]; } set { if (index < 0 || index >= Capacity) throw new IndexOutOfRangeException(); int chunk = (int)(index / arysize); int offset = (int)(index % arysize); content[chunk][offset] = value; } } public HugeArray(long capacity) { Capacity = capacity; int nChunks = (int)(capacity / arysize); int nRemainder = (int)(capacity % arysize); if (nRemainder == 0) content = new T[nChunks][]; else content = new T[nChunks + 1][]; for (int i = 0; i < nChunks; i++) content[i] = new T[arysize]; if (nRemainder > 0) content[content.Length - 1] = new T[nRemainder]; } public IEnumerator GetEnumerator() { return content.SelectMany(c => c).GetEnumerator(); } IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } } 

这个是静态分配的,但要制作一个适合需求的增长并不是很难。 只需确保您指定的块大小不会完全超出范围。 为了以防万一,我已经根据项目大小进行了计算。

您可以避免使用真实数组并通过流模拟它们。

如果你想要它(你做的),你只能使用long(2 ^ 64/2(有符号)位)然后你只需要索引* n个字节并读取n个字节。

如果使用int32或double(n = 4),则有2,8e +17个位置的空间。

对于分布式计算来说,这听起来像是一个问题,比如Google Map Reduce。

如果它对于您当前的基础架构来说太大,请将其扩展到更多的盒子。

好吧,我很确定你不能拥有一个大小为6500000000的数组,因为它通常比计算机内存大(没有操作系统会给软件提供那么多内存。)可能还有其他原因。 如果由于某种原因你认为你可以得到那么多ram但你认为数组很小,你可以尝试使用基于链表的对象(比如堆栈甚至链表本身)。 链接列表不受索引数量的限制(如果它在你的ram范围内)

我写这是一个解决方案,但希望有人能更好地提供我可以标记为已接受的答案。

一个解决方案,因为限制是在一个数组的维度上将是使用多维数组并简单地在多维数组中索引通过计算位置就好像它是一维数组

 //pseudocode var index = some large number; var index1 = index/sizeofarrays; var index2 = index%sizeofarrays; var data = myverylargemultidimentionalarray[index1,index2]; 

我的建议是使用本机代码(即C ++ x64),因为C#不适合循环这么多的元素。 在尝试将大量数据加载到RAM之前,请仔细考虑从数据中提取所需的信息。

听起来你应该使用流向我。 只要在阅读完块后处理块,内存流就应该没问题。

我的猜测是,无论填充什么arrays的运行方式都比消耗它的速度快? 如果是这种情况,您可以使用流只是一个缓冲区。 当缓冲区达到临界质量时,阻止新条目,同时清除后退日志。 听起来你已经有足够的记忆力而不是问题。

您的缓冲区内容可以以块的forms传递给并行库,并保留索引以提供当前索引。

伪代码是:

  • 接收新项目并添加到超级内存流(内存将复制到页面文件,所以如果你还有疯狂的磁盘数量,RAM甚至更少的问题!)

TASK THREAD(为每个算法复制):

  • 缓冲区有项目
  • 从缓冲区读取对象
  • 过程对象

如果要在每个任务中利用并行处理,首先流式传输一个对象块,然后将它们作为一个集合与一个起始索引一起传递给您的方法,这样您仍然可以推导出当前项目索引。