【问题标题】:Compiler hang when initializing large std::arrays初始化大型 std::arrays 时编译器挂起
【发布时间】:2023-11-21 09:33:01
【问题描述】:

我需要初始化一个非常大的多维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 成为一个引用(对分配的内存)是最好的方式,但我无法弄清楚语法。 p>

【问题讨论】:

  • 如果它被简单地实例化为World world;,那么您消耗的字节数略多于1000^3 * sizeof(std::unique_ptr&lt;&gt;)。在 64 位系统上,最低 7.629 GB。所以是的,我会说你越过了自动可变空间限制的界限。我渴望知道这是要解决的问题。
  • 我刚刚尝试编译这个(将事物更改为 int)并编译。你能举一个小例子来说明编译器失败了吗?
  • @WhozCraig 没有在本地运行,希望我学校里没有人使用这台机器做任何事情。刚刚得到这个 g++: internal compiler error: Killed (program cc1plus) 请提交一个完整的错误报告,如果合适的话,带有预处理的源代码。有关说明,请参阅 bugzilla.redhat.com/bugzilla>。使用了计算机上所有 24GB 的内存
  • @thirtythreeforty 除了我怀疑它构建嵌入了所有这些 DD 的对象模块之外,我无法解释为什么这会对 g++ 产生影响。我确实发布了一个替代方案,可能在此期间为您工作。希望对您有所帮助。
  • 这看起来像GCC Bug #59659:编译器通过直接初始化每个元素为大型std::array 生成巨大的初始化代码。

标签: c++ arrays c++11


【解决方案1】:

当您使用unique_ptr 时,看起来您正在寻找类似稀疏的 3d 矩阵类型。要实现稀疏矩阵,您可以查看What is the best way to create a sparse array in C++?,作为实现细节,您可以使用Boost Multi-index 来实现对所有维度的快速访问。

【讨论】:

    【解决方案2】:

    好的,我无法解释为什么 g++ 对此感到不满,但在您解决这个问题之前,请考虑将其作为您的成员声明:

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

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

    World() : space{size}
    

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

    【讨论】:

    • size = 4000000000 在不到一秒的时间内编译完毕。
    • 我觉得有一种方法可以将空间作为 unique_ptr 到 3D array 构造并通过正确调用 new 来初始化它(并适当地修改所有用法取消引用),但我现在也无法使用构造函数语法。无论如何,这行得通。
    • @thirtythreeforty 只要你有东西可以使用。您当然可以在外部使用 std::unique_ptr 执行此操作并自行分配,但如果您在运行时需要,您可以通过 World 成员根据需要保留和放置并点击向量来执行相同操作。至少这让你在堆上并脱离自动存储。我会尝试做一些错误搜索,看看当我得到备用周期时是否已经报告了一些关于此的内容。很高兴你有临时工作要做。希望您也可以为此支持 FDINoff。他做了一些工作。
    • @thirtythreeforty 或 WhozCraig 你们中的任何一个在 gnu 错误跟踪器上有一个帐户并且愿意在那里提交错误报告吗?还是我应该这样做?
    • 我可能有一个帐户,但如果你愿意处理它,我会很好。
    【解决方案3】:
    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));
    }
    

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

    【讨论】: