Lazy ByteString:在某些情况下内存爆炸

时间:2014-06-18 15:21:48

标签: haskell lazy-evaluation

下面我们有两个看似功能相同的程序。第一个内存保持不变,而第二个内存爆炸(在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 test2Explode.hs(但显然不是两者),那么该程序就不会爆炸。

是否存在Explode.hs而非NoExplode.hs中的内存爆炸的原因,以及爆炸计划(Explode.hs)同时需要print test1的原因和print test2以便发布?

1 个答案:

答案 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获取第二个块等......