我正在构建一些中等大小的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的条款,因为它非常方便,但我需要克服记忆问题。如何优化上述程序,使其不会占用太多内存?
答案 0 :(得分:9)
如果你想要良好的内存行为,你需要确保在生成它们时写出子句,而不是将它们收集在内存中并将它们转储,如使用延迟或更明确的方法(如管道),调查员,管道等。
该方法的主要障碍是DIMACS格式需要标题中的子句和变量的数量。这可以防止幼稚实现充分懒惰。有两种可能性:
务实的是将条款首先写入临时位置。之后,这些数字是已知的,因此您将它们写入真实文件并附加临时文件的内容。
如果子句的生成没有副作用(除了DIMACSM
monad提供的效果之外)并且足够快,那么更漂亮的方法是可能的:运行两次,首先丢弃子句并只计算数字,打印标题行,再次运行生成器;现在打印条款。
(这来自我实施SAT-Britney的经验,我采用了第二种方法,因为它更适合该环境中的其他要求。)
此外,在您的代码中,addAll
不够懒惰:即使在写入(在c
意义上)条款之后,也需要保留列表MonadWriter
。这是另一个空间泄漏。我建议您将add
实现为原始操作,然后addAll = mapM_ add
。
答案 1 :(得分:3)
正如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