【问题标题】:A workaround for a big multidimensional array (Jagged Array) C#?大型多维数组(锯齿状数组)C# 的解决方法?
【发布时间】:2013-04-17 05:56:35
【问题描述】:

我正在尝试初始化一个三维数组以加载体素世界。

地图的总大小应为 (2048/1024/2048)。我试图初始化一个“int”的锯齿状数组,但我抛出了一个内存异常。尺寸限制是多少? 我的桌子大小:2048 * 1024 * 2048 = 4'191'893'824

有人知道有办法解决这个问题吗?

// System.OutOfMemoryException here !
int[][][] matrice = CreateJaggedArray<int[][][]>(2048,1024,2048);
// if i try normal Initialization I also throws the exception
int[, ,] matrice = new int[2048,1024,2048];

    static T CreateJaggedArray<T>(params int[] lengths)
    {
        return (T)InitializeJaggedArray(typeof(T).GetElementType(), 0, lengths);
    }

    static object InitializeJaggedArray(Type type, int index, int[] lengths)
    {
        Array array = Array.CreateInstance(type, lengths[index]);
        Type elementType = type.GetElementType();

        if (elementType != null)
        {
            for (int i = 0; i < lengths[index]; i++)
            {
                array.SetValue(
                    InitializeJaggedArray(elementType, index + 1, lengths), i);
            }
        }

        return array;
    }

【问题讨论】:

  • 您的CreateJaggedArray 方法不是创建一个锯齿状数组,而是创建一个多维数组,非常具体地不是一个锯齿状数组。
  • @MehdiBugnard 看看变量int[, ,] matrice 的类型即可。如果它是一个锯齿状数组,它需要是int[][][]
  • 如果世界大多是“空白空间”,那么你可以做得比大数组更好。通常的技术是使用八叉树:en.wikipedia.org/wiki/Octree
  • @Filip:确实。此外:由于一个 int 是 4 个字节,天真地表示所有 40 亿个体素将消耗 160 亿字节的 虚拟地址空间。在 32 位进程上,只有 2 GB 的虚拟地址空间可用周期。 (当然,虚拟内存本质上是无限的;请记住,内存可用性受磁盘大小而不是物理内存大小的限制。但是 32 上的 虚拟地址空间非常有限位处理器。)如果结构中有大量冗余,八叉树可以将其压缩到非常易于管理的大小。
  • @MehdiBugnard:我建议您首先不要尝试将数据加载到大数组中。只需将其直接加载到您的八叉树中即可。

标签: c# arrays memory multidimensional-array jagged-arrays


【解决方案1】:

C# 中单个对象的最大大小为 2GB。由于您正在创建一个多维数组而不是锯齿状数组(尽管您的方法的名称),它是一个需要包含所有这些项目的单个对象,而不是多个。如果您实际上使用的是锯齿状数组,那么您将不会有一个包含所有数据的 single 项(即使总内存占用量会更大,而不是更小,只是分散得更多) .

【讨论】:

  • 非常感谢。你知道一种有效的方法来初始化 3D 中的“jaggedArray”,每个案例一个项目吗?
  • @MehdiBugnard 坚持使用良好的 ol' for 循环;每个维度一个...为什么要把事情复杂化。
  • NET 4.5 在 x64 中有一个选项,通过在 app.config 中设置 gcAllowVeryLargeObjects 来明确允许对象大于 2gb。
  • 我认为OP的问题实际上归结为将这样的结构存储在内存中(具有合理的性能)。这也是我想知道的。
【解决方案2】:

来自MSDN documentation on Arrays(已添加重点)

默认情况下,数组的最大大小为 2 GB。在一个 64位环境,可以通过设置 gcAllowVeryLargeObjects 配置元素的启用属性 在运行时环境中为 true。但是,数组仍将是 限制在总共 40 亿个元素,并且最大索引为 任何给定维度的 0X7FEFFFFF(0X7FFFFFC7 用于字节数组和 单字节结构的数组)。

所以尽管有上述答案,即使你设置了标志以允许更大的对象大小,数组仍然被限制在元素数量的 32 位限制。

编辑:您可能需要重新设计以消除对当前使用的多维数组的需求(正如其他人所建议的那样,在使用 actual之间有几种方法可以做到这一点> 锯齿状数组,或其他一些维度集合)。考虑到元素数量的比例,最好使用动态分配对象/内存的设计,而不是必须预先分配它的数组。 (除非您不介意使用许多 GB 的内存) EDITx2:也就是说,也许您可​​以定义定义填充内容的数据结构,而不是定义所有可能的体素世界,甚至是“空”的世界。 (我假设绝大多数体素是“空的”而不是“填充的”)

编辑:虽然不是微不足道的,特别是如果大部分空间被认为是“空的”,那么你最好的选择是引入某种空间树,它可以让你有效地查询你的世界以查看特定对象中的对象区域。例如:Octrees(按照 Eric 的建议)或 RTrees

【讨论】:

  • 您在反驳什么“以上答案”?除您之外的所有答案都表明对象的最大大小为 2 GB。
  • 内存大小限制是最后的钉子。也许我误解了 MSDN 文档,但听起来您可以在 64 位环境中解决最大数组大小,但仍然限于最大数组 元素计数
  • @Servy 对不起,我说错了。我正在寻找一种解决方法来创建一个有许多给定的 3D 表。我编辑了标题
  • 谢谢我刚刚使用了八叉树。我将我的“BigArray”世界切割成 64 个区域,然后计算八叉树。但首先我在这个数组中声明我的世界。
【解决方案3】:

按照描述创建这个对象,无论是作为标准数组还是作为锯齿状数组,都会破坏允许 CPU 发挥性能的引用局部性。我建议你改用这样的结构:

class BigArray 
{
    ArrayCell[,,] arrayCell = new ArrayCell[32,16,32];

    public int this[int i, int j, int k]
    { 
        get { return (arrayCell[i/64, j/64, k/64])[i%64, j%64, k%16]; } 
    }
}


class ArrayCell 
{
    int[,,] cell = new int[64,64,64];

    public int this[int i, int j, int k] 
    { 
        get { return cell[i,j,k]; } 
    }  
}

【讨论】:

  • 谢谢,我会尝试在我的代码中实现这一点,我会返回我的结果。
  • 我只纠正了 2-3 个语法错误。但是他在这一行没有错误-> return (arrayCell[i/64, j/64, k/64])[i%64, j%64, k%16];返回 (arrayCell[i/64, j/64, k/64])[i%64, j%32, k%64]; ?
  • 对不起,我无法理解逻辑“返回 (arrayCell[i/64, j/64, k/64])[i%64, j%64, k%16] ;"。是否可以使用 2048,1024,2048 的数组来修改您的代码
【解决方案4】:

非常感谢所有试图帮助我理解和解决我的问题的工作人员。

我尝试了几种解决方案来加载大量数据并存储在一个表中。 两天后,这是我的测试,最后是解决方案,它可以将 4'191'893'824 条目存储到一个数组中

我添加了我的最终解决方案,希望有人能提供帮助

目标

我记得目标:初始化一个整数数组 [2048/1024/2048] 用于存储 4'191'893'824 数据


测试 1:使用 JaggedArray 方法(失败)


抛出系统内存不足异常

            /* ******************** */
            /* Jagged Array method  */
            /* ******************** */
            
            // allocate the first dimension;
            bigData = new int[2048][][];
            for (int x = 0; x < 2048; x++)
            {
                // allocate the second dimension;
                bigData[x] = new int[1024][];
                for (int y = 0; y < 1024; y++)
                {
                    // the last dimension allocation
                    bigData[x][y] = new int[2048];
                }
            }

测试 2:使用 List 方法(失败)


抛出系统内存不足异常(将大数组分成几个小数组。不幸的是,“列表”允许最大“2GB”内存分配,就像一个简单的数组一样。)

        /* ******************** */
        /* List method          */
        /* ******************** */
        
        List<int[,,]> bigData = new List<int[,,]>(512);
        for (int a = 0; a < 512; a++)
        {
            bigData.Add(new int[256, 128, 256]);
        }
   

测试 3:使用 MemoryMappedFile(解决方案)


我终于找到了解决办法! 使用类“Memory Mapped File”包含虚拟内存中文件的内容。

MemoryMappedFile MSDN 与我在 codeproject here 上找到的自定义类一起使用。初始化很长,但效果很好!

        /* ************************ */
        /* MemoryMappedFile method  */
        /* ************************ */

        string path = AppDomain.CurrentDomain.BaseDirectory;            
        var myList = new GenericMemoryMappedArray<int>(2048L*1024L*2048L, path); 
        using (myList)
        {
            myList.AutoGrow = false;

            /*
            for (int a = 0; a < (2048L * 1024L * 2048L); a++)
            {
                myList[a] = a;
            }
            */

            myList[12456] = 8;
            myList[1939848234] = 1;
            // etc...
        }
     

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-11-13
    • 1970-01-01
    • 2013-02-24
    • 2015-10-18
    • 1970-01-01
    • 2018-11-04
    • 2018-07-26
    相关资源
    最近更新 更多