GCC c ++ 11使用STL位集<UINT的大量RAM

在今天编写我的程序时,我注意到GCC的内存消耗模式(编译步骤)中有一些非常奇怪的东西(我敢肯定可以用某种方式解释)。 称为“cc1plus”的进程使用大约10 GB的RAM用于少于10000行代码的程序。 在评论和取消注释代码行后,我终于找到了“罪魁祸首”:

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和gng均为7 GB) ),所以如果你不得不按下重置按钮,不要抱怨和诅咒神像你没有被警告过。

我的测试机器:

设置1:Debian 7.0 64,gcc 4.8.1

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

关于bitset构造函数的实现的一个暗示(很明显,如果是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 GB的内存(40亿/ 8)。 您看到的开销可能是由于内部编译器优化。

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

  • Clang -std = c ++ 98:好的
  • Clang -std = c ++ 11:FAIL
  • g ++ -std = c ++ 98:FAIL
  • g ++ -std = c ++ 11:失败
  • 因此,至少对于Clang而言,C ++ 98中缺少constexpr将允许您在没有合理资源(不确定Coliru允许客户端有多少内存的情况下)没有问题的情况下编译此程序。

    链接地址: http://www.djcxy.com/p/66593.html

    上一篇: GCC c++11 using a lot of RAM with STL bitset<UINT

    下一篇: c++