我正在开发一个 C++ 程序,从一个 1200 行文件(它初始化一个相当复杂的状态机)中编译的目标代码接近 1 兆字节。是什么让文件如此之大?有没有办法可以找到目标文件中占用空间的内容?
7 回答
当目标文件大于它们的最小值时,可能有几个原因:
- 静态包含依赖库
- 使用调试信息构建
- 使用分析信息构建
- 使用模板创建(非常)复杂的数据结构(可能是递归提升结构)
- 编译时不打开优化标志(节省不多,如果使用过度可能会导致困难)
首先,我建议检查您是否正在使用调试信息进行构建,这在我的经验中会导致最臃肿。
(我假设您已启用优化和死代码剥离)。
打开链接器的“生成映射文件”选项并检查输出。
常见的罪魁祸首是产生大量代码的宏/模板和大型全局对象。
可能是一些模板实例化(尤其是std::iostream
s),也可能是广泛的内联(即完全定义在头文件中的类)。但是,首先 1 兆字节的目标文件有什么问题?在链接期间,它很可能会产生一个很小的二进制文件。我在这里有一个项目,其中包含 20 MiB 的目标文件,例如链接到 700 KiB 的二进制文件。
更新:也可能是一些大型静态数组/对象。除此之外,使用 MSVC++ 和 GCC,您可以查看为文件生成的程序集,这可以为您提供一些提示(对于 GCC,它是g++ -S foo.cpp
,对于 MSVC++,它是'/FAs')。要么你会看到很多模板实例,那么这些就是原因。如果不是,则为对象的对象大小static
。
另一个可能的原因是链接时间代码生成,一个 VC++ 选项。这会将编译器的后端移动到链接器中。这允许更好的优化,但目标文件现在必须包含通常在前端和后端之间传递的所有内部数据结构。
一个不错的选择是查看工具中提到的用于分析 ELF 部分和符号大小的可能性,例如:
nm -C --print-size --size-sort --radix=d tst.o
size -A -d tst.o | sort -k2 -n
我已经看到调试符号在 C++ 中非常大的情况,例如比二进制文件的其余部分大 30 倍,所以这绝对是要注意的事情之一,size
由于大量的调试相关部分,上述命令会立即提示您,您可以通过使用strip
或剥离二进制文件来仔细检查g++ -s
。
另一件需要注意的事情是您是否可以通过显式模板实例化节省一些空间/时间:显式模板实例化 - 何时使用?该答案的“如何快速分析您的构建以查看它是否会从模板实例化中获得很多收益”部分还包含一种在大型(C++)项目中跨对象文件查找重复对象的方法。
您可以通过添加编译标志-flto -Wl,-allow-multiple-definition
,您可以添加-fuse-linker-plugin
. -Wa,-mbig-obj
不适用于 x86/32 位架构(仅限 x64)
这是我用来查看编译时间值的宏:
template <size_t N> struct compile_time_number { private: typedef int check; };
#define compiler_check_number(N) ((compile_time_number< N >::check)0)
然后在编译时高兴地查看哪些符号占用了空间。
编辑:由于似乎没有人理解这一点,我会澄清一下:使用它的方法是添加compiler_check_number(sizeof(<insert struct or global variable here>))
. 编译器会将变量或结构的大小作为编译时错误吐出。很少有代码是巨大目标文件的原因。
我一直使用它来查看事情有多大,而无需运行调试器。