百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 优雅编程 > 正文

我是如何提升 Rust 编译器的速度?

sinye56 2024-10-22 16:16 8 浏览 0 评论

作者 | Nicholas Nethercote

译者 | 弯月,责编 | 伍杏玲

出品 | CSDN(ID:CSDNnews)

【CSDN 编者按】本文是Rust的核心研发人员,在本文中,他将分享一下再2019年,他在提升 Rust 编译器的速度上,做了哪些新的工作来优化它。

更快的Globals

libsyntax用一个全局数据结构Globals存储了3个表,分别存储了有关span(代码位置)、符号和数据清理(与宏扩展相关)的信息。访问这些表的代价很高,所以我找到了许多改进方法。

  • #59693(https://github.com/rust-lang/rust/pull/59693):AST中的每个元素都有一个span,描述了元素在原始源代码中的位置。

    每个span由偏移、长度以及与宏扩展相关的值组成。这三个字段共占12个字节,考虑到它需要添加到AST的每个元素上,12个字节太多了,而且很多时候这三个字段并不需要这么多空间。

    因此,编译器采用了只需4字节的压缩格式,而当长度超过4字节时则采用备用策略——将其存储到Globals中的哈希表中。而该PR将其改为了8个字节。这会稍稍增加内存的使用量,但上述备用策略的执行率可以从10-20%降低到不足1%,这可以加速许多工作负载,最大可以提升14%。

  • #61253(https://github.com/rust-lang/rust/pull/61253):有许多操作都需要访问数据清理信息,通常这些操作会调用两次甚至三次,因此会造成重复查找数据清理信息。该PR引入了组合操作,可以避免重复查找。这可以将packed-simd上的速度提高10%,对于普通工作负载效率可以提高3%。

  • #61484(https://github.com/rust-lang/rust/pull/61484):与上述#61253类似,只不过这个PR在许多基准测试中都赢得了提高2%的好成绩。

  • #60630(https://github.com/rust-lang/rust/pull/60630):编译器有一个驻留的字符串类型,名叫符号(symbol)。编译器对该类型的使用不太一致。因此,导致许多符号与普通字符串的比较需要逐个字符比较符号表中的字符串。

    实际上,符号与符号的比较的开销很小,只需进行整数比较。该PR删除了符号与字符串的比较操作,强迫编译器更广泛地使用符号类型。(幸运的是,大多数引入的符号都使用了静态已知、预先驻留的字符串,因此没有额外的成本。)该PR在各种基准测试中赢得了高达1%的效率提升,而且符号的使用也更为一致。

  • #60815(https://github.com/rust-lang/rust/pull/60815):与上述#60630类似,该PR也在各项基准测试中赢得了高达1%的效率提升。

  • #60467、#60910、#61035、#60973:这些PR避免了一些不必要的符号,可以进一步将编译器的效率提高1%。

其他

以下各项提升没有统一的主题。

  • #57719(https://github.com/rust-lang/rust/pull/57719):该PR将一个常用的函数改成了内联函数,可以将一个工作负载的效率提高4%。

  • #58210(https://github.com/rust-lang/rust/pull/58210):该PR改变了一个常用的断言(仅在调试版本中有效),可以将一个工作负载的效率提高20%!

  • #58207(https://github.com/rust-lang/rust/pull/58207):上述我曾提到了字符串驻留。Rust编译器还会针对其他经常出现重复值的类型使用驻留,包括一种名叫LazyConst的类型。

    然而,intern_lazy_const函数有很多Bug,而且没有真正执行驻留,它只是分配了一个新的LazyConst,而却不会检查之前是否遇到过!该PR修复了这个问题,大幅降低了内存使用率的峰值,而且页错误还减少了59%。

  • #59507(https://github.com/rust-lang/rust/pull/59507):该美化输出会针对每个缩进的空格调用write!,在某些工作负载上,缩进级别会超过100级。该PR能在绝大多数情况下将所有处理放到一个write!调用中。该PR可以在多个基准测试中体现出7%的提升。

  • #59626(https://github.com/rust-lang/rust/pull/59626):该PR更改了数据结构的预分配大小,以更好地满足实际需要,在某些工作负载上,可以将内存使用量的峰值减少20 MiB。

  • #61612(https://github.com/rust-lang/rust/pull/61612):该PR优化了解析器中的热路径,因为常量标志符会毫无目的地重复“这是不是一个关键字?”的测试,对于拥有大量常量的程序来说,该PR可以提升7%的性能。

分析改进

下面的改进涉及一些分析工具。

  • #59899(https://github.com/rust-lang/rust/pull/59899):为了让枚举变量从大到小列举,我修改了-Zprint-type-sizes的输出。该PR可以方便你看到超大的变量,特别是对于有许多变量的枚举。

  • #62110(https://github.com/rust-lang/rust/pull/62110):为了改进-Ztime-passes标志的输出,我删除了一些无用信息的输出,并添加了总编译时间的度量。

此外,我还改进了rustc-perf基准测试套件中的分析支持。首先,我添加了OProfile性能分析的支持。我承认至今我还没有发现显著的提升。在运行时,大约有一半的几率会出现错误,很让人失望。

其次,我添加了新版DHAT性能分析的支持。虽然这篇文章写自2019年,但值得一提的是,我于2018年对新DHAT的帮助做了一些改进,PR包括:#55167、#55346、#55383、#55384、#55501、#55525、#55556、#55574、#55604、#55777、#55558、#55745、#55778、#55905、#55906、#56268、#56090、#56269、#56336、#56369和#56737。

最后,我还写了一篇有关Rust性能所有基准的概要描:https://github.com/rust-lang-nursery/rustc-perf/blob/master/collector/benchmarks/README.md

流水线编译

上述改进(以及此前完成的所有改进)都属于微优化,也就是说,我使用分析数据来优化一小段代码。

但是,我们还应该考虑对Rust编译器速度进行更大规模的系统性改进。今年第二季度的时候,我曾与Alex Crichton在流水线编译项目上展开了合作,这一功能通过叠加依赖箱(crate)的编译,提升了构建多箱Rust项目时的并行度。没有流水线的编译如下图所示:

在使用了流水线后,编译如下图所示:

我承担了Rust编译器方面的工作,而Alex负责的是Cargo方面的工作。

如果你想了解其中的工作原理,以及使用方法和大量的测量数据,请阅读这篇文章:https://internals.rust-lang.org/t/evaluating-pipelined-rustc-compilation/10199

其效果高度依赖于项目的箱结构以及编译机器的配置。我们发现有些项目的性能提升可达1.84倍,而有些项目则没有任何提升。在最差的情况下,还有可能导致速度出现几乎可以忽略不计的减慢,但是无需担心,因为它不会导致任何额外的工作,只是改变了某些工作的顺序。

目前流水线编译还处于不稳定阶段,这篇文章记录了我们发现的问题:https://github.com/rust-lang/rust/issues/60988

今后的工作

我想在第三季度的时候,展开下列的工作:

  • 对于流水线编译,我想尝试在编译器前端,尽早推动元数据的创建,这有可能会进一步提高速度。

  • Rust编译器经常使用memcpy,虽然不是直接使用,但生成的代码经常为了移动值或其他原因使用memcpy。编译器不会在“检查”阶段执行任何代码生成,通常有2-8%的指令会发生在memcpy中。我想了解其中的原因,并看看是否可以改进。一种可能性是编译器中移动了超大的类型;另一种可能性是代码生成有问题。如果是前一种情况,则很容易修复;如果是后一种,则难度较大。但是,如果能修改好的话,许多Rust程序都会受益。

  • 增量编译的效率有时不是很理想。在某些工作负载上,即便做了很微小的修改,但是重新编译所需的时间与完整的非增量编译大致相同。也许我可以通过增量实现的一个小改动来提升性能。

  • 我想看看解析器中是否还有其他可以改进的热路径,比如像#61612。

不过我还有各种Firefox的工作安排,不知道是否能顺利完成上述的所有工作。

原文:https://blog.mozilla.org/nnethercote/2019/07/17/how-to-speed-up-the-rust-compiler-in-2019/

本文为 CSDN 翻译,转载请注明来源出处。

【END】

相关推荐

程序员:JDK的安装与配置(完整版)_jdk的安装方法

对于Java程序员来说,jdk是必不陌生的一个词。但怎么安装配置jdk,对新手来说确实头疼的一件事情。我这里以jdk10为例,详细的说明讲解了jdk的安装和配置,如果有不明白的小伙伴可以评论区留言哦下...

Linux中安装jdk并配置环境变量_linux jdk安装教程及环境变量配置

一、通过连接工具登录到Linux(我这里使用的Centos7.6版本)服务器连接工具有很多我就不一一介绍了今天使用比较常用的XShell工具登录成功如下:二、上传jdk安装包到Linux服务器jdk...

麒麟系统安装JAVA JDK教程_麒麟系统配置jdk

检查检查系统是否自带java在麒麟系统桌面空白处,右键“在终端打开”,打开shell对话框输入:java–version查看是否自带java及版本如图所示,系统自带OpenJDK,要先卸载自带JDK...

学习笔记-Linux JDK - 安装&配置

前提条件#检查是否存在JDKrpm-qa|grepjava#删除现存JDKyum-yremovejava*安装OracleJDK不分系统#进入安装文件目...

Linux新手入门系列:Linux下jdk安装配置

本系列文章是把作者刚接触和学习Linux时候的实操记录分享出来,内容主要包括Linux入门的一些理论概念知识、Web程序、mysql数据库的简单安装部署,希望能够帮到一些初学者,少走一些弯路。注意:L...

测试员必备:Linux下安装JDK 1.8你必须知道的那些事

1.简介在Oracle收购Sun后,Java的一系列产品就被整合到Oracle官网中,打开官网乍眼一看也不知道去哪里下载,还得一个一个的摸索尝试,而且网上大多数都是一些Oracle收购Sun前,或者就...

Linux 下安装JDK17_linux 安装jdk1.8 yum

一、安装环境操作系统:JDK版本:17二、安装步骤第一步:下载安装包下载Linux环境下的jdk1.8,请去官网(https://www.oracle.com/java/technologies/do...

在Ubuntu系统中安装JDK 17并配置环境变量教程

在Ubuntu系统上安装JDK17并配置环境变量是Java开发环境搭建的重要步骤。JDK17是Oracle提供的长期支持版本,广泛用于开发Java应用程序。以下是详细的步骤,帮助你在Ubuntu系...

如何在 Linux 上安装 Java_linux安装java的步骤

在桌面上拥抱Java应用程序,然后在所有桌面上运行它们。--SethKenlon(作者)无论你运行的是哪种操作系统,通常都有几种安装应用程序的方法。有时你可能会在应用程序商店中找到一个应用程序...

Windows和Linux环境下的JDK安装教程

JavaDevelopmentKit(简称JDK),是Java开发的核心工具包,提供了Java应用程序的编译、运行和开发所需的各类工具和类库。它包括了JRE(JavaRuntimeEnviro...

linux安装jdk_linux安装jdk软连接

JDK是啥就不用多介绍了哈,外行的人也不会进来看我的博文。依然记得读大学那会,第一次实验课就是在机房安装jdk,编写HelloWorld程序。时光飞逝啊,一下过了十多年了,挣了不少钱,买了跑车,娶了富...

linux安装jdk,全局配置,不同用户不同jdk

jdk1.8安装包链接:https://pan.baidu.com/s/14qBrh6ZpLK04QS8ogCepwg提取码:09zs上传文件解压tar-zxvfjdk-8u152-linux-...

运维大神教你在linux下安装jdk8_linux安装jdk1.7

1.到官网下载适合自己机器的版本。楼主下载的是jdk-8u66-linux-i586.tar.gzhttp://www.oracle.com/technetwork/java/javase/downl...

window和linux安装JDK1.8_linux 安装jdk1.8.tar

Windows安装JDK1.8的步骤:步骤1:下载JDK打开浏览器,找到JDK下载页面https://d.injdk.cn/download/oraclejdk/8在页面中找到并点击“下载...

最全的linux下安装JavaJDK的教程(图文详解)不会安装你来打我?

默认已经有了linux服务器,且有root账号首先检查一下是否已经安装过java的jdk任意位置输入命令:whichjava像我这个已经安装过了,就会提示在哪个位置,你的肯定是找不到。一般我们在...

取消回复欢迎 发表评论: