GCC c++11 使用大量 RAM 和 STL 位集<UINT_MAX>

GCC c++11 using a lot of RAM with STL bitset<UINT_MAX>

本文关键字:UINT lt gt MAX 位集 STL c++11 RAM GCC      更新时间:2023-10-16

今天编译我的程序时,我注意到GCC(编译步骤(的内存消耗模式中有一些非常奇怪的东西(我相信可以以某种方式解释(。名为"cc1plus"的进程对代码少于 10,000 行的程序使用大约 10 GB 的 RAM。在注释和取消注释代码行之后,我终于找到了"罪魁祸首":

std::bitset<UINT_MAX>

如果你想测试自己,请尝试这个非常短的程序:

#include <iostream>
#include <bitset>
#include <climits>
std::bitset<UINT_MAX> justAVar;
int main()
{
   std::cout << UINT_MAX << std::endl;
   return 0;
}
使用 -std=c++

11 或 -std=c++0x 编译它请注意,即使是这个小例子在编译时也会使用大量 RAM(在我的情况下,GCC 的两个盒子上都有 7 GB,clang 的两个盒子上都有 2.6 GB(,所以如果你必须按下重置按钮,不要抱怨和诅咒众神,就像你没有被警告过一样。

我的测试机器:

设置 1: Debian 7.0 64, gcc 4.8.1

设置 2:Ubuntu 12.04 64、gcc 4.7.3、gcc 4.8.1、clang 3.0.6(最后一个使用 -std=c++0x(

关于位集构造函数实现的一个提示(显然由 if def 保护 C++11 和 C++0x(,正如我的一位同事向我展示的那样:它是使用 constexpr 声明

现在的问题是:有人可以解释一下我(我们(在这种情况下所有这些编译器是怎么回事吗?

谢谢

查看bitset实现,其开头如下所示:

template<size_t _Nw>
struct _Base_bitset
{
  typedef unsigned long _WordT;

  _WordT _M_w[_Nw];
  constexpr _Base_bitset()
  : _M_w() { }

我们可以创建一个最小的测试用例,如下所示:

template<unsigned N> 
struct bset
{
    unsigned int v[N/32];
    constexpr bset() : v() {}   
};

bset<1000000000> x;

位集必须通过常量初始化来初始化:

3.6.2 非局部变量的初始化 [basic.start.init]

执行常量初始化:

— 如果初始化了具有静态或线程存储持续时间的对象 通过构造函数调用,如果构造函数是 constexpr 构造 函数。。。

实际术语和一般情况下,它意味着在编译时评估构造对象的内存映像并将其分配给.data部分。

好吧,事实证明,如果内存图像只是很多零,gcc 足够聪明,可以弄清楚并在 .bss 中分配对象,但看起来,它首先必须创建图像并检查它。

当然,更好的方法是推断,如果bitset的唯一成员是值初始化的,并且该成员是一个数组,并且数组的元素没有构造函数,因此它们的值初始化是零初始化,然后数组是零初始化的,那么对象是零初始化的,然后完成。

std::bitset不会

动态分配其存储,它包含在对象本身中。这意味着编译器很可能尝试跟踪其状态,以允许常量折叠和其他优化。在某些地方使用constexpr的事实也促成了这一点。这包括跟踪bitset各个部分价值的一些开销,将导致它分配数吨的内部结构。

我不确定在哪些情况下会触发此功能,并且在不同的编译器/版本上可能会有所不同,或者取决于某些设置。

UINT_MAX

大多数现代机器上约为40亿。由于std::bitset<N>存储N位,因此这转化为这种std::bitset所需的0.5千兆字节内存(40亿/8(。您看到的开销可能是由于内部编译器优化造成的。

http://coliru.stacked-crooked.com/的一些小实验:

  • Clang -std=c++98: OK
  • Clang -std=c++11: FAIL
  • g++ -std=c++98: FAIL
  • g++ -std=c++11: FAIL

因此,至少对于 Clang 来说,C++98 中缺少constexpr将允许您在合理的资源上编译该程序而不会出现问题(不确定 Coliru 允许客户端多少内存(。