编译器在初始化大型std::arrays时卡住了。

7

我需要初始化一个非常大的多维 std::array 数据:

class Thing;

class World
{
public:
    World() : space{nullptr} {};
    ~World() = default;
private:
    static unsigned int const size = 1000;
    std::array<std::array<std::array<std::unique_ptr<Thing>, size>, size>, size> space;
};

如果您尝试实例化此内容,G++ 4.8.2会崩溃:它会消耗所有可用的内存并且不会返回。也就是说,编译器会挂起,我永远得不到可执行文件。为什么会这样呢?请注意,clang++没有任何问题。
注意:我完全意识到将如此多的数据放在堆栈上可能会导致堆栈溢出。最佳的初始化方式是什么?我认为将space作为引用(指向已分配的内存)是最好的方法,但我无法弄清楚语法。

1
如果将其实例化为简单的 World world;,则会消耗略多于 1000^3 * sizeof(std::unique_ptr<>) 字节。在 64 位系统上,这将是至少 7.629 GB。所以,是的,我会说你已经超过了自动变量空间限制的界限。我非常想知道这个问题的解决方案是什么。 - WhozCraig
1
我刚刚尝试编译了这个代码(将Thing更改为int),并且它成功编译了。你能否给出一个小例子来证明编译器失败了? - FDinoff
3
希望@WhozCraig没有在本地运行,希望我学校的任何人都没有在使用这台机器。 刚刚收到以下错误信息: g++:内部编译器错误:已终止(程序cc1plus) 请提交完整的错误报告,如果适当,请附加预处理源代码。 有关说明,请参见http://bugzilla.redhat.com/bugzilla。 该计算机使用了全部的24GB内存。 - FDinoff
1
@FDinoff 是的,我认为如果他将最外层部分向量化,它可能在中间阶段对他有用,但我对此感到有些不确定,因为它有点像把问题推到了后面。如果数字足够大,同样的问题可能会再次出现。 - WhozCraig
3
这似乎是 GCC Bug #59659:编译器通过直接初始化每个元素生成巨大的初始化器代码,从而为大型std::array生成初始化器代码。 - Casey
显示剩余15条评论
3个回答

2

从你使用unique_ptr来看,你似乎在寻找一种稀疏的三维矩阵类型。要实现稀疏矩阵,你��以查看如何在C++中创建稀疏数组?,作为一个实现细节,你可以使用Boost Multi-Index 来实现对所有维度的快速访问。


2

好的,我无法解释为什么g++对此感到困扰,但在你解决这个问题之前,请考虑以下成员声明:

std::vector<std::array<std::array<std::unique_ptr<Thing>,size>,size>> space;

在构造函数的初始化列表中:

World() : space{size}

那应该至少能让你编译并将所有内容移动到堆中。注意:这最好是一个64位进程。我将不得不搜寻以找出为什么g++正在做我所怀疑的事情。


1
这个编译时使用了 size = 4000000000 ,不到一秒钟就完成了。 - FDinoff
我感觉可以通过将空间作为3D数组构造函数的unique_ptr,并使用适当的new调用进行初始化(并相应地修改所有用法以进行解引用)来完成此操作,但我还无法理清构造函数的语法。不过,无论如何,这个方法是有效的。 - George Hilliard
只要你有东西可以使用,@thirtythreeforty。你当然可以在外部使用std::unique_ptr<>并自己分配内存,但如果你需要在运行时进行操作,你可以通过World成员保留和插入所需的内容,并访问向量来完成相同的操作。至少这样可以将你放在堆上并脱离自动存储。我会尝试一些错误调试,看看是否已经有人报告了这个问题。很高兴你有一些临时的东西可以使用。希望你也能为FDinoff的工作提供支持。他做了很多工作。 - WhozCraig
@thirtythreeforty 或 WhozCraig,你们两个在 GNU Bug Tracker 上有账户并愿意提交错误报告吗?还是我自己去做? - FDinoff
@thirtythreeforty,实际上我不会,所以你们其中一个必须去做。对此造成的不便很抱歉。但是嘿,是找到了它 =P - WhozCraig
显示剩余2条评论

0
vector<vector<vector<unique_ptr<Thing>>>> space;

而当初始化时:

for (int i = 0; i < 1000; i++)
{
   vector<vector<unique_ptr<Thing>>> sp1;
   for (int j = 0; j < 1000; j++)
   {
      vector<unique_ptr<Thing>> sp2;
      for (int k = 0; k < 1000; k++)
         sp2.push_back(make_unique<Thing>("params", "to", "construct", "thing"));

       sp1.push_back(move(sp2));
   }

   space.push_back(move(sp1));
}

这种方法与您的类似,但它是在堆中构造向量,而不是在栈中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接