Performance GHC 8.0.x中的Foldl内存性能

Performance GHC 8.0.x中的Foldl内存性能,performance,haskell,fold,Performance,Haskell,Fold,我在检查一些代码的内存使用情况时遇到了一个奇怪的问题 使用foldl对一个非常大的列表中的元素求和,我得到了一个恒定的内存使用量 使用foldl'我还可以获得恒定的内存使用率(正如预期的那样) 使用foldr内存会增长,并使我的系统崩溃(没有我预期的堆栈溢出异常) 触发它所需的最低代码是: main=打印$foldx(+)0[1..1000000000000] 其中foldx是foldl,foldr或foldl' 我当时的印象是,情况正好相反 我使用上述代码设置回购: 这是怎么回事?是GH

我在检查一些代码的内存使用情况时遇到了一个奇怪的问题

使用
foldl
对一个非常大的列表中的元素求和,我得到了一个恒定的内存使用量

使用
foldl'
我还可以获得恒定的内存使用率(正如预期的那样)

使用
foldr
内存会增长,并使我的系统崩溃(没有我预期的堆栈溢出异常)

触发它所需的最低代码是:

main=打印$foldx(+)0[1..1000000000000]

其中foldx是
foldl
foldr
foldl'

我当时的印象是,情况正好相反

我使用上述代码设置回购:

这是怎么回事?是GHC 8.0.x太聪明了吗? 我在macOS Sierra上

谢谢

foldl
foldl'
在这种情况下,GHC认为可以对
foldl
进行严格的修改,并将其重写为使用
foldl'
。请参见下文GHC如何优化foldl结构

请注意,这仅适用于使用优化
-O
编译的情况。没有优化,
foldl
程序会消耗我所有的内存并崩溃


查看
ghc-O-fforce recomp-ddump siml foldl.hs的输出,我们可以看到ghc完全消除了使用的庞大列表,并将表达式优化为尾部递归函数:

Rec {
-- RHS size: {terms: 20, types: 5, coercions: 0, joins: 0/0}
Main.main_go [Occ=LoopBreaker] :: Integer -> Integer -> Integer
[GblId, Arity=2, Str=<S,U><S,1*U>]
Main.main_go
  = \ (x_a36m :: Integer) (eta_B1 :: Integer) ->
      case integer-gmp-1.0.0.1:GHC.Integer.Type.gtInteger#
             x_a36m lim_r4Yv
      of wild_a36n
      { __DEFAULT ->
      case GHC.Prim.tagToEnum# @ Bool wild_a36n of {
        False ->
          Main.main_go
            (integer-gmp-1.0.0.1:GHC.Integer.Type.plusInteger
               x_a36m 1)
            (integer-gmp-1.0.0.1:GHC.Integer.Type.plusInteger eta_B1 x_a36m);
        True -> eta_B1
      }
      }
end Rec }
1000000000000000000
的限制对于Thunk占用比RAM更多的空间和程序崩溃来说非常大。

foldl
foldl'
在这种情况下,GHC认为可以对
foldl
进行严格的修改,并将其重写为使用
foldl'
。请参见下文GHC如何优化foldl结构

请注意,这仅适用于使用优化
-O
编译的情况。没有优化,
foldl
程序会消耗我所有的内存并崩溃


查看
ghc-O-fforce recomp-ddump siml foldl.hs的输出,我们可以看到ghc完全消除了使用的庞大列表,并将表达式优化为尾部递归函数:

Rec {
-- RHS size: {terms: 20, types: 5, coercions: 0, joins: 0/0}
Main.main_go [Occ=LoopBreaker] :: Integer -> Integer -> Integer
[GblId, Arity=2, Str=<S,U><S,1*U>]
Main.main_go
  = \ (x_a36m :: Integer) (eta_B1 :: Integer) ->
      case integer-gmp-1.0.0.1:GHC.Integer.Type.gtInteger#
             x_a36m lim_r4Yv
      of wild_a36n
      { __DEFAULT ->
      case GHC.Prim.tagToEnum# @ Bool wild_a36n of {
        False ->
          Main.main_go
            (integer-gmp-1.0.0.1:GHC.Integer.Type.plusInteger
               x_a36m 1)
            (integer-gmp-1.0.0.1:GHC.Integer.Type.plusInteger eta_B1 x_a36m);
        True -> eta_B1
      }
      }
end Rec }

1000000000000000000
的限制对于thunks占用比RAM更多的空间和程序崩溃来说非常大。

在这种特定情况下,GHC可能正在优化
foldl
以接近
foldl'
。除此之外,其余的应该是预期的行为:
foldl
应该在恒定空间中运行,而
foldr
应该分配大量thunk,因为
(+)
是严格的。在这种特定情况下,GHC可能正在优化
foldl
以接近
foldl'
。除此之外,其余的应该是预期的行为:
foldl'
应该在恒定空间中运行,而
foldr
应该分配大量thunk,因为
(+)
是严格的。感谢ThreeFx(和chi),这非常有意义