Haskell优化器是否在范围内对重复的函数调用使用memoization?

Nik*_*kov 12 compiler-construction optimization haskell memoization

考虑这个功能:

f as = if length as > 100 then length as else 100
Run Code Online (Sandbox Code Playgroud)

由于函数是纯粹的,因此很明显两个调用的长度都是相同的.我的问题是Haskell优化器将上面的代码转换成以下代码吗?

f as = 
  let l = length as
  in if l > 100 then l else 100
Run Code Online (Sandbox Code Playgroud)

如果是,那么哪个级别设置启用它?如果没有,那么为什么呢?在这种情况下,内存浪费不能成为本答案中解释的原因,因为一旦函数执行完成,引入的变量就会被释放.


请注意,由于本地范围,这不是此问题的重复,因此可能会得到完全不同的答案.

Don*_*art 16

GHC现在默认执行一些CSE,因为-fcse标志已打开.

默认情况下启用..启用common-sub-expression消除优化.如果你有一些不想要常见的unsafePerformIO表达式,那么关闭它会非常有用.

然而,由于引入共享(以及因此空间泄漏)的问题,它是保守的.虽然CSE传球越来越(而且这个).

最后,请注意有一个完整的CSE插件.

如果您有可以从中受益的代码.


kos*_*kus 13

即使在这样的本地环境中,仍然存在这样的情况:共享的引入总是优化并不明显.考虑这个示例定义

f = if length [1 .. 1000000] > 0 then head [1 .. 1000000] else 0
Run Code Online (Sandbox Code Playgroud)

与这一个

f = let xs = [1 .. 1000000] in if length xs > 0 then head xs else 0
Run Code Online (Sandbox Code Playgroud)

并且你会发现在这种情况下,第一个表现要好得多,因为在列表上执行的每个计算都很便宜,而第二个版本将导致列表在内存中完全展开length,并且只能被丢弃之后head减少了.

  • 尽管存在这个问题,ghc可能会对CSE更加激进.您只需要估算CSEing的值.一个简单的估计是基类型占据了可忽略的空间. (3认同)

sha*_*ang 5

您描述的案例更多地与常见的子表达式消除而不是memoization有关,但似乎GHC目前不这样做,因为意外共享可能导致空间泄漏.