Haskell Lazy ByteString:某些情况下内存爆炸
下面我们有两个看起来功能相当的程序。对于第一种情况,内存保持不变,而对于第二种情况,内存爆炸(在Ubuntu 14.04 64位中使用ghc 7.8.2和bytestring-0.10.4.0): 非爆炸性:Haskell Lazy ByteString:某些情况下内存爆炸,haskell,lazy-evaluation,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 = 1000000
--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
爆炸:
--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
其他详细信息:
--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
中,我从test1
的定义中提取了BL.take num$BLC.cycle$BLC.pack“abc”
,并将其分配给自己的值longByteStr
奇怪的是,如果我们在Explode.hs
中注释掉print test1
或print test2
(但显然不是两者都注释掉),那么程序就不会爆炸
内存在
Explode.hs
中爆炸而不是在NoExplode.hs
中爆炸是否有原因,以及为什么爆炸程序(Explode.hs
)同时需要print test1
和print test2
才能爆炸?/p>为什么ghc
在一种情况下执行公共表达式消除,但不是在另一个?谁知道呢。可能是被内联杀死的常见表达式。基本上,这取决于内部实施
关于-ddump simp
,请参见此SO问题:
我用
ghc-7.8.2
复制了它。它执行公共表达式消除。您可以检查-ddump siml
的输出。所以你实际上创建了一个懒惰的bytestring
在第一个版本中,您创建了两个lazy bytestring<代码>打印测试1强制执行第一个测试,但它是动态垃圾收集的,因为没有其他人使用它。与打印test2相同,它强制第二个bytestring,并动态地执行GC'ed
在第二个版本中,您创建了一个lazybytestring<代码>打印测试1强制它,但它不能被GC'ed,因为它是
打印测试2
所需要的。因此,在第一次打印后
将整个bytestring加载到内存中
如果删除一个打印
,则bytestring将再次被动态GC'ed。因为它不在其他任何地方使用
PS.“GC'ed on fly”的意思是:
print
获取第一个块并将其输出到stdout
。该区块可用于GC。然后,prints
获取第二个块,等等。“在第二个版本中,您创建了一个lazy bytestring。print test1
强制它,但它不能被GC'ed,因为它是print test2
所需要的:我不确定我是否完全理解这一点longByteStr
仅用于test1
,而不用于test2
@artella-Hmm。。是的,我错过了,对不起。但是我不能用ghc-7.6
复制它。您使用的编译器版本是什么?您好,我在Ubuntu 64位上使用了ghc-7.8.2
,我已经测试了好几次,并且可以一致地复制它。我使用的是7.6.3
,我用7.8.2
复制了它。看看最新的答案。啊,真狡猾。我没有意识到这种优化。我已经通过将Explode.hs中的test2
表达式中的abc
更改为def
间接验证了这一点,在这种情况下,程序不再爆炸!谢谢