为什么此功能会导致较高的内存使用率,并且有任何建议减少内存使用率?
编辑:一个更简单的示例
示例(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)
使用重击来表示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)
答案 0 :(得分:1)
reduceBsToC
正在生成Gr
图。它表示为Map
,它不是惰性结构或流结构(它是树)。因此,折痕积累的图形可能与原始列表一样大。
答案 1 :(得分:1)
除非添加一个完整且可验证的示例,否则我能够找出问题所在。
在最后for
的最后,我的Megaparsec计算被懒惰地评估了,这意味着读取了整个文件以生成解析计算,但没有立即执行。
我在数据构造函数中添加了strict fields,在解析器中得到了print
。例如:
return
这将强制以下命令在构建MyParsedData时立即进行解析,而不是推迟解析。
data MyParsedData = MyParsedData { value1 :: !Int, value2 :: !Int }
此外,我尝试放弃严格的字段,而是使用了 BangPatterns,这也解决了该问题。这涉及添加BangPatterns编译指示,并在稍后在foldl的累加函数(参考原始问题)中对数据进行模式匹配时使用它们:
myParse = do
val1 <- parseVal1
val2 <- parseVal2
return $ MyParsedData val1 val2
这会强制在折叠过程中执行解析。
说明:当MyParsedData用于折叠时,它会在模式匹配之前构建。