使用低内存解析大型XML文件的Haskell方法

6

所以,我已经尝试过几个Haskell XML库,包括hexpat和xml-enumerator。在阅读了《Real World Haskell》中的IO章节(http://book.realworldhaskell.org/read/io.html)后,我认为如果运行以下代码,则会在使用过程中进行垃圾回收。

然而,当我在大文件上运行它时,内存使用量会随着运行而不断增加。

runghc parse.hs bigfile.xml

我错在哪里呢?我的假设是错误的吗?map/filter是否强制评估所有内容?
import qualified Data.ByteString.Lazy as BSL
import qualified Data.ByteString.Lazy.UTF8 as U
import Prelude hiding (readFile)
import Text.XML.Expat.SAX 
import System.Environment (getArgs)

main :: IO ()
main = do
    args <- getArgs
    contents <- BSL.readFile (head args)
    -- putStrLn $ U.toString contents
    let events = parse defaultParseOptions contents 
    mapM_ print $ map getTMSId $ filter isEvent events

isEvent :: SAXEvent String String -> Bool 
isEvent (StartElement "event" as) = True
isEvent _ = False

getTMSId :: SAXEvent String String -> Maybe String
getTMSId (StartElement _ as) = lookup "TMSId" as

我的最终目标是使用类似SAX的简单接口解析一个庞大的XML文件。我不想意识到整个结构,只需收到“事件”通知即可。


1
当您编译它而不是在解释模式下运行时,您是否也会遇到这种行为? - hammar
编译时别忘了使用优化(-O2)。 - Thomas M. DuBuisson
你需要编译和优化才能进行垃圾回收吗?如果是这样,我将来会尝试一下。 - Sean Clark Hess
2个回答

8
我是hexpat的维护者。这是一个bug,我已经在hexpat-0.19.8中修复了它。感谢您提醒我。
这个bug在ghc-7.2.1上是新出现的,与where子句绑定到三元组以及unsafePerformIO有关,我需要在Haskell中使用unsafePerformIO来使与C代码的交互看起来是纯的。

3
这似乎是hexpat的问题。 在编译,优化,并且仅针对像length这样的简单任务运行时,会导致线性内存使用。
观察hexpat,我认为存在过多的缓存(请参阅parseG 函数)。 我建议联系hexpat维护者并询问是否预期此行为。 无论如何,它都应该在haddocks中提到,但资源消耗似乎经常在库文档中被忽略。

根据一个快速的堆剖面,看起来大部分是由泄漏的(:)构造函数引起的。 - hammar
很高兴知道我的假设没有错。我想我会继续尝试其他的软件包。谢谢! - Sean Clark Hess

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接