使用xml-conduit parseBytes进行堆内存建立

时间:2019-03-29 12:38:53

标签: haskell memory-leaks xml-conduit

我正在使用xml-conduit的流接口https://hackage.haskell.org/package/xml-conduit-1.8.0/docs/Text-XML-Stream-Parse.html#v:parseBytes解析一些相当大的XML文件,但是我看到此内存积聚(在一个小的测试文件中):

heap use over time

顶级用户在哪里:

heap blame

实际数据不应占用太多堆-如果我进行序列化和重新读取,则常驻内存使用量为千字节,而此处为兆字节。

我设法用以下方法重现此问题的最小示例:

{-# LANGUAGE BangPatterns      #-}
{-# LANGUAGE OverloadedStrings #-}

module Main where

import           Control.Monad
import           Control.Monad.IO.Class
import           Data.Conduit
import           Data.Conduit.Binary    (sourceFile)
import qualified Data.Conduit.List      as CL
import           Data.Text              (Text)
import           Text.XML.Stream.Parse

type Y = [(Text, Text)]

main :: IO ()
main = do
  res1 <- runConduitRes $
          sourceFile "test.xml"
          .| Text.XML.Stream.Parse.parseBytes def
          .| parseMain
          .| CL.foldM get []
  print res1

get :: (MonadIO m, Show a) => [a] -> [a] -> m [a]
get acc !vals = do
 liftIO $! print vals           -- this oughta force it?
 return $! take 1 vals ++ acc

parseMain = void $ tagIgnoreAttrs "Period" parseDetails

parseDetails = many parseParam >>= yield

parseParam = tag' "param" parseParamAttrs $ \idAttr -> do
  value <- content
  return (idAttr, value)

parseParamAttrs = do
  idAttr <- requireAttr "id"
  attr "name"
  return idAttr

1 个答案:

答案 0 :(得分:0)

如果我将get更改为仅返回["hi"]或其他内容,我将无法获得积累。因此,似乎返回的文本仍然引用了它们所在的较大文本(例如,零拷贝切片,请参见https://hackage.haskell.org/package/text-0.11.2.0/docs/Data-Text.html#g:18的注释),因此即使我们仅使用少量零件。

我们的解决方法是在要产生的任何属性上使用Data.Text.copy

someattr <- requireAttr "n"
yield (T.copy someattr)

这使我们可以解析几乎恒定的内存使用情况。

(如果我们想节省更多的内存,我们可能考虑使用https://markkarpov.com/post/short-bs-and-text.html#shorttext。)