我想为蒙特卡罗模拟处理几百个二进制数据块(“场景”)。每个场景包含100万个浮点数。以下是我为场景数据创建虚拟二进制文件的方法:
import Data.Binary
import qualified Data.ByteString.Lazy as B
import Data.Array.Unboxed
scenSize = 1000000
scens = 100
main = do
let xs = array (1,scenSize) [(i, 0.0) | i <- [1..scenSize]] :: UArray Int Float
let l = take scens $ Prelude.repeat xs
B.writeFile "bintest.data" (encode l)
return ()
这很好用。现在我想处理这些场景。由于可能存在很多场景(场景= 1000左右),因此处理应该一次懒得一个块。我尝试了decodeFile
,但这似乎不起作用:
import Data.Binary
import qualified Data.Array.IArray as IA
import Data.Array.Unboxed as A
main = do
bs <- decodeFile "bintest.data" :: IO [UArray Int Float]
mapM_ doStuff bs
return ()
doStuff b =
Prelude.putStrLn $ show $ b IA.! 100000
该程序似乎首先将所有数据加载到内存中,然后在运行结束时打印所有数字。它在我的32位Ubuntu机器上使用了很多内存和崩溃的scens = 500。
我做错了什么?是否有一种简单的方法可以使程序懒惰地运行?
答案 0 :(得分:4)
decodeFile
不是懒惰的,只需查看源-it调用decodeOrFail
,它本身必须解析整个文件以确定成功或失败。
编辑:
所以我认为在原版binary
中工作的东西现在已经被破坏了(阅读:它现在是一个非懒惰的记忆猪)。我怀疑最好的一个解决方案是使用惰性readFile
和runGetIncremental
然后手动将块推入解码器:
import Data.Binary
import Data.Binary.Get
import Data.ByteString.Lazy as L
import Data.ByteString as B
import qualified Data.Array.IArray as IA
import Data.Array.Unboxed as A
main = do
bs <- getListLazy `fmap` L.readFile "bintest2.data"
mapM_ doStuff bs
return ()
doStuff b = print $ b IA.! 100000
重要的是:
getListLazy :: L.ByteString -> [UArray Int Float]
getListLazy lz = go decodeUArray (L.toChunks lz)
where
go :: Decoder (UArray Int Float) -> [B.ByteString] -> [UArray Int Float]
go _ [] = []
go dec (b:bs) =
case pushChunk dec b of
Done b' o a -> a : go decodeUArray (b' : bs)
Partial f -> case bs of
(x:xs) -> go (f $ Just x) xs
[] -> []
Fail _ _ s -> error s -- alternatively use '[]'
decodeUArray :: Decoder (UArray Int Float)
decodeUArray = runGetIncremental get
请注意,这个解决方案没有麻烦解码然后通过解码器管理列表长度 - 我只是更改了生成器代码以输出大量数组而不是数组列表。
为了避免像这样的代码,我认为管道是可行的方法。