如何避免Rust中大型表达式的二次方级编译耗时?
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行重复内容 // ... +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 ; }
编译此代码时会触发Segmentation fault (core dumped)错误(减少行数可正常编译,推测是表达式过长导致崩溃)。
已尝试的解决方案
额外括号
尝试在多处添加括号,为每行首尾加括号可避免崩溃,但编译耗时长达1小时,且会改变运算顺序的语义:
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行重复内容 // ... +(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) ; }
fasteval库
fasteval可快速处理表达式,但仅支持基础变量操作与函数调用,无法兼容复杂Rust代码与表达式的混合场景。
变量拆分
将每个表达式拆分为大量生成变量(每个二元运算符对应一个变量)可避免崩溃,但编译耗时仍与变量数量呈二次方关系(如8000个数相加耗时9秒,16000个数相加耗时37秒)。
分解为多个函数
该方案理论可行,但会大幅增加代码生成的工作量。
修改编译器
查看Rust编译器源码后发现,编译缓慢可能源于语法树操作时大量克隆抽象语法节点。由于底层数据多为 immutable,可考虑复用引用而非复制整棵树,但实现难度较大。
核心疑问
还有哪些方法可以避免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 | +-------------------------------------------------+-----------+-----------------+----------+------------+ ...

改用括号与i32后的性能变化
根据建议,使用1i32可避免typeck耗时。3000行文件的性能分析显示,耗时主要集中在thir_body:
+-------------------------------------------------+-----------+-----------------+----------+------------+ | 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 | +-------------------------------------------------+-----------+-----------------+----------+------------+
内容的提问来源于stack exchange,提问作者Peter de Rivaz
相关产品推荐
相关产品推荐

