标准导致内存消耗爆炸,没有CAF在眼前 [英] Criterion causing memory consumption to explode, no CAFs in sight
问题描述
基本上我有一个简单的函数调用,当与 Criterion < 假设我有以下程序: 然后这个程序(用O0编译)运行良好,没有 如果我们使用 似乎没有CAF正在生产,与 然后内存消耗爆炸。然而,打印 似乎没有CAF存在产生的。因此,为什么 在没有 对于 看来( Basically I have a simple function call, which
when used in conjunction with Criterion, results in
the memory consumption exploding. Suppose I have the following program : Then this program (compiled with O0) runs fine, without
the memory exploding. If we use It seems that no CAFs are being produced, which is consistent
with the fact that the program does not explode. Now if I
run the following program which uses criterion 1.1.0.0 : then the memory consumption explodes. However printing
the core yields : and it seems that no CAFs are being produced. Therefore why
does the latter program, which uses criterion, result in the memory consumption exploding, whereas the former program
does not? I am using GHC version 7.8.3 In your version without For the and It seems like 这篇关于标准导致内存消耗爆炸,没有CAF在眼前的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
<$
模块主要其中
import Criterion.Main $ b $ {$#$} $ b import Data.List
num :: Int
num = 10000000
lst :: a - > [Int]
lst _ = [1,2..num]
myadd :: Int - > Int - > Int
myadd!x!y = let!result = x + y in
result
mysum = foldl'myadd 0
main :: IO ()
main = do
print $ mysum(lst())
的内存爆炸。
cabal build -v
产生一个调用的编译
命令的转储,然后标记 -ddump-simpl -fforce-recomp -O0 -dsuppress-all
(建议在 IO / Monadic赋值运算符导致ghci爆炸无限列表)到 ghc - -make -no-link ...
命令,我们得到以下核心:
num
num = I#10000000
lst
lst = \ @ a_a3Yn _ - > enumFromThenTo $ fEnumInt(I#1)(I#2)num
myadd
myadd =
\ x_a3Cx y_a3Cy - >
case x_a3Cx of x1_X3CC {I#ipv_s4gX - >
case y_a3Cy of y1_X3CE {I#ipv1_s4h0 - >
+ $ fNumInt x1_X3CC y1_X3CE
}
}
mysum
mysum = foldl'myadd(I#0)
main
main =
print
$ fShowInt(mysum(enumFromThenTo $ fEnumInt(I#1)(I#2)num))
main
main = runMainIO main
一致该程序不会爆炸的事实。现在,如果我
运行使用条件1.1.0.0的以下程序:
{ - #OPTIONS_GHC -fno-cse # - }
{ - #LANGUAGE BangPatterns# - }
模块Main其中
import Criterion.Main
import Data.List
num :: Int
num = 10000000
lst :: a - > [Int]
lst _ = [1,2..num]
myadd :: Int - > Int - > Int
myadd!x!y = let!result = x + y in
result
mysum = foldl'myadd 0
main :: IO ()
main = defaultMain [
bgroupsummation
[benchmysum$ whnf mysum(lst())]
]
核心产量:
pre $ $ $ $ $ $ $ $ $ $ $#$ 10000000
lst
lst = \ @ a_a3UV _ - > enumFromThenTo $ fEnumInt(I#1)(I#2)num
myadd
myadd =
\ x_a3Cx y_a3Cy - >
case x_a3Cx of x1_X3CC {I#ipv_s461 - >
case y_a3Cy of y1_X3CE {I#ipv1_s464 - >
+ $ fNumInt x1_X3CC y1_X3CE
}
}
mysum
mysum = foldl'myadd(I#0)
main
main =
defaultMain
(:(bgroup
(unpackCString#summation#)
(:( bench bench
(unpackCString#mysum #)
(whnf mysum(enumFromThenTo $ fEnumInt(I#1)(I#2)num)))
([])))
([]))
main
main = runMainIO main
做后者的程序,它使用标准,导致内存消耗爆炸,而前一个程序
没有?我使用GHC版本7.8.3 标准的版本中
,由 lst()
返回的列表被懒惰地产生,然后增量地收集垃圾,而 mysum
消耗它,因为那里没有其他的引用。
标准
版本,请看 whnf $ c的定义
whnf ::(a - > b) - > a - >可执行基准
whnf = pureFunc id
{ - #INLINE $#
$ $ $ $和 pureFunc
:
pureFunc ::(b - > c) - > (a - > b) - > a - >基准
pureFunc降低f0 x0 =基准$ f0 x0
其中fx n
| n< = 0 = return()
|否则=评估(减少(f x))>>去fx(n-1)
{ - #INLINE pureFunc# - }
x
)在 go
中最终会被绑定到您的 lst()
,而 n
是基准测试的迭代次数。当第一次基准迭代完成时, x
将全部被评估,但是这次它不能被垃圾收集:它仍然保存在内存中,因为它是共享的通过递归去掉fx(n-1)
。{-# OPTIONS_GHC -fno-cse #-}
{-# LANGUAGE BangPatterns #-}
module Main where
import Criterion.Main
import Data.List
num :: Int
num = 10000000
lst :: a -> [Int]
lst _ = [1,2..num]
myadd :: Int -> Int -> Int
myadd !x !y = let !result = x + y in
result
mysum = foldl' myadd 0
main :: IO ()
main = do
print $ mysum (lst ())
cabal build -v
to yield a dump of the compilation
commands invoked, and then tag -ddump-simpl -fforce-recomp -O0 -dsuppress-all
(suggested in IO/Monadic assign operator causing ghci to explode for infinite list) to the end of the ghc --make -no-link ...
command, we get the following core : num
num = I# 10000000
lst
lst = \ @ a_a3Yn _ -> enumFromThenTo $fEnumInt (I# 1) (I# 2) num
myadd
myadd =
\ x_a3Cx y_a3Cy ->
case x_a3Cx of x1_X3CC { I# ipv_s4gX ->
case y_a3Cy of y1_X3CE { I# ipv1_s4h0 ->
+ $fNumInt x1_X3CC y1_X3CE
}
}
mysum
mysum = foldl' myadd (I# 0)
main
main =
print
$fShowInt (mysum (enumFromThenTo $fEnumInt (I# 1) (I# 2) num))
main
main = runMainIO main
{-# OPTIONS_GHC -fno-cse #-}
{-# LANGUAGE BangPatterns #-}
module Main where
import Criterion.Main
import Data.List
num :: Int
num = 10000000
lst :: a -> [Int]
lst _ = [1,2..num]
myadd :: Int -> Int -> Int
myadd !x !y = let !result = x + y in
result
mysum = foldl' myadd 0
main :: IO ()
main = defaultMain [
bgroup "summation"
[bench "mysum" $ whnf mysum (lst ())]
]
num
num = I# 10000000
lst
lst = \ @ a_a3UV _ -> enumFromThenTo $fEnumInt (I# 1) (I# 2) num
myadd
myadd =
\ x_a3Cx y_a3Cy ->
case x_a3Cx of x1_X3CC { I# ipv_s461 ->
case y_a3Cy of y1_X3CE { I# ipv1_s464 ->
+ $fNumInt x1_X3CC y1_X3CE
}
}
mysum
mysum = foldl' myadd (I# 0)
main
main =
defaultMain
(: (bgroup
(unpackCString# "summation"#)
(: (bench
(unpackCString# "mysum"#)
(whnf mysum (enumFromThenTo $fEnumInt (I# 1) (I# 2) num)))
([])))
([]))
main
main = runMainIO main
criterion
, the list returned by lst ()
gets produced lazily and then incrementally garbage collected while mysum
consumes it, since there are no other references to the list.criterion
version, however, look at the definition of whnf
:whnf :: (a -> b) -> a -> Benchmarkable
whnf = pureFunc id
{-# INLINE whnf #-}
pureFunc
:pureFunc :: (b -> c) -> (a -> b) -> a -> Benchmarkable
pureFunc reduce f0 x0 = Benchmarkable $ go f0 x0
where go f x n
| n <= 0 = return ()
| otherwise = evaluate (reduce (f x)) >> go f x (n-1)
{-# INLINE pureFunc #-}
x
in go
above would eventually become bound to the list returned by your lst ()
, while n
is the number of iterations for the benchmarking. When the first benchmark iteration is finished, x
will all have been evaluated, but this time it cannot be garbage collected: It is still kept in memory because it is shared with the following iterations through the recursive go f x (n-1)
.