使用大型表达式(8k 行需要一小时编译)时,是否可以避免 Rust 中的二次编译时间?

Pet*_*vaz 13 rust

背景

我有一个代码生成器,可以在 Rust 中生成长表达式,但编译器似乎不能很好地处理这个问题。在这些示例中,为了简单起见,我使用常量/加法,但实际上我希望支持涉及变量和其他编程结构的更复杂的表达式。

例子

fn main() {
  let _x = 1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1
+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1
+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1
// ...
// 500 identical lines
// ...
+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1
+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1
;
}
Run Code Online (Sandbox Code Playgroud)

当我尝试编译它时它崩溃了Segmentation fault (core dumped)。(它的中间行数较少,所以我认为这只是表达式长度引起的崩溃。)

我尝试过的

额外的括号

我尝试过在不同的地方放入括号。在每行的开头和结尾添加括号可以阻止崩溃,但需要一小时来编译(并且由于操作顺序不同,还会更改语义):

fn main() {
  let _x = 1+(1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1)
+(1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1)
+(1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1)
// ...
// 8000 identical lines
// ...
+(1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1)
+(1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1)
;
}
Run Code Online (Sandbox Code Playgroud)

法斯特瓦尔

fasteval看起来处理表达式的速度要快得多,但只能处理基本的变量操作和函数调用,因此不会处理表达式和更复杂的 Rust 代码的混合。

变量

将每个表达式分解为许多生成的变量(每个二元运算符一个)也可以阻止崩溃,但变量数量的编译时间仍然是二次方(例如,添加 8000 个数字需要 9 秒,添加 16000 个数字需要 37 秒)。

将表达式分解为多个函数

这感觉应该可行,但代码生成方面需要做很多工作。

改变编译器

查看 Rust 编译器源代码,看起来速度缓慢可能是由于在进行树操作时对抽象语法节点进行了大量克隆。感觉很多时候底层数据是不可变的,因此可以重用对现有不可变数据的引用,而不是复制所有树。然而,这感觉可能需要相当多的努力才能开始工作。

问题

有没有其他方法可以避免 Rust 中大型表达式的编译时间过慢?(这是我的第一个 Rust 程序,所以我可能错过了一些明显的东西,比如编译器标志或更惯用的编写表达式的方式)

更新

编译器的配置文件输出示例。

+-------------------------------------------------+-----------+-----------------+----------+------------+
| Item                                            | Self time | % of total time | Time     | Item count |
+-------------------------------------------------+-----------+-----------------+----------+------------+
| typeck                                          | 4.99s     | 69.503          | 4.99s    | 1          |
+-------------------------------------------------+-----------+-----------------+----------+------------+
| thir_body                                       | 1.73s     | 24.112          | 1.73s    | 1          |
+-------------------------------------------------+-----------+-----------------+----------+------------+
| mir_built                                       | 328.48ms  | 4.572           | 2.06s    | 1          |
+-------------------------------------------------+-----------+-----------------+----------+------------+
| run_linker                                      | 50.88ms   | 0.708           | 50.88ms  | 1          |
+-------------------------------------------------+-----------+-----------------+----------+------------+
...
Run Code Online (Sandbox Code Playgroud)

轮廓

更改为使用括号和 i32

正如 Chayim Friedman 在下面的回答中所建议的,可以通过使用 1i32 来避免 typeck 时间。该配置文件现在大部分时间都花在 thir_body 中(来自 3k 行文件的配置文件):

+-------------------------------------------------+-----------+-----------------+----------+------------+
| Item                                            | Self time | % of total time | Time     | Item count |
+-------------------------------------------------+-----------+-----------------+----------+------------+
| thir_body                                       | 6.49s     | 73.044          | 6.49s    | 1          |
+-------------------------------------------------+-----------+-----------------+----------+------------+
| mir_built                                       | 1.02s     | 11.506          | 7.51s    | 1          |
+-------------------------------------------------+-----------+-----------------+----------+------------+
| mir_borrowck                                    | 402.33ms  | 4.529           | 7.93s    | 1          |
+-------------------------------------------------+-----------+-----------------+----------+------------+
| mir_drops_elaborated_and_const_checked          | 394.76ms  | 4.444           | 394.80ms | 1          |
+-------------------------------------------------+-----------+-----------------+----------+------------+
| typeck                                          | 306.75ms  | 3.453           | 312.72ms | 1          |
+-------------------------------------------------+-----------+-----------------+----------+------------+
| optimized_mir                                   | 118.45ms  | 1.333           | 513.32ms | 5          |
+-------------------------------------------------+-----------+-----------------+----------+------------+
| run_linker                                      | 52.22ms   | 0.588           | 52.22ms  | 1          |
+-------------------------------------------------+-----------+-----------------+----------+------------+
Run Code Online (Sandbox Code Playgroud)

Cha*_*man 11

问题是它1不是固定类型,它是一个推断变量,可以是任何整数类型。为了确定它是i32,编译器必须遍历Addimpl 的所有组合,并回退到后备类型i32

使用后缀整数 ( 1i32) 编译速度会快得多。