我需要处理非常大的小类型数组(int 或 float 数组),我仅在具有大量 RAM 的计算机上针对 X64,物理内存在我的场景中从来不是问题。在查看 gcAllowVeryLargeObjects 的文档时,我注意到了这一点:
\n\n\xe2\x80\xa2 对于字节数组和单字节结构数组,任何单一维度的最大索引为 2,147,483,591 (0x7FFFFFC7),对于其他类型为 2,146,435,071 (0X7FEFFFFF)。
\n\n现在我的问题是我实际上“需要”使用比这更大的数组,这里合适的解决方法是什么?创建数组的数组或其他抽象?
\n\n知道我主要需要顺序访问这些数组(从不随机读取,但通常不同的段由不同的线程顺序读取,可能一次超过 100 个线程)我最好的选择是什么?
\n\n我可能需要保存多达 65 536 000 000 个或更多元素的数组。
\n如果您确实必须打破数组长度限制,那么您必须将数组拆分为合适大小的块。您可以将这些块包装在具有适当语义的容器中,例如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(); }
}
Run Code Online (Sandbox Code Playgroud)
这是静态分配的,但制作一个可以根据需求进行增长的分配并不是太难。只需确保您指定的块大小没有完全超出范围即可。为了以防万一,我根据物品尺寸进行了计算。