递归函数的内联优化,借助 where 子句(在 Haskell 中)

Inline optimization for recursive functions, with the help of a where clause (in Haskell)

序曲

  1. Link 1 这个 post 说:

"For a self-recursive function, the loop breaker can only be the function itself, so an INLINE pragma is always ignored," according to the GHC manual

  1. 这个答案描述了一种通过 GHC 内联进行优化的方法。

  2. Link 3 这(无关)post 说明编译器无论如何都可以自由拒绝显式内联建议。或者也许将 -O2 标志传递给编译器可以达到与下面显示的优化版本相同的效果?

示例代码

(如 Link 2 所示)

--PRE-OPTIMIZED VERSION--
func :: (a -> a) -> [a] -> [a]
func f [] = []
func f [x] = [x]
func f (x:s:xs) = x:(f s):(func f xs)

----OPTIMIZED VERSION----
func :: (a -> a) -> [a] -> [a]
func f = loop
  where
    loop []  = []
    loop [x] = [x]
    loop (x:s:xs) = x : f s : loop xs 

------Eg-USAGE----------
> mapToEverySecond (*2) [1..10]
[1,4,3,8,5,12,7,16,9,20] 

问题

引用@amalloy 在 中的疑惑,当然

f is "still implicitly passed around just as much [as explicitly passing f in the PRE-OPTIMIZED VERSION] by the closure-creating machinery" ?

顺便说一句,我正在阅读融合技术,这是 Link 1 中描述的另一种方法,尽管我并不想寻求最快的实现来代替惯用风格。

只是好奇 whether/how 在优化版本中看到的“where-definition”技巧(编译器是否决定内联这样的函数)。

欢迎补充链接!

这是一个相当糟糕的示例,因为内联此特定代码几乎没有任何好处。但是在重要的情况下,类似的模式总是会变慢。大思路是,在“优化”func中,如果我写

func myFun

GHC 可以内联 func 并获取

let
  loop []  = []
  loop [x] = [x]
  loop (x:s:xs) = x : myFun s : loop xs 
in loop

在这种情况下,这并没有更好的意义,但如果严格要求 myFun 的结果,那么它会将对任意地址的调用替换为对已知地址的调用,这样速度更快。此外,如果 myFun 在“有趣”的上下文中使用(其中关于传递给它的参数或对其结果做了什么的了解),那么 GHC 可能会内联 myFun 以利用它.