簡體   English   中英

使用Writer monad寫入文件時如何避免內存問題?

[英]How do I avoid memory problems when writing to file using the Writer monad?

我正在構建一些中等大小的DIMACS文件,但是使用下面使用的方法,與生成的文件大小相比,內存使用量相當大,而在我需要生成的一些較大文件上,我遇到out of memory不足的問題。

import Control.Monad.State.Strict
import Control.Monad.Writer.Strict
import qualified Data.ByteString.Lazy.Char8 as B
import Control.Monad
import qualified Text.Show.ByteString as BS
import Data.List

main = printDIMACS "test.cnf" test

test = do
  xs <- freshs 100000
  forM_ (zip xs (tail xs))
    (\(x,y) -> addAll [[negate x, negate y],[x,y]])

type Var = Int
type Clause = [Var]

data DIMACSS = DS{
  nextFresh :: Int,
  numClauses :: Int
} deriving (Show)

type DIMACSM a = StateT DIMACSS (Writer B.ByteString) a

freshs :: Int -> DIMACSM [Var] 
freshs i = do
  next <- gets nextFresh
  let toRet = [next..next+i-1]
  modify (\s -> s{nextFresh = next+i}) 
  return toRet

fresh :: DIMACSM Int
fresh = do
  i <- gets nextFresh
  modify (\s -> s{nextFresh = i+1}) 
  return i

addAll :: [Clause] -> DIMACSM ()
addAll c = do
  tell 
    (B.concat . 
    intersperse (B.pack " 0\n") . 
    map (B.unwords . map BS.show) $ c)
  tell (B.pack " 0\n")
  modify (\s -> s{numClauses = numClauses s + length c})

add h = addAll [h]

printDIMACS :: FilePath -> DIMACSM a -> IO ()
printDIMACS file f = do
  writeFile file ""
  appendFile file (concat ["p cnf ", show i, " ", show j, "\n"])
  B.appendFile file b
   where
     (s,b) = runWriter (execStateT f (DS 1 0))
     i = nextFresh s - 1
     j = numClauses s

我想保留monadic的條款,因為它非常方便,但我需要克服記憶問題。 如何優化上述程序,使其不會占用太多內存?

如果你想要良好的內存行為,你需要確保在生成它們時寫出子句,而不是在內存中收集它們並將它們轉儲,如使用延遲或更明確的方法,如管道,枚舉器,管道等等。

這種方法的主要障礙是DIMACS格式需要標題中的子句和變量的數量。 這可以防止幼稚實現充分懶惰。 有兩種可能性:

務實的是將條款首先寫入臨時位置。 之后,數字已知,因此您將它們寫入實際文件並附加臨時文件的內容。

如果子句的生成沒有副作用(除了你的DIMACSM monad提供的效果)並且足夠快,那么更漂亮的方法是可能的:運行兩次,首先丟棄子句,只計算數字,打印標題行,運行再次發電機; 現在打印條款。

(這來自我實施SAT-Britney的經驗,我采用了第二種方法,因為它更符合其他要求。)

此外,在您的代碼中, addAll不夠懶惰:即使在寫入(在MonadWriter意義上)子句之后,列表c需要保留。 這是另一個空間泄漏。 我建議你實現add作為原始操作,然后addAll = mapM_ add

正如Joachim Breitner的回答所解釋的那樣,問題是DIMACSM不夠懶,因為使用了monad的嚴格版本,並且因為在將ByteString寫入文件之前需要變量和子句的數量。 解決方案是使用Monads的惰性版本並執行兩次。 事實證明,將WriterT作為外部monad也是必要的:

import Control.Monad.State
import Control.Monad.Writer

...

type DIMACSM a = WriterT B.ByteString (State DIMACSS) a

...

printDIMACS :: FilePath -> DIMACSM a -> IO ()
printDIMACS file f = do
  writeFile file ""
  appendFile file (concat ["p cnf ", show i, " ", show j, "\n"])
  B.appendFile file b
   where
     s = execState (execWriterT f) (DS 1 0)
     b = evalState (execWriterT f) (DS 1 0)
     i = nextFresh s - 1
     j = numClauses s

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM