我需要使用非常大的小型数组(int或float数组),我只在具有大量ram的机器上定位X64,物理内存在我的场景中永远不会成为问题。在查看gcAllowVeryLargeObjects的doc时,我注意到了这一点:
•对于字节数组和单字节结构数组,任何单个维度的最大索引为2,147,483,591(0x7FFFFFC7),对于其他类型,最大索引为2,146,435,071(0X7FEFFFFF)。
现在我的问题是我实际上需要"要使用比这更大的阵列,这里适当的解决方法是什么?创建数组或其他抽象数组?
知道我主要需要顺序访问这些数组(从不随机读取,但通常不同的段通过不同的线程顺序读取,可能同时有100多个线程)我最好的选择是什么?
我可能需要保存最多65 536 000 000个或更多元素的数组。
答案 0 :(得分:3)
如果你真的必须打破数组长度限制,那么你必须将数组拆分成适当大小的块。您可以将这些块一起包装在具有适当语义的容器中,例如James McCaffrey在一段时间之前发表博客的BigArrayOfLong对象。还有很多其他人喜欢它。
基本思想是使用锯齿状数组来分配您将要使用的空间。请注意,多维数组不会给你带来任何好处,因为它仍然是一个单独的对象,而锯齿状数组是一个较小的数组数组,每个数组都是它自己的对象(可能不是连续的)。
这是一个非常简单(而非特别优化)的实现:
public class HugeArray<T> : IEnumerable<T>
where T : struct
{
public static int arysize = (Int32.MaxValue >> 4) / Marshal.SizeOf<T>();
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<T> GetEnumerator()
{
return content.SelectMany(c => c).GetEnumerator();
}
IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); }
}
这个是静态分配的,但要使一个增长以适应需求并不太难。只需确保您指定的块大小不会完全超出范围。为了以防万一,我已根据项目大小进行了计算。
答案 1 :(得分:0)
您可以避免使用真实数组并通过流模拟它们。
如果您希望它可以搜索(您这样做),则限制为长(2 ^ 64/2(带符号)位) 然后你只需要索引* n个字节并读取n个字节。
如果使用int32或double(n = 4),则有2,8e +17个位置的空间。
答案 2 :(得分:0)
对于分布式计算来说,这听起来像是一个问题,比如Google Map Reduce。
当它对于您当前的基础架构来说太大时,请将其扩展到更多的框。
答案 3 :(得分:0)
嗯,我很确定你不能拥有一个大小为6500000000的数组,因为它比计算机内存要大(没有操作系统会给软件提供那么多内存。)可能还有其他原因。 如果由于某种原因你相信你可以得到那么多ram但你认为数组很小,你可以尝试使用基于链表的对象(比如堆栈甚至链表本身)。 链接列表不受索引数量的限制(如果它在你的ram范围内)
答案 4 :(得分:0)
我写这个作为解决方案,但希望有人能更好地提供我可以标记为已接受的答案。
一个解决方案,因为限制是在一个数组的维度上,将使用多维数组,并通过计算位置,就像它是一维数组一样简单地在多维数组中索引
//pseudocode
var index = some large number;
var index1 = index/sizeofarrays;
var index2 = index%sizeofarrays;
var data = myverylargemultidimentionalarray[index1,index2];
答案 5 :(得分:0)
我的建议是使用本机代码(即C ++ x64),因为C#不适合循环这么多元素。在尝试将大量数据加载到RAM之前,请仔细考虑从数据中提取所需的信息。
答案 6 :(得分:0)
听起来你应该使用一个流给我。只要在阅读完块后处理块,内存流就应该没问题。
我的猜测是,无论什么填充你的阵列运行速度比消耗它的速度快?如果是这种情况,您可以使用流只是一个缓冲区。当缓冲区达到临界质量时,阻止新条目,同时清除后退日志。听起来你已经有足够的记忆力而不是问题。
您的缓冲区内容可以以块的形式传递给并行库,并保留索引以提供当前索引。
伪代码:
TASK THREAD(为每个算法复制):
如果要在每个任务中利用并行处理,首先流式传输一个对象块,然后将它们作为一个集合与起始索引一起传递给您的方法,这样您仍然可以推导出当前项目索引。