【问题标题】:why my C++ output executable is so big?为什么我的 C++ 输出可执行文件这么大?
【发布时间】:2010-05-15 06:34:37
【问题描述】:

我有一个相当简单的 C++ 项目,它使用 boost::regex 库。我得到的输出大小为 3.5Mb。据我了解,我正在静态链接所有 boost .CPP 文件,包括所有函数/方法。也许有可能以某种方式指示我的链接器仅使用来自 boost 的必要元素,而不是全部?谢谢。

$ c++ —version
i686-apple-darwin10-g++-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5659)

size 是这么说的:

$ size a.out
__TEXT  __DATA  __OBJC  others  dec hex
1556480 69632   0   4296504912  4298131024  100304650

我试过strip

$ ls -al
...  3946688 May 21 13:20 a.out
$ strip a.out
$ ls -al
...  3847248 May 21 13:20 a.out

ps。这就是我的代码的组织方式(也许这是问题的主要原因):

// file MyClass.h
class MyClass {
  void f();
};
#include "MyClassImpl.h"

// file MyClassImpl.h
void MyClass::f() {
  // implementation...
}

// file main.cpp
#include "MyClass.h"
int main(int ac, char** av) {
  MyClass c;
  c.f();
}

你怎么看?

【问题讨论】:

  • 也许您正在使用在可执行文件中插入的调试信息进行构建。尝试在启用优化的情况下进行构建。
  • 你在编译优化吗?
  • 您使用的是什么编译器/链接器,以及哪个操作系统?
  • 由于它是一个相对简单的项目,也许您可​​以将其归结为一些示例代码,这些代码编译得比您想要/预期的要大得多?将帮助这里的人们弄清楚发生了什么。
  • 我刚刚添加了我的代码结构的简短说明。也许这会有所帮助。谢谢

标签: c++ boost


【解决方案1】:

您是否在启用调试符号的情况下进行编译?这可能占规模的很大一部分。另外,您如何确定二进制文件的大小?假设您在一个类 UNIX 平台上,您使用的是直接的“ls -l”还是“size”命令。如果二进制文件包含调试符号,两者可能会给出截然不同的结果。例如,这是我在构建 Boost.Regex "credit_card_example.cpp" 示例时得到的结果。

$ g++ -g -O3 foo.cpp -lboost_regex-mt

$ ls -l a.out 
-rwxr-xr-x 1 void void 483801 2010-05-20 10:36 a.out

$ size a.out
   text    data     bss     dec     hex filename
  73330     492     336   74158   121ae a.out

仅生成目标文件时会出现类似的结果:

$ g++ -c -g -O3 foo.cpp

$ ls -l foo.o 
-rw-r--r-- 1 void void 622476 2010-05-20 10:40 foo.o

$ size foo.o
   text    data     bss     dec     hex filename
  49119       4      40   49163    c00b foo.o

编辑:添加了一些静态链接结果...

这是静态链接时的二进制大小。它更接近你所得到的:

$ g++ -static -g -O3 foo.cpp -lboost_regex-mt -lpthread

$ ls -l a.out 
-rwxr-xr-x 1 void void 2019905 2010-05-20 11:16 a.out

$ size a.out 
   text    data     bss     dec     hex filename
1204517    5184   41976 1251677  13195d a.out

也有可能很大一部分来自 Boost.Regex 库所依赖的其他库。在我的 Ubuntu 机器上,Boost.Regex 共享库的依赖项如下:

$ ldd /usr/lib/libboost_regex-mt.so.1.38.0 
        linux-gate.so.1 =>  (0x0053f000)
        libicudata.so.40 => /usr/lib/libicudata.so.40 (0xb6a38000)
        libicui18n.so.40 => /usr/lib/libicui18n.so.40 (0x009e0000)
        libicuuc.so.40 => /usr/lib/libicuuc.so.40 (0x00672000)
        librt.so.1 => /lib/tls/i686/cmov/librt.so.1 (0x001e2000)
        libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x001eb000)
        libm.so.6 => /lib/tls/i686/cmov/libm.so.6 (0x00110000)
        libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x009be000)
        libpthread.so.0 => /lib/tls/i686/cmov/libpthread.so.0 (0x00153000)
        libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0x002dd000)
        /lib/ld-linux.so.2 (0x00e56000)

ICU 库可以变得非常大。除了调试符号之外,也许它们是二进制文件大小的主要贡献者。此外,在静态链接的情况下,Boost.Regex 库本身似乎由大型目标文件组成:

$ size --totals /usr/lib/libboost_regex-mt.a | sort -n
      0       0       0       0       0 regex_debug.o (ex /usr/lib/libboost_regex-mt.a)
      0       0       0       0       0 usinstances.o (ex /usr/lib/libboost_regex-mt.a)
      0       0       0       0       0 w32_regex_traits.o (ex /usr/lib/libboost_regex-mt.a)
   text    data     bss     dec     hex filename
    435       0       0     435     1b3 regex_raw_buffer.o (ex /usr/lib/libboost_regex-mt.a)
    480       0       0     480     1e0 static_mutex.o (ex /usr/lib/libboost_regex-mt.a)
   1543       0      36    1579     62b cpp_regex_traits.o (ex /usr/lib/libboost_regex-mt.a)
   3171     632       0    3803     edb regex_traits_defaults.o (ex /usr/lib/libboost_regex-mt.a)
   5339       8      13    5360    14f0 c_regex_traits.o (ex /usr/lib/libboost_regex-mt.a)
   5650       8      16    5674    162a wc_regex_traits.o (ex /usr/lib/libboost_regex-mt.a)
   9075       4      32    9111    2397 regex.o (ex /usr/lib/libboost_regex-mt.a)
  17052       8       4   17064    42a8 fileiter.o (ex /usr/lib/libboost_regex-mt.a)
  61265       0       0   61265    ef51 wide_posix_api.o (ex /usr/lib/libboost_regex-mt.a)
  61787       0       0   61787    f15b posix_api.o (ex /usr/lib/libboost_regex-mt.a)
  80811       8       0   80819   13bb3 icu.o (ex /usr/lib/libboost_regex-mt.a)
 116489       8     112  116609   1c781 instances.o (ex /usr/lib/libboost_regex-mt.a)
 117874       8     112  117994   1ccea winstances.o (ex /usr/lib/libboost_regex-mt.a)
 131104       0       0  131104   20020 cregex.o (ex /usr/lib/libboost_regex-mt.a)
 612075     684     325  613084   95adc (TOTALS)

如果这些目标文件中的部分或全部链接到您的二进制文件中,仅 Boost.Regex 就可以获得高达 ~600K。

【讨论】:

  • +1 尝试使用 boost::spirit 和 -O2 -g。 250LoC --> 20M,不开玩笑。这些符号太长了,它会使 valgrind 崩溃。模板调试符号不会乱来。
  • 不要使用 -g 或 -ggdb 标志进行编译。或者,在您的可执行文件上运行 strip -g 以去除调试符号。(请注意,运行 exe 时不会加载调试信息,将它们保留在可执行文件中不会影响您的 RAM)
  • 我不使用 -g 编译。 MacOS 条没有 -g 选项... :(
  • 似乎 strip -S 会在 MacOSX 上做到这一点
  • strip -S 后二进制大小相同 :(
【解决方案2】:

-O3 标志不会优化代码的大小,而是优化执行速度。所以也许例如一些循环展开会导致更大的文件。尝试使用其他一些优化标志进行编译。 -Os 标志将针对小型可执行文件进行优化。

【讨论】:

  • 这也是一个很好的观点。使用 -Os 编译 Boost.Regex "credit_card_example.cpp" 示例在动态和静态链接的情况下都将二进制文件的大小减少了大约 20K(请参阅我对 -O3 结果的回答)。无论如何,如果它显着降低了@Vincenzo 的二进制大小,我会感到惊讶。当然值得一试。
  • +1,我不知道 -Os 选项使尺寸变小了,绝对是未来的好信息!
【解决方案3】:

如果您是静态链接,那么大多数链接器将只包含所需的对象。

3.5Mb 并不是那么大 - 在 PC 系统上,所以大小可能取决于操作系统等

【讨论】:

  • 我的源代码只有 10KSLOC,boost-regex 是另一个 6KSLOC。这段代码(16KSLOC)如何生成 3.5Mb 的可执行文件?
  • @Vin 模板、运行时、预处理器加起来。 loc 不是很有意义
  • 3.5 MB 不适合 L1 缓存。对于嵌入式设备(如索尼 PSP)来说也是一个真正的问题,其中 4 megs 将是二进制文件在所有内存中的最大可分配大小。我提醒演示制作者可以制作只有 4k 声音的 3d 程序,而完全可以在 64k 中进行演示。
【解决方案4】:

如果您正确设置了链接顺序(最依赖,然后是最小依赖),链接器应该只抓取您的程序实际使用的符号。此外,由于模板的使用,很多(但不是全部,我不能代表正则表达式)提升功能只是标题。

更有可能是调试信息/符号表/等占用了二进制文件中的空间。模板名称(例如 iostream 和标准容器)很长,会在符号表中创建很大的条目。

你没有说你正在使用什么操作系统,但如果它是一个 unix 变体作为测试,你实际上可以 strip 一份你的二进制文件的副本来删除所有额外的信息并查看剩下的内容:

cp a.out a.out.test
strip a.out.test
ls -l a.out*

在我测试的一个二进制文件上,它删除了大约 90% 的文件大小。请注意,如果您这样做,如果没有要调试的未剥离二进制文件的副本,任何内核都将毫无用处 - 您将没有任何符号名称或任何东西,只有程序集和地址。 3.5 MB 在现代确实是一个很小的文件。即使只有 10Ksloc 的源代码,也很可能只有那么多调试/符号信息。

【讨论】:

  • 无需调试符号和相关杂物,无需剥离二进制文件以确定大小。只需使用size 命令,如我的回答中所述。
【解决方案5】:

你说你有 3 个文件。 对我来说,MyClassImpl.h 可能是一个 .cpp,因为它包含实现。

无论如何,如果您实际上是在编译包括 boost::regex 在内的两个文件,您最终会得到两倍于 boost::regex 的大小(确切地说,如果您在两个文件中使用相同的功能,您的成本将是原来的两倍在太空中)。

这是因为大多数 boost 功能都是内联模板。

最好的,

【讨论】:

    【解决方案6】:

    如果你有 ldd 可用,你可以用它来检查你是否真的链接到所有的 boost 库。

    另一种可能性是大小是仅使用标头库的副作用,许多 boost 库都是这种类型的,包括它们可以内联更多您可以相信的代码。由于使用了几个不同的模板参数,您还可以生成某种组合爆炸。

    为了获得更好的诊断,您应该尝试使用正则表达式创建一个非常短的程序并查看您获得的大小。如果你的程序真的很短 3.5 Mo 是相当大的。我当前的 projet 可执行文件也使用 BOOST(但不是正则表达式)并且大小相同。但我说的是大约 20000 行 C++。因此,某处应该有一个陷阱。

    【讨论】:

      猜你喜欢
      • 2012-06-14
      • 2020-04-05
      • 1970-01-01
      • 2012-03-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-06
      • 1970-01-01
      相关资源
      最近更新 更多