Rust和C / C ++的跨语言链接时间优化LTO
链接时间优化(LTO)是LLVM实施整个程序优化的方法。跨语言LTO是Rust编译器中的一项新功能,使LLVM的链接时间优化可以在混合的C / C ++ / Rust代码库中执行。也是一项功能,完美结合了Rust编程语言和LLVM编译器平台的两个优点:
• Rust缺乏语言运行时和底层访问能力,几乎具有与现有C / C ++代码库无缝集成的独特能力,并且
• 作为语言不可知的基础,LLVM提供了一个通用基础,在该基础上不再需要编写特定代码的源语言。
那么,跨语言LTO会做什么?有两个答案:
• 从技术角度来看,允许在不考虑实现语言边界的情况下优化代码库,即使在编写了一个编译单元的情况下,也可以跨单个编译单元执行重要的优化(如函数内联)用Rust编写,另一个用C ++编写。
• 从心理学的角度(可能同样重要)来看,有助于减轻许多注重性能的开发人员在开发一款在不同源语言实现的功能之间来回跳动的软件,可能会产生的效率低下的烦恼感。
由于Firefox是一个大型的,对性能敏感的代码库,其中很多部分都用Rust编写,因此跨语言LTO一直是Firefox开发人员中最喜欢的愿望清单。因此,Mozilla的低级工具团队的工作人员全力以赴,以在Rust编译器中实现。

为了解释跨语言LTO的工作原理,退后一步并回顾LLVM世界中传统的编译和“常规”链接时间优化是如何工作的,这很有用。
背景-LLVM编译管道的鸟瞰图
Clang和Rust编译器都遵循类似的编译工作流程,在某种程度上由LLVM规定:

  1. 编译器前端.bc为每个编译单元生成一个LLVM位代码模块()。在C和C ++中,每个源文件将产生一个编译单元。在Rust中,每个板条箱至少转换为一个编译单元。

    .c --clang–> .bc

    .c --clang–> .bc

    .rs --+
    |
    .rs --±-rustc–> .bc
    |
    .rs --+

  2. 下一步,LLVM的优化管道将单独优化每个LLVM模块:

    .c --clang–> .bc --LLVM–> .bc (opt)

    .c --clang–> .bc --LLVM–> .bc (opt)

    .rs --+
    |
    .rs --±-rustc–> .bc --LLVM–> .bc (opt)
    |
    .rs --+

  3. LLVM然后将每个模块降低为机器代码,以便每个模块得到一个目标文件:

    .c --clang–> .bc --LLVM–> .bc (opt) --LLVM–> .o

    .c --clang–> .bc --LLVM–> .bc (opt) --LLVM–> .o

    .rs --+
    |
    .rs --±-rustc–> .bc --LLVM–> .bc (opt) --LLVM–> .o
    |
    .rs --+

  4. 最后,链接器将获取目标文件集,并将链接在一起成为二进制文件:

    .c --clang–> .bc --LLVM–> .bc (opt) --LLVM–> .o ------+
    |
    .c --clang–> .bc --LLVM–> .bc (opt) --LLVM–> .o ------+
    |
    ±-ld–> bin
    .rs --+ |
    | |
    .rs --±-rustc–> .bc --LLVM–> .bc (opt) --LLVM–> .o --+
    |
    .rs --+
    如果不涉及任何LTO,这就是常规的编译工作流程。每个编译单元都是单独进行优化的。优化器不知道其它编译单元内部的函数定义,因此无法内联或根据实际作用做出其它类型的决策。为了使内联和优化能够跨越编译单元边界进行,LLVM支持链接时间优化。
    LLVM中的链接时间优化
    LTO的基本原理是,某些LLVM的优化过程被推回到链接阶段。为什么要链接阶段?因为那是流水线中的整个程序(即整个编译单元集)一次可用的点,因此跨编译单元边界的优化成为可能。通过链接程序的插件,可以在链接阶段执行LLVM工作。

这是LTO具体实现的方式:
• 编译器将每个编译单元转换为LLVM位代码(即,跳过降级为机器代码),

• 链接器通过LLVM链接器插件知道如何读取LLVM位代码模块(例如常规目标文件),以及

• 链接器再次通过LLVM链接器插件,合并它遇到的所有位码模块,然后在执行实际链接之前运行LLVM优化过程。
有了这些功能后,启用了C ++代码LTO的新编译工作流程如下所示:

.c --clang--> .bc --LLVM--> .bc (opt) ------------------+ - - +|     |
.c --clang--> .bc --LLVM--> .bc (opt) ------------------+ - - +|     |+-ld+LLVM--> bin
.rs --+                                                 ||                                                 |
.rs --+--rustc--> .bc --LLVM--> .bc (opt) --LLVM--> .o -+|
.rs --+

Rust代码仍被编译为常规目标文件。因此,Rust代码对于链接时进行的优化是不透明的。但是,看一下该图,似乎应该不难改变,对吧?
跨语言链接时间优化
实施跨语言LTO在概念上很简单,因为该功能是建立在巨头的肩膀上的。由于Rust编译器使用LLVM,因此所有重要的构建块都可以使用。最终的图看起来与期望的非常rustc相像,发出了优化的LLVM位代码和LLVM链接器插件,并将其与其余模块一起整合到LTO流程中:

.c --clang--> .bc --LLVM--> .bc (opt) ---------+|
.c --clang--> .bc --LLVM--> .bc (opt) ---------+|+-ld+LLVM--> bin
.rs --+                                        ||                                        |
.rs --+--rustc--> .bc --LLVM--> .bc (opt) -----+|
.rs --+

尽管如此,实现生产就绪型实施仍然是较大的时间投资。在弄清所有内容如何融合之后,主要的挑战是让Rust编译器生成LLVM位代码,该代码既与Clang生成的位代码兼容,又与链接器插件接受的兼容。遇到的一些问题是:
• Rust编译器和Clang都基于LLVM,但可能使用了不同版本的LLVM。由于Rust的LLVM版本通常与特定的LLVM版本不匹配,可以是LLVM信息库中的任意修订版,这一事实使情况更加复杂。涉及到的所有LLVM版本实际上都必须紧密匹配,以使事情顺利进行。Rust编译器的文档为各个版本的Rust和Clang提供了一个兼容性表。

• 默认情况下,Rust编译器会在将相同的板条箱传递给链接器之前,在同一个板条箱的所有编译单元上执行一种特殊的LTO形式,称为ThinLTO。但是,很快了解到,在尝试对已经完成该过程的模块执行另一轮ThinLTO时,LLVM链接器插件因分段错误而崩溃。考虑并指示Rust编译器在针对跨语言案例进行编译时禁用其自身的ThinLTO传递,并且实际上一切都很好-直到几周后即使分段错误仍神秘地被禁用,分段错误仍神秘地返回。

再次需要进行两次LTO,这一次是常规LTO传递rustc进去,然后将其输出送入链接器插件中的ThinLTO中。这种设置尽管在计算上很昂贵,却是理想的,可以产生更快的代码,并且可以在Rust方面更好地消除死代码。从理论上讲,应该可以正常工作。然而rustc,尽管不停地检查ThinLTO是否被Rust所使用,以某种方式产生的符号名称显然已经通过ThinLTO的修改了。随着问题的持续存在,逐渐开始严重质疑对LLVM内部工作的理解,同时,逐渐没有关于如何进一步调试想法。

当发现Rust的预编译标准库仍将启用ThinLTO时,无论用于测试的编译器设置如何,都可以想象。标准库(包括其LLVM位代码表示)作为Rust二进制发行版的一部分进行编译,始终使用Rust的构建服务器中的设置进行编译。在本地进行的完整LTO传递rustc会将这个麻烦的位代码拉到输出模块中,又会使链接器插件再次崩溃。ThinLTO被libstd默认关闭。

• 经过上述修复后,成功地启用了跨语言LTO来编译整个Firefox。不幸的是,发现没有实际的跨语言优化发生。Clang和rustcLlang都生成LLVM位代码,LLD生成可运行的Firefox二进制文件,在查看机器代码时,甚至没有琐碎的函数都跨语言边界内联。经过调试(没有意识到LLVM的优化说明),事实证明Clangtarget-cpu在所有函数上都发出了一个属性,而rustc没有,这使得LLVM拒绝了内联机会。

为了防止将来出于类似原因静默地回归该功能,在扩展Rust编译器的测试框架和CI方面付出了相当大的努力。现在,能够编译并运行兼容版本的Clang,并使用它来执行跨语言LTO的端到端测试,从而确保小型功能确实可以跨语言边界内联。
这个列表可能还会持续一段时间,每个其它目标平台都会带来新的惊喜。为了保持对许多活动部件的控制,必须仔细进行每一步的回归测试。对基础实现充满信心,Firefox提供了一个大型,复杂,多平台的测试用例。
使用跨语言LTO:一个简单的例子
确切的构建工具调用会有所不同,具体取决于是由rustcClang还是Clang执行最后的链接步骤,以及Rust代码是通过Cargo还是rustc直接进行编译。Rust的编译器文档描述了各种情况。其中最简单的rustc一个直接生成一个静态库,而Clang进行链接,如下所示:

# Compile the Rust static library, called "xyz"
rustc --crate-type=staticlib -O -C linker-plugin-lto -o libxyz.a lib.rs# Compile the C code with "-flto"
clang -flto -c -O2 main.c# Link everything
clang -flto -O2 main.o -L . -lxyz

该-C linker-plugin-lto选项指示Rust编译器发出LLVM位代码,然后可将其用于“完整”和“薄” LTO。第一次进行设置可能非常麻烦,所有涉及的编译器和链接器都必须是兼容版本。从理论上讲,大多数主要的连接器都会起作用。实际上,LLD似乎是Linux上最可靠的LLD,第二名是Gold,BFD链接器的版本至少应为2.32。在Windows和macOS上,唯一经过正确测试的链接器ld64分别是LLD和LLD。对于ld64Firefox,使用修补程序版本是因为LLVM位代码rustc产生喜欢触发此链接器与ThinLTO预先存在的问题的信息。

结论
针对Windows,macOS和Linux上的Firefox发布版本启用了跨语言LTO,Mozilla的低级工具团队,对此感到满意。尽管仍然需要简化该功能的初始设置,已经启用了从Firefox中的Rust组件中删除重复的逻辑的功能,因为代码可以简单地调用等效的C ++实现并依赖于这些调用。即使可以将跨语言的LTO进行适当的测试并进行持续测试,即使将与现有的C ++代码紧密集成在一起,也肯定会降低在Rust中实现新组件的心理门槛。

从1.34版开始,Rust编译器中提供了跨语言LTO,并且可以与Clang 8一起使用。可以尝试一下,并报告Rust Bug Tracker中的任何问题。

Rust和C / C ++的跨语言链接时间优化LTO相关推荐

  1. XLORE2:大规模跨语言知识图谱构建与应用

    论文地址:XLORE2: Large-scale Cross-lingual Knowledge Graph Construction and Application ABSTRACT XLORE2 ...

  2. LLVM LTO(Link Time Optimizer) 链接时优化

    Link Time Optimizer 翻译自: https://llvm.org/docs/LinkTimeOptimization.html https://llvm.org/docs/GoldP ...

  3. 论文浅尝 - EMNLP2020 | 低资源跨语言实体链接中的设计挑战

    论文笔记整理:谭亦鸣,东南大学博士. 来源:EMNLP 2020 链接:https://arxiv.org/pdf/2005.00692.pdf 1.背景介绍 跨语言实体链接(XEL)旨在将任一非英语 ...

  4. 论文浅尝 - TACL2020 | 改进低资源跨语言实体链接的候选生成问题

    论文笔记整理:谭亦鸣,东南大学博士. 来源:TACL 2020 链接: https://arxiv.org/ftp/arxiv/papers/2003/2003.01343.pdf 1.背景介绍 跨语 ...

  5. 直播 | ACL 2021论文解读:低资源语言场景下的跨语言文本摘要

    「AI Drive」是由 PaperWeekly 和 biendata 共同发起的学术直播间,旨在帮助更多的青年学者宣传其最新科研成果.我们一直认为,单向地输出知识并不是一个最好的方式,而有效地反馈和 ...

  6. Dapr + .NET 实战(十三)跨语言开发

    欢迎大家参加4小时Dapr+.NET 5的实战课程 课程链接     https://ke.qq.com/course/4000292?tuin=1271860f 因为基于Dapr的服务架构是不限语言 ...

  7. 跨语言调用Hangfire定时作业服务

    背景 Hangfire允许您以非常简单但可靠的方式执行后台定时任务的工作.内置对任务的可视化操作.非常方便. 但令人遗憾的是普遍都是业务代码和hagnfire服务本身聚合在一个程序中运行,极大的限制了 ...

  8. 论文浅尝 - ACL2022 | 基于多语言语义解耦表示的跨语言迁移方法实现多语言阅读理解...

    论文作者:吴林娟,天津大学,研究方向为自然语言理解 论文链接:http://arxiv.org/abs/2204.00996 代码地址:https://github.com/wulinjuan/SSD ...

  9. 论文浅尝 | 改善多语言KGQA的 Zero-shot 跨语言转换

    笔记整理:谭亦鸣, 东南大学博士生 来源:NAACL'21 链接:https://aclanthology.org/2021.naacl-main.465/ 概述 为了扩展多语言知识图谱问答的应用,Z ...

最新文章

  1. 除了海思麒麟 华为在ARM芯片上还有哪些成就?
  2. 城市大脑不仅是AI系统,更是结合人类智慧的混合智能巨系统
  3. spring的@Transactional注解详细用法
  4. java 内部类 加载_java内部类及类加载顺序
  5. JAVA 计算String类型的时间差(秒)
  6. 修改浏览器下拉条颜色和粗细
  7. js 获取td高度_JS或jQuery获取宽高度
  8. 数据结构学习(二)——单链表的操作之头插法和尾插法创建链表
  9. 盛大游戏杯第十五届上海大学程序设计联赛暨上海金马五校赛
  10. 互联网晚报 | 3月22日 星期二 |​ ​工作人员标注mu5735残骸并展开调查;万门大学疑似解散VIP群跑路...
  11. 【资源下载】921页《用Python3带你从小白入门机器学习实战》教程手册
  12. 学好python需要哪些基础_学Python要避免哪些坑,如何巩固好基础
  13. 年度神作!这本Python 3.6的书刷爆朋友圈,网友:太香!
  14. 重温 2020 AI 应用场景,哪个最让你印象深刻?
  15. Python-Django-视图
  16. 为何我们要离职?---一个程序猿的社会学感悟
  17. excel 文件中新建的无法打开
  18. 物联网技术练习题(二)——多选题与简答题
  19. 年货来咯:精选年度最受欢迎干货,覆盖客户端、服务端、前端、数据、算法……...
  20. 计算机前端总线频率,前端总线频率

热门文章

  1. Alibaba Cloud Linux 2.1903 LTS 64位服务器yum源下载404,Alibaba Cloud Linux 2实例中使用docker-ce、epel等YUM源安装软件失败
  2. IDEA自定义快捷指令,快捷生成代码、注释
  3. 一分钟带你了解什么是“复杂度” 算法上的O(1)、O(n)、O(logn) 这些都是什么❓❓
  4. Docker安装Apache与运行简单的web服务——httpd helloworld
  5. 2022-2028年中国碘矿行业竞争格局分析及市场需求前景报告
  6. HJ75 公共字符串计算
  7. docker run 或者 docker restart 启动镜像就自动退出
  8. jieba分词工具的使用方法
  9. 2021年大数据Hadoop(十二):HDFS的API操作
  10. Git基本命令和GitFlow工作流