[英]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.