编译器会优化未使用的链接文件吗?

Posted

技术标签:

【中文标题】编译器会优化未使用的链接文件吗?【英文标题】:Will the compiler optimize out unused linked files? 【发布时间】:2013-10-16 19:57:05 【问题描述】:

我正在编写一些速度非常重要的代码。我只是在编写测​​试用例后开始制作主要的二进制文件。对于我的测试运行器,我只是将所有内容提供给带有通配符的链接器。 (如下)

在我看来,链接是 C++ 将事物粘合在一起的阶段 - 填充对函数等的引用并将所有内容放在一起以形成二进制文件。

# Do the linking for the test binary
$(BIN)test_cases: $(TEST)TestRunner.o
    $(CC) $(TEST)*.o $(SRC)*.o $(CPPUNITLINKS) $(MAINLINKS) -o $(BIN)test_cases

我的问题是,鉴于我希望以任何可能的方式加速我的程序,我会更好地链接“主”二进制文件所需的最低限度的文件吗?这会导致更精简的可执行程序或更快的程序,还是编译器已经有效地丢弃了它不需要的任何东西?

【问题讨论】:

我认为链接所有 obj 文件不会影响 exe 的性能 应该是简单的尝试一个实验。写“hello, world”,链接一堆不需要的 .obj 文件,看看可执行文件有多大。在没有 .objs 的情况下重复,看看该可执行文件有多大。 @user888379:因为(在现代操作系统中)整个可执行文件没有加载到内存中——只有实际使用的部分——拥有大型可执行文件并不会降低速度。 当然,我可以测试一下,我可能会显式链接主二进制文件,看看它是如何影响它的。我的程序很难加快测试速度,但是因为它有很多随机方面。我想我在了解了更多关于链接过程的知识之后,感觉自己做对了! 【参考方案1】:

当您将目标文件链接到程序中时,链接器将解析程序中所有未解析的符号。如果您想消除死代码(GCC默认不做的),您可以执行以下操作:

    使用-fdata-sections-ffunction-sections 标志构建目标文件(有关详细信息,请参阅GCC manual); 使用-Wl,--gc-sections 优化标志链接目标文件,该标志告诉链接器丢弃未引用的部分。

注意:只有未使用的 static 函数会被自动删除。

理论上冗余符号的存在只会影响结果程序的大小。但是,我偶然发现了一些帖子,其中人们报告在剥离死代码后性能提高了 1% 到 2%。当然,代码库必须足够大才能注意到这种效果。

请注意,有时这种方法可能无法正常工作。例如,我在某些系统上遇到过崩溃或链接问题,可能是由于此功能的实现中存在错误。

此外,不要认为这些标志在每种情况下都能提高性能和/或大小。通过标志打开此功能并且默认情况下不存在,这是有充分理由的。实际上, 有时链接器可能会创建更大的对象和可执行文件和/或更慢的代码,更不用说您肯定也会遇到调试问题。

总之,在使用此功能时要非常谨慎,并始终按照其他答案中的建议分析您的代码前后。

最后,如果你真的追求速度,可以查看my other answer on some useful GCC optimization flags

最后但并非最不重要的一点是,所谓的链接时间优化 (LTO) 是一个巨大的、有前途的新概念,它已被引入 GCC,并且最近变得或多或少地稳定使用。相应的标志是 -lto,有关详细信息,请参阅 herehere。虽然现在它是可用的,但在某些平台上并不是所有东西都是闪亮的。例如,在 Windows 上,GCC 端口 MinGW/MinGW-w64 仍在努力使 LTO 支持生产质量。

【讨论】:

谢谢,我一定要试试这个!【参考方案2】:

只要文件不是绝对巨大,用于制作二进制文件的文件数量对执行时间的影响很小。当然,如果构建程序所需的时间是您想要改进的,那么减少目标文件的数量可能是实现更快构建时间的一个步骤。

执行程序所需的时间很大程度上取决于实际执行的代码。如果您有 0、1、3、5、20、100 或 10000 个未被调用的函数,则不会产生可衡量的差异。

了解您的代码为何运行缓慢(如果确实运行缓慢 - 可能只是因为执行您要求的工作需要很长时间)的关键是使用称为分析器的工具。探查器有很多选项,它们在很大程度上都做同样的事情。在最基本的层面上,分析器会告诉您在每个功能上花费了多少时间,而这反过来会告诉您将精力集中在哪里。然后,指令分析器将允许您深入到各个指令,以查看编译器做了什么以及在函数中花费的时间。

【讨论】:

【参考方案3】:

加速任何程序的第一步是 PROFILE。一个好的配置文件将显示程序中每个函数使用的时间量。

使用来自分析的数据,找到调用次数最多的函数或花费最多时间的函数。这些是您应该专注于优化的功能。

优化时,先按需求优化(例如删除一些),然后按设计(选择不同的算法,删除函数),然后通过编码(重写更高效的代码,例如减少分支和跳转),最后通过使用平台特定代码(专门的汇编指令)。编码优化的捷径是告诉编译器使用最大优化来提高速度。

如果您要使用动态库或共享库,请将常用函数放在同一个库中。这允许操作系统仅根据需要加载较少的库。

【讨论】:

以上是关于编译器会优化未使用的链接文件吗?的主要内容,如果未能解决你的问题,请参考以下文章

keil4如何在编译时不编译未调用的函数?

生成库文件,会链接依赖的库文件吗?

当我一步编译所有内容时,GCC 可以更好地优化事情吗?

链接器可以内联函数吗?

我在keil里,工程编译好之后,啥都不改,再编译一次,又要重新一个文件一个文件编译是怎么回事?

链接器如何使用静态库解析引用