下面我们有两个看似功能相同的程序。第一个内存保持不变,而第二个内存爆炸(在Ubuntu 14.04 64位中使用ghc 7.8.2& bytestring-0.10.4.0):
不爆炸:
--NoExplode.hs
--ghc -O3 NoExplode.hs
module Main where
import Data.ByteString.Lazy as BL
import Data.ByteString.Lazy.Char8 as BLC
num = 1000000000
bytenull = BLC.pack ""
countDataPoint arg sum
| arg == bytenull = sum
| otherwise = countDataPoint (BL.tail arg) (sum+1)
test1 = BL.last $ BL.take num $ BLC.cycle $ BLC.pack "abc"
test2 = countDataPoint (BL.take num $ BLC.cycle $ BLC.pack "abc") 0
main = do
print test1
print test2
爆炸:
--Explode.hs
--ghc -O3 Explode.hs
module Main where
import Data.ByteString.Lazy as BL
import Data.ByteString.Lazy.Char8 as BLC
num = 1000000000
bytenull = BLC.pack ""
countDataPoint arg sum
| arg == bytenull = sum
| otherwise = countDataPoint (BL.tail arg) (sum+1)
longByteStr = BL.take num $ BLC.cycle $ BLC.pack "abc"
test1 = BL.last $ longByteStr
test2 = countDataPoint (BL.take num $ BLC.cycle $ BLC.pack "abc") 0
main = do
print test1
print test2
其他详细信息:
不同之处在于Explode.hs
我从BL.take num $ BLC.cycle $ BLC.pack "abc"
的定义中取出test1
,并将其分配给自己的值longByteStr
。
奇怪的是,如果我们在print test1
中注释掉print test2
或Explode.hs
(但显然不是两者),那么该程序就不会爆炸。
是否存在Explode.hs
而非NoExplode.hs
中的内存爆炸的原因,以及爆炸计划(Explode.hs
)同时需要print test1
的原因和print test2
以便发布?
答案 0 :(得分:4)
为什么ghc
在一种情况下执行常见表达式消除,而在另一种情况下不执行?谁知道。也许通过内联杀死的常见表达式。基本上它取决于内部实施。
关于-ddump-simp
,请参阅此问题:Reading GHC Core
我用ghc-7.8.2
复制了它。它执行常见的表达式消除。您可以检查-ddump-simpl
的输出。所以你实际上创建了一个惰性字节串。
在第一个版本中,您创建了两个惰性字节串。 print test1
强制第一个,但它是垃圾收集,因为没有其他人使用它。同样适用于print test2
- 它会强制第二个字节串,并且它是动态的GC。
在第二个版本中,您将创建一个惰性字节字符串。 print test1
强制它,但它不能被GC编辑,因为print test2
需要它。因此,在第一个print
之后,您将整个字节字符串加载到内存中。
如果删除一个print
,则字节字符串会在运行中再次进行GC。因为它没有在其他任何地方使用。
PS。 " GC即时播放"表示:print
获取第一个块并将其输出到stdout
。块可用于GC。然后prints
获取第二个块等......