我正在使用xml-conduit的流接口https://hackage.haskell.org/package/xml-conduit-1.8.0/docs/Text-XML-Stream-Parse.html#v:parseBytes解析一些相当大的XML文件,但是我看到此内存积聚(在一个小的测试文件中):
顶级用户在哪里:
实际数据不应占用太多堆-如果我进行序列化和重新读取,则常驻内存使用量为千字节,而此处为兆字节。
我设法用以下方法重现此问题的最小示例:
{-# 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
答案 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。)