2016-03-28 45 views
4

上我偶然發現了一個奇怪的內存前一個問題(haskell-data-hashset-from-unordered-container-performance-for-large-sets)上的一些意見工作泄漏管道:生產內存泄漏

module Main where 

import System.Environment (getArgs) 
import Control.Monad.Trans.Resource (runResourceT) 
import Data.Attoparsec.ByteString (sepBy, Parser) 
import Data.Attoparsec.ByteString.Char8 (decimal, char) 
import Data.Conduit 
import qualified Data.Conduit.Attoparsec as CA 
import qualified Data.Conduit.Binary as CB 
import qualified Data.Conduit.List as CL 

main :: IO() 
main = do (args:_) <- getArgs 
      writeFile "input.txt" $ unlines $ map show [1..4 :: Int] 
      case args of "list" -> m1 
         "fail" -> m2 
         "listlist" -> m3 
         "memoryleak" -> m4 
         --UPDATE 
         "bs-lines":_ -> m5 
         "bs":_ -> m6 
         _ -> putStr $ unlines ["Usage: conduit list" 
              ,"    fail" 
              ,"    listlist" 
              ,"    memoryleak" 
              --UPDATE 
              ,"    bs-lines" 
              ,"    bs" 
              ] 
m1,m2,m3,m4 :: IO() 
m1 = do hs <- runResourceT 
      $ CB.sourceFile "input.txt" 
      $$ CB.lines 
      =$= CA.conduitParser (decimal :: Parser Int) 
      =$= CL.map snd 
      =$= CL.consume 
     print hs 
m2 = do hs <- runResourceT 
      $ CB.sourceFile "input.txt" 
      $$ CA.conduitParser (decimal :: Parser Int) 
      =$= CL.map snd 
      =$= CL.consume 
     print hs 
m3 = do hs <- runResourceT 
      $ CB.sourceFile "input.txt" 
      $$ CB.lines 
      =$= CA.conduitParser (decimal `sepBy` (char '\n') :: Parser [Int]) 
      =$= CL.map snd 
      =$= CL.consume 
     print hs 
m4 = do hs <- runResourceT 
      $ CB.sourceFile "input.txt" 
      $$ CA.conduitParser (decimal `sepBy` (char '\n') :: Parser [Int]) 
      =$= CL.map snd 
      =$= CL.consume 
     print hs 
-- UPDATE 
m5 = do inpt <- BS.lines <$> BS.readFile "input.txt" 
     let Right hs = mapM (parseOnly (decimal :: Parser Int)) inpt 
     print hs 
m6 = do inpt <- BS.readFile "input.txt" 
     let Right hs = (parseOnly (decimal `sepBy` (char '\n') :: Parser [Int])) inpt 
     print hs 

下面是一些例子輸出:

$ > stack exec -- example list 
[1234] 
$ > stack exec -- example listlist 
[[1234]] 
$ > stack exec -- conduit fail 
conduit: ParseError {errorContexts = [], errorMessage = "Failed reading: takeWhile1", errorPosition = 1:2} 
$ > stack exec -- example memoryleak 
(Ctrl+C) 

-- UPDATE 
$ > stack exec -- example bs-lines 
[1,2,3,4] 
$ > stack exec -- example bs 
[1,2,3,4] 

現在的問題我有:

  • 爲什麼m1不生產[1,2,3,4]
  • 爲什麼m2失敗?
  • 爲什麼m4與所有其他版本相比表現完全不同,併產生空間泄漏?
+0

你用懶惰的I/O和attoparsec本身檢查你的代碼嗎?如果attoparsec的回溯數據超過預期,我不會感到驚訝。 –

+0

@MichaelSnoyman我用bytestring/attoparsec版本更新了我的問題,但我不確定它是否等同於導管版本(因爲我對導管不太熟悉)。至少它產生了我期望的產出。 – epsilonhalbe

回答

4

爲什麼m2失敗?

輸入文件作爲字符流是:

1\n2\n3\n4\n 

由於decimal解析器不希望一個新行字符,消耗所述第一數目之後的剩餘流是:

\n2\n3\n4\n 

由於輸入流未耗盡,conduitParser將再次運行流上的解析器,這次它甚至無法使用第一個字符,因此失敗。

爲什麼m4的表現完全不同於所有其他版本併產生空間泄漏?

decimal `sepBy` (char '\n')只會消耗整數\n兩者之間,之後成功解析四個數字,輸入流中只有一個是性格:

\n 

decimal `sepBy` (char '\n')都不能消耗它,更有甚者會不會失敗:sepBy可以不消耗任何東西並返回空列表。因此它不會無限地解析並永不終止。

爲什麼m1不生成[1,2,3,4]?

我也想知道它!我想這與融合有關,也許你應該聯繫管道包的作者,他只是評論你的問題。

+2

由於評論的解釋太長,我將其添加爲另一個答案。 –

2

回答關於M1的問題:當您使用CB.lines,你轉向輸入,看起來像:

["1\n2\n3\n4\n"] 

到:

["1", "2", "3", "4"] 

然後,attoparsec解析 「1」,等待更多的輸入,看到「2」,等等。

+0

是否使用'conduitParser'不正確使用'parseOnly'和'CL.map',如果我理解正確'conduitParser'逐步解析數據流,並且不是解析'[1,2, 3,4]'。 – epsilonhalbe

+0

這不是我要說的。結合'CB.lines'和'conduitParser'是有問題的,因爲前者去掉了後者需要做適當解析的換行符。 –