下面我们有两个看似功能相同的程序。第一个内存保持不变,而第二个内存爆炸(在 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
为了爆发?
最佳答案
为什么ghc
在一种情况下执行常用表达式消除,但在另一种情况下不执行?谁知道。也许是被内联杀死的常见表达式。基本上它取决于内部实现。
关于-ddump-simp
,请参阅这个 SO 问题:Reading GHC Core
我用 ghc-7.8.2
复制了它.它执行通用表达式消除。您可以检查 -ddump-simpl
的输出.所以你实际上创建了一个惰性字节串。
在第一个版本中,您创建了两个惰性字节串。 print test1
强制第一个,但它是动态收集的垃圾,因为没有其他人使用它。 print test2
也是如此-- 它强制使用第二个字节串,并且它是动态 GC 的。
在第二个版本中,您创建一个惰性字节串。 print test1
强制它,但它不能被 GC 处理,因为 print test2
需要它.结果,在第一个 print
之后您已将整个字节串加载到内存中。
如果您删除一个 print
, 字节串再次被动态 GC。因为它没有在其他任何地方使用。
PS。 “GC'ed on the fly”的意思是:print
获取第一个 block 并将其输出到 stdout
.该 block 可用于 GC。然后prints
取第二 block ,等等......
关于haskell - 懒惰的字节串 : memory exploding in certain cases,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24289369/