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),这非常有意义