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

Ron*_*dau 3 .net c# .net-4.5

我需要处理非常大的小类型数组(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

Cor*_*rey 5

如果您确实必须打破数组长度限制,那么您必须将数组拆分为合适大小的块。您可以将这些块包装在具有适当语义的容器中,例如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)

这是静态分配的,但制作一个可以根据需求进行增长的分配并不是太难。只需确保您指定的块大小没有完全超出范围即可。为了以防万一,我根据物品尺寸进行了计算。