如何使C ++链接占用更少的内存

时间:2014-09-25 10:06:21

标签: c++ templates c++11 linker

我正在使用很多模板进行C ++大学研究项目,这些模板还有其他嵌套模板等等。该项目涉及特定研究领域的有效指数数据结构。你可以想象:索引结构有很多参数要调整,所以我们过度使用模板参数。当然,我们希望使用不同的参数集测试我们的索引,因此有很多模板实例化。

项目不是那么大。也许50k LOC。但是,链接需要50秒,消耗超过7 GB的内存(!!!)。我在32GB的工作站上,所以一切都很好。我经常有从事这个项目的学士和硕士生。问题是他们经常使用4或8 GB RAM的笔记本电脑。因此,这些学生在编写该项目时遇到了很大麻烦。得到的测试二进制文件(即仅包含索引结构的单元测试的二进制文件)是700兆字节。 其中大多数是符号,因为嵌套模板会产生巨大的名称。如果我在二进制文件上使用strip,它会下降到8兆字节。

那么有没有办法减少链接期间的RAM使用量?有没有办法在嵌套模板中使用较小的符号?

我们在Ubuntu 14.10下使用g ++ 4.9和std=c++11进行编译。

编辑:

它似乎真的是嵌套模板。我们有两个具有非常深层嵌套模板的测试用例。这些测试的两个.o文件几乎占最终二进制文件内存的90%。它们导致方法名称长度超过3000个字符。这里没有办法不使用嵌套模板,因为它们构成了一个"处理树"一个示例查询。使用深层嵌套模板时,有没有办法让名字缩短?

3 个答案:

答案 0 :(得分:3)

  

那么有没有办法减少链接期间的RAM使用量?有没有办法在嵌套模板中使用较小的符号?

您是否考虑在客户端代码中使用 pimpl idiom

考虑一下你有这个包含链的情况:

A.h - > B.h - > C.h - > D.h(C包括D,B包括C等)

假设A.h定义了AA类,B.h定义了B类等等(AA用BB实现,BB用CC实现等等。)

如果DD是一个大模板并用于CC的实现,模板化代码A,B和C将被编译三次。

现在,考虑如果不是C.h而不是D.h,会发生什么情况,你会遇到以下情况:

C.h foward声明CCImpl *pimpl并将其所有方法转发给pImpl->方法(并且不包括D.h)。

C.cpp包括C.h和D.h,并实现CCImplCC

现在,D将包含一次(并编译一次,用于C.cpp)。 A和B将仅包括C.h,带有CImpl前向声明。 A.h,B.h和C.h不再知道存在模板。

答案 1 :(得分:2)

GCC对其使用的RAM有一个垃圾收集方案。

参数ggc-min-expandggc-min-heapsize用于确定GCC何时应该清除并释放未使用的内存(它们的默认值是系统内存总量的百分比)。

您可以尝试以下方式:

g++ --param ggc-min-expand=0 --param ggc-min-heapsize=8192

从GCC手册:

  

<强> GGC-分钟展开

     

GCC使用垃圾收集器来管理自己的内存分配。此参数指定垃圾的最小百分比   应该允许收集器的堆在集合之间扩展。   调整这可能会提高编译速度;它对代码没有影响   代。

     

当RAM> = 1GB时,默认值为30%+ 70%*(RAM / 1GB),上限为100%。如果getrlimit可用,那么“RAM”的概念就是   最小的实际RAM,RLIMIT_RSS,RLIMIT_DATA和RLIMIT_AS。如果GCC   无法计算特定平台上的RAM,即下限   使用了30%。将此参数和ggc-min-heapsize设置为零   导致每次机会都发生完整的收集。这是   非常慢,但可以用于调试。

     

<强> GGC-最小堆大小

     

垃圾收集器堆开始前的最小大小   打扰收集垃圾。第一个集合发生在   堆扩展为ggc-min-expand%超出ggc-min-heapsize。再次,调整   这可能会提高编译速度,并且对代码没有影响   代。        默认值为RAM / 8,下限为4096(4兆字节),上限为131072(128兆字节)。如果getrlimit是   可用,“RAM”的概念是实际RAM中最小的,   RLIMIT_RSS,RLIMIT_DATA和RLIMIT_AS。如果GCC无法计算   特定平台上的RAM,使用下限。设置这个   参数非常大,有效地禁用垃圾回收。设置   此参数和ggc-min-expand为零会导致完整集合   发生在每个机会。

进一步详情:

答案 2 :(得分:1)

明智地使用继承。您可能有class Foo<1,4,8,1,9,int, std::string>作为class Bar的基类,然后目标文件只会提到Bar

请注意,typedef不会引入用于链接目的的名称。

[编辑] 为了解决另一个注释的性能问题,一个空的派生类在正常的优化级别上不会增加​​常见编译器的开销(并且即使在调试版本中通常也没有开销)