链接时优化简介
在传统编译流程中,gcc 将单个源文件(称谓一个编译单元)直接进行编译优化生成包含汇编代码的.o
目标对象文件,并由链接器对这些.o
文件进行符号表解析与重定位,链接成可执行文件。在这个过程中,拥有跨文件函数调用信息的链接器由于操作的是汇编代码,难以进行编译优化,而可以执行编译优化的环节,却没有跨文件的全局信息。这样的编译框架,虽然提高了编译效率,每次重新编译只需要编译修改过的少量编译单元,但也丢失了许多跨文件的优化机会。
链接时优化(LTO), 设计的初衷就是希望能够在链接时,拥有跨编译单元的调用信息的时候,进行编译优化,提供更多的优化机会。为了达到这个目的,LTO 需要将编译优化所需的 IR 信息保留到链接时。在链接时,链接器会调用 LTO 插件,执行全程序分析,生成更加有效的优化决策,再经由编译优化生成更高效的IR,进一步转成包含汇编代码的目标对象文件,最后由链接器完成常规的链接工作。
版本构建使能
背景
为了获得更优的性能与更小的二进制体积,许多海外社区已经在版本构建中使用了链接时优化,链接时优化也正在成为各方寻找编译优化机会的新战场。openEuler 计划从 24.09 创新版本开始,在版本构建中引入 LTO。
方案
我们将在 openEuler-rpm-config 的 macro 中所包含的全局编译选项中插入 -flto -ffat-lto-objects
,以达到在构建软件包是使能 lto 的效果。其中 -flto
用以使能链接时优化,而 -ffat-lto-objects
用以生成同时包含 LTO 对象信息和常规链接所需的汇编信息的胖目标对象。在当前构建中,LTO 对象信息会参与 LTO 优化,而由于 LTO 目标对象文件在不同版本的 gcc 之间是不通用的,因此在打包生成对应的 .rpm
包之前,我们会将 .o/.a
文件中的 LTO 相关字段给消除,仅保留常规链接所需的汇编代码信息,不对静态库本身产生影响。
使能范围
由于 LTO 的编译流程与常规的编译流程相差较大,影响较广,为了控制 LTO 对版本质量的冲击,我们当前仅对 500+ 个软件包使能了 lto,你可以在 /usr/lib/rpm/%{_vendor}/lto_white_list
中找到这些软件包的清单。这些白名单应用在使能了 LTO 之后构建成功,并通过了自带的测试套。lto 编译选项仅在构建白名单应用时被设置为 -flto -ffat-lto-objects
,否则置为空。
我们会在后续的创新版本上,联合应用维护者,扩大 LTO 使能的范围。
注意事项
当前热补丁机制与 LTO 仍存在不兼容,热补丁在 LTO 开启时会失效。我们已与热补丁团队就解决方式达成一致,会在后续版本中解决该问题。