2012-10-30 9 views
8

मैं कुछ मामूली बड़ी 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 इमारत रखने के लिए के बाद से यह बहुत आसान है करना चाहते हैं, लेकिन मैं स्मृति समस्या को दूर करने की जरूरत है। मैं उपर्युक्त प्रोग्राम को कैसे अनुकूलित कर सकता हूं ताकि यह बहुत अधिक स्मृति का उपयोग न करे?

उत्तर

9

यदि आप अच्छी मेमोरी व्यवहार चाहते हैं, तो आपको यह सुनिश्चित करने की ज़रूरत है कि आप उन्हें उत्पन्न करते समय क्लॉज लिखते हैं, उन्हें स्मृति में एकत्रित करने और उन्हें डंप करने के बजाय, या तो आलस्य या अधिक स्पष्ट दृष्टिकोण जैसे कि कंडिट्स , गणक, पाइप या पसंद है।

उस दृष्टिकोण में मुख्य बाधा यह है कि डीआईएमएसीएस प्रारूप हेडर में खंडों और चरों की संख्या की अपेक्षा करता है। यह निष्पक्ष कार्यान्वयन को पर्याप्त आलसी होने से रोकता है। दो संभावनाएं हैं:

व्यावहारिक रूप से क्लॉज को अस्थायी स्थान पर लिखना है। उसके बाद संख्याएं ज्ञात हैं, इसलिए आप उन्हें वास्तविक फ़ाइल में लिखते हैं और अस्थायी फ़ाइल की सामग्री जोड़ते हैं।

खूबसूरत दृष्टिकोण संभव है यदि खंड की पीढ़ी कोई साइड इफेक्ट नहीं है (अपने DIMACSM इकाई द्वारा की पेशकश प्रभाव के अलावा) और पर्याप्त तेजी से है: यह दो बार चलाने के लिए, पहले खंड दूर फेंक और सिर्फ संख्या की गणना, प्रिंट हेडर लाइन, जेनरेटर फिर से चलाएं; अब खंडों को मुद्रित करें।

(यह SAT-Britney, जहां मैं दूसरे दृष्टिकोण लिया लागू करने, क्योंकि यह उस संदर्भ में अन्य आवश्यकताओं के साथ बेहतर फिट के साथ मेरा अनुभव से है।)

इसके अलावा, अपने कोड में, addAll नहीं पर्याप्त आलसी है : सूची c को लिखने के बाद भी बनाए रखने की आवश्यकता है (MonadWriter अर्थ में) खंड। यह एक और अंतरिक्ष रिसाव है। मेरा सुझाव है कि आप add को प्रारंभिक ऑपरेशन के रूप में लागू करें और फिर addAll = mapM_ add

+1

तेज़ उत्तर के लिए धन्यवाद! मैंने वास्तव में आदिम ऑपरेशन के रूप में जोड़ा और 'addAll = mapM_ add' का उपयोग किया, लेकिन यह भी बदतर था। – HaskellElephant

+1

हां, मुझे विश्वास है कि यदि आपके पास आलसी पर्याप्त लेखक नहीं है तो यह बदतर है; लेकिन अगर लेखक तुरंत परिणाम लिखेंगे तो बेहतर होना चाहिए। क्या आप यह देखने के लिए प्रोफाइलिंग करते हैं कि '[क्लॉज] 'या' बाइटस्ट्रिंग 'स्मृति को ओवरफ़्लो का कारण बनती है या नहीं? –

+0

मैंने किया और यह 'बाइटस्ट्रिंग' है। – HaskellElephant

3

के रूप में जोआचिम Breitner के जवाब में बताया गया है समस्या यह है कि DIMACSM, काफी आलसी नहीं था दोनों क्योंकि monads की सख्त संस्करणों का उपयोग किया गया था और क्योंकि चर और खंड की संख्या ByteString से पहले की जरूरत है फाइल करने के लिए लिखा जा सकता है किया गया था। समाधान मोनाड्स के आलसी संस्करणों का उपयोग करना और उन्हें दो बार निष्पादित करना है। यह पता चला है कि WriterT बाहरी मोनड होना आवश्यक है:

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 
+0

क्या आप समझा सकते हैं कि मोनैड का आदेश यहां क्यों मायने रखता है? –

+0

नहीं, मुझे नहीं पता, यह मेरी मशीन पर महत्वपूर्ण है, अगर इससे आपके लिए कोई फर्क नहीं पड़ता है तो मैं टेक्स्ट छोड़ दूंगा लेकिन कोड रखूंगा। – HaskellElephant

+0

मैंने कोशिश नहीं की; मैं सिर्फ उत्सुक हूँ। –

संबंधित मुद्दे