是否有'span`和`fol​​dl'的优化组合,或者GHC会优化组合吗?

时间:2014-01-22 04:45:10

标签: haskell optimization ghc fold

假设我想要列出列表中的所有元素,但不包括第一个负数,并返回数字和列表的其余部分。这样做的简单方法是

addPos l = s `seq` (s,back)
  where
    (front, back) = span (>= 0) l
    s = sum front

seq应该确保没有人通过在总和之前强制退回来意外地建立一个巨大的thunk。

然而,我很好奇GHC是否足够智能以避免创建中间前沿列表。此外,有人可以解释(如果有的话)它会发现它可以严格累积总和吗? Prelude定义使用foldl而不是foldl',GHC定义看起来等同于

1 个答案:

答案 0 :(得分:5)

当我们谈论编译器优化中间列表时,我们通常会谈论在GHC的RULES编译指示中实现的“融合”;你可以了解它是如何工作的,哪些列表函数是“好消费者”和“生产者”here

不幸的是,span看起来不像是“好的制作人”。您可以通过询问GHC的核心输出并获取使用ghc -O2 -ddump-simpl -dsuppress-module-prefixes -dsuppress-uniques -ddump-core-stats -ddump-inlinings -ddump-rule-firings test.hs

触发的规则列表来亲眼看到

这是一个清理过的输出:

Rule fired: Class op >=
Rule fired: SPEC Data.List.sum
Inlining done: geInt{v r3n} [gid]
Inlining done: sum_sum1{v rkV} [gid]
Inlining done: span{v r1Q} [gid]
Inlining done: sum_sum'1{v rl6} [gid]

==================== Tidy Core ====================
Result size of Tidy Core = {terms: 24, types: 27, coercions: 0}

addPos1 :: Int -> Bool
addPos1 = \ (ds :: Int) -> case ds of _ { I# x -> >=# x 0 }

addPos [InlPrag=INLINE[0]] :: [Int] -> (Int, [Int])
addPos =
  \ (w :: [Int]) ->
    case $wspan @ Int addPos1 w of _ { (# ww1, ww2 #) ->
    case $wsum' ww1 0 of ww3 { __DEFAULT -> (I# ww3, ww2) }
    }

您可以看到我们称之为某种重写/专门span,然后是sum

您可能会看到vector库是否可以融合它们,或者看看性能如何比较有趣。