【问题标题】:Simple parser running out of memory简单的解析器内存不足
【发布时间】:2016-12-29 11:20:25
【问题描述】:

我想了解为什么这个简单的解析器会耗尽大文件的内存。我真的不知道我在做什么错。

import Data.Attoparsec.ByteString.Char8
import qualified Data.Attoparsec.ByteString.Lazy as Lazy
import System.Environment
import qualified Data.ByteString.Lazy as B
import Control.Applicative

parseLine :: Parser String
parseLine = manyTill' anyChar (endOfLine <|> endOfInput)

parseAll :: Parser [Int]
parseAll = manyTill' 
        (parseLine >> (return 0)) -- discarding what's been read
        endOfInput

main :: IO()
main = do 
        [fn] <- getArgs
        text <- B.readFile fn

        case Lazy.parse parseAll text of
                Lazy.Fail _ _ _ -> putStrLn "bad"
                Lazy.Done _ _ -> putStrLn "ok" 

我正在运行程序:

 runhaskell.exe test.hs x.log

输出:

test.hs: Out of memory

x.log 大小约为 500MB。我的机器有 16GB 的 RAM。

【问题讨论】:

    标签: haskell memory-leaks attoparsec


    【解决方案1】:

    如果您查看the documentation of attoparsec,您会注意到有一个类似的示例,并附有以下评论:

    注意重叠的解析器 anyCharstring "--&gt;"。虽然这可行,但效率不高,因为它会导致大量回溯。

    使用anyChar 的替代方法拒绝endOfLine 接受的字符应该可以解决问题。例如

    satisfy (\c -> c `notElem` ['\n', '\r'])
    

    【讨论】:

      【解决方案2】:

      我对 Attoparsec 不太熟悉,但我认为您可能很难单独使用它来解析常量内存中的大文件。如果您将顶级解析器 parseAll 替换为:

      parseAll :: Parser ()
      parseAll = skipMany anyChar
      

      并对其进行分析,您会发现内存使用量仍然无限制地增长。 (当我将您的代码转换为使用严格的ByteStrings 的增量阅读时,它没有任何区别。)

      我认为问题是这样的:因为 Attoparsec 会自动回溯,所以它必须为 parseAll(你的版本或我的版本——没关系)做好准备:

      (parseAll <* somethingThatDoesntMatch) <|> parseDifferently
      

      如果parseAll 已经解析了 50 万行并到达末尾,somethingThatDoesntMatch 将使它一直回溯到开头,然后用 parseDifferently 重新解析所有内容。所以,回溯的元信息和 ByteStrings 本身在解析完成之前不能被释放。

      现在,您的解析器(以及我上面的示例)“显然”不需要以这种方式回溯,但 Attoparsec 不会推断这一点。

      我可以想到几种方法:

      1. 如果您解析的是兆字节而不是千兆字节,请考虑使用 Parsec,它只会在明确时回溯(例如,使用 try)。
      2. 使用手工制作的非回溯解析器将您的日志文件分解为行(或行块),并在每一行/块上运行 Attoparsec 解析器。

      【讨论】:

      • 这更好地回答了我的问题,因为我想了解问题而不是解决问题。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-26
      • 2013-08-08
      相关资源
      最近更新 更多