折叠地图操作结果时的懒惰

时间:2018-06-29 18:02:57

标签: haskell lazy-evaluation

为什么此功能会导致较高的内存使用率,并且有任何建议减少内存使用率?

编辑:一个更简单的示例

示例(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操作的整个结果将在内存中。


编辑

convertByteStringToCreduceBsToC进行了澄清:

convertByteStringToCMegaparsec函数,对于这种格式而言太长了。

reduceBsToC使用fgl。 (简体):

type MyGraph = Gr UNode UEdge
reduceBsToC :: MyGraph -> B -> MyGraph
reduceBsToC gr End = gr
reduceBsToC gr b = maybe makeDefault setGraph (tryAddToGr gr b)

2 个答案:

答案 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用于折叠时,它会在模式匹配之前构建。