这里有两个版本的简单算术表达式求值器(游乐场链接:https://play.rust-lang.org/?version=nightly&mode=release&edition=2021&gist=d3da06b0077b29e0e3ac85720c567dd8)。
第二个版本使用递归调用来重用一些代码(显然在这个玩具示例中不值得这样做,但这就是为什么它是一个玩具示例)。
我相信一个足够聪明的编译器可以意识到对
有没有能够进行这种类型优化的优化编译器(适用于任何语言)?在编译器文献中是否有一个对这种类型优化的名称?这个问题在不久的将来是否有望得到正确优化,还是说(一般版本的)这是一个非常困难的开放性问题?
注意:我也将此标记为C++,因为我的问题并不特定于Rust,尽管我的示例是(据我所知,这种优化可能存在于LLVM的与语言无关的传递中)
编辑: TCO(尾调用优化)不是我要找的 - 上面的逻辑不依赖于递归调用处于尾位置。另外,与一些最初的评论相反,Clang在C++的一般情况下并不能解决这个问题 - 这里有一个被修改过的示例,其中递归调用不在尾位置,并且递归调用没有内联。https://godbolt.org/z/cGabvvvro(是的,一个版本打印了两次 - 不影响主要观点)
eval_slow(Sum(...))
的递归调用本身并没有进行任何递归调用,因此是安全的进行内联,并且eval_slow
实际上应该编译成与eval_fast
相同的汇编代码。实际上,rustc目前并没有执行这种优化,eval_slow
包含一个递归调用(请参见游乐场链接中的汇编输出)。有没有能够进行这种类型优化的优化编译器(适用于任何语言)?在编译器文献中是否有一个对这种类型优化的名称?这个问题在不久的将来是否有望得到正确优化,还是说(一般版本的)这是一个非常困难的开放性问题?
pub enum Expr {
Lit(isize),
Sum(isize, isize),
Sub(isize, isize),
}
// simple and fast
pub fn eval_fast(expr: Expr) -> isize {
use Expr::*;
match expr {
Lit(x) => x,
Sum(x, y) => x + y,
Sub(x, y) => x - y
}
}
// we'd like to inline the recursive call to `eval_slow(Sum(...))`, but it doesn't happen.
pub fn eval_slow(expr: Expr) -> isize {
use Expr::*;
match expr {
Lit(x) => x,
Sum(x, y) => x + y,
Sub(x, y) => eval_slow(Sum(x, -y))
}
}
注意:我也将此标记为C++,因为我的问题并不特定于Rust,尽管我的示例是(据我所知,这种优化可能存在于LLVM的与语言无关的传递中)
编辑: TCO(尾调用优化)不是我要找的 - 上面的逻辑不依赖于递归调用处于尾位置。另外,与一些最初的评论相反,Clang在C++的一般情况下并不能解决这个问题 - 这里有一个被修改过的示例,其中递归调用不在尾位置,并且递归调用没有内联。https://godbolt.org/z/cGabvvvro(是的,一个版本打印了两次 - 不影响主要观点)
__force_inline
关键字。你可以在这里找到更多相关信息。 - HelpfulHelper#[inline(always)]
,它会进行内联,所以我猜这回答了问题吧? - Chayim Friedman#[inline(always)]
是无效的。它只是延迟了该函数的代码生成(所有inline
函数都在调用点生成),所以你在 godbolt 上看不到代码,但它会在调用点生成递归。请参考:https://godbolt.org/z/cveh447T7 - Angelicos Phosphoros