【发布时间】:2018-12-08 23:18:48
【问题描述】:
为什么这个函数会导致内存占用过高,有什么减少内存占用的建议吗?
编辑:一个更简单的例子
示例(1)GC发现每个元素打印后都不需要内存了:
printThings = readThing >=> mapM_ (parseThing >>> print)
示例(2)整个列表保存在内存中
printThings = readThing >=> map parseThing >>> print
请注意,在下面我的确切问题中,我折叠了地图结果,希望只评估每个元素,然后让 GC 释放该元素。
我有一个程序可以读取数据、解析数据并减少数据。作为一个最小的例子:
aFoo :: FilePath -> IO ()
aFoo = readFile >=> lines >>> map convertStringToB >>> reduceBsToC >>> print
reduceBsToC = foldl' bToC base
更具体地说,我正在懒洋洋地阅读一个文件:
import Data.ByteString.Lazy.Char8 as B
actualFoo = B.readFile >=> B.split '\n' >>> map convertByteStringToB >>> reduceBsToC >>> print)
我看到这个程序的内存使用量很大(我的输入约为 4GB):
- 正在将整个文件读入内存
- 或者更可能的是,
map的整个结果都存储在内存中
我期待 map convertByteStringStringToB 创建的 [B] 会被折叠懒惰地阅读。如果我只打印 [B] 我看不到这种行为,并且使用的内存要少得多(~10MB):
readFoo :: FilePath -> IO [ByteString]
readFoo = B.readFile >=> B.split '\n' >>> return
printFoo :: FilePath -> IO ()
printFoo = readFoo >=> mapM_ (convertByteStringToB >>> print)
-- Lazily reading in file and converting each 'line'
我知道foldl'的实现是:
foldl' f z [] = z
foldl' f z (x:xs) = let z' = z `f` x
in seq z' $ foldl' f z' xs
我假设(x:xs) 使用一个thunk 来表示xs,否则map 操作的整个结果将在内存中。
编辑
convertByteStringToC 和 reduceBsToC 被要求澄清:
convertByteStringToC 是一个 Megaparsec 函数,对于这种格式来说太长了。
reduceBsToC 使用fgl。 (简化):
type MyGraph = Gr UNode UEdge
reduceBsToC :: MyGraph -> B -> MyGraph
reduceBsToC gr End = gr
reduceBsToC gr b = maybe makeDefault setGraph (tryAddToGr gr b)
【问题讨论】:
-
convertByteStringToB和reductBsToC是什么?原因可能就在那里。 -
感谢您提供最小示例!您是否也可以将其重写为完整且可验证?见MCVE。