2012-03-17 9 views
9

में बड़े लॉग फाइल मैं कई 200MB + फ़ाइलें है कि मैं के माध्यम से grep करना चाहते हैं मान लीजिए। मैं हास्केल में यह कैसे करूं?पार्स हास्केल

यहाँ मेरी प्रारंभिक कार्यक्रम है:

import Data.List 
import Control.Monad 
import System.IO 
import System.Environment 

main = do 
    filename <- liftM head getArgs 
    contents <- liftM lines $ readFile filename 
    putStrLn . unlines . filter (isPrefixOf "import") $ contents 

इस के माध्यम से यह पार्स करने से पहले स्मृति में पूरी फ़ाइल पढ़ता है। तब मैं इस के साथ चला गया:

import Data.List 
import Control.Monad 
import System.IO 
import System.Environment 

main = do 
    filename <- liftM head getArgs 
    file <- (openFile filename ReadMode) 
    contents <- liftM lines $ hGetContents file 
    putStrLn . unlines . filter (isPrefixOf "import") $ contents 

मैंने सोचा था कि के बाद से hGetContents, आलसी है it will avoid reading the whole file into memory। लेकिन valgrind के तहत दोनों स्क्रिप्ट चलाने के लिए दोनों के लिए समान स्मृति उपयोग दिखाया गया। तो या तो मेरी स्क्रिप्ट गलत है, या valgrind गलत है। मैं

ghc --make test.hs -prof 

का उपयोग कर स्क्रिप्ट संकलित करता हूं मुझे क्या याद आ रही है? बोनस प्रश्न: मैं इस बात पर बहुत सारे उल्लेख देखता हूं कि हास्केल में आलसी आईओ वास्तव में एक बुरी चीज है। मैं सख्त आईओ का उपयोग कैसे करूं?

अद्यतन:

तो ऐसा लगता है कि मैं valgrind की मेरी पढ़ने में गलत था। +RTS -s की सहायता से देखें कि मैं क्या मिलेगा:

7,807,461,968 bytes allocated in the heap 
1,563,351,416 bytes copied during GC 
     101,888 bytes maximum residency (1150 sample(s)) 
     45,576 bytes maximum slop 
      2 MB total memory in use (0 MB lost due to fragmentation) 

Generation 0: 13739 collections,  0 parallel, 2.91s, 2.95s elapsed 
Generation 1: 1150 collections,  0 parallel, 0.18s, 0.18s elapsed 

INIT time 0.00s ( 0.00s elapsed) 
MUT time 2.07s ( 2.28s elapsed) 
GC time 3.09s ( 3.13s elapsed) 
EXIT time 0.00s ( 0.00s elapsed) 
Total time 5.16s ( 5.41s elapsed) 

महत्वपूर्ण लाइन 101,888 bytes maximum residency, जो कहते हैं कि किसी भी बिंदु पर मेरी स्क्रिप्ट ज्यादा से ज्यादा स्मृति के 101 केबी का उपयोग कर रहा था। जिस फ़ाइल के माध्यम से मैं grepping था 44 एमबी था। तो मुझे लगता है फैसले है: readFile और hGetContents दोनों आलसी होते हैं।

फ़ॉलो-अप प्रश्न:

मैं स्मृति के 7GB ढेर पर आवंटित क्यों दिखाई देता है? यह एक स्क्रिप्ट के लिए वास्तव में उच्च लगता है जो 44 एमबी फ़ाइल में पढ़ रहा है। अनुवर्ती

अद्यतन करने के लिए सवाल

ढेर पर आबंटित स्मृति के कुछ जीबी की तरह लग रहा है, इसलिए चिंता का कोई कारण हास्केल के लिए असामान्य है। String रों के बजाय ByteString रों का उपयोग करते हुए स्मृति उपयोग लेता है एक बहुत नीचे:

81,617,024 bytes allocated in the heap 
     35,072 bytes copied during GC 
     78,832 bytes maximum residency (1 sample(s)) 
     26,960 bytes maximum slop 
      2 MB total memory in use (0 MB lost due to fragmentation) 
+0

हम, क्या आप वाकई 'putStrLn'' के साथ इसे लिखने से पहले पूरी 'अनलाइन' स्ट्रिंग बनाने की आवश्यकता नहीं है? मैं 'Control.Monad.forM_ (फ़िल्टर (isPrefixOf" आयात ") सामग्री जैसे कुछ को करने का प्रयास करूंगा) $ putStrLn'। हालांकि यह सिर्फ एक अनुमान है। –

+0

@ रिकार्डो: नहीं, 'अनलिन्स' का आलसी मूल्यांकन किया जा सकता है। 'PutStr $ $ ghशी' में $ map शो [1 ..] 'को अनदेखा करने का प्रयास करें। – ephemient

+0

क्या -O2 जादुई रूप से समस्या का समाधान करता है? – gspr

उत्तर

5

दोनों readFile और hGetContents आलसी होना चाहिए। +RTS -s के साथ अपना प्रोग्राम चलाने का प्रयास करें और देखें कि वास्तव में कितनी मेमोरी का उपयोग किया जाता है। आपको क्या लगता है कि पूरी फाइल मेमोरी में पढ़ी जाती है?

आपके प्रश्न के दूसरे भाग के लिए, आलसी आईओ कभी-कभी अप्रत्याशित space leaks या resource leaks की जड़ पर है। वास्तव में आलसी आईओ की गलती नहीं है, बल्कि यह निर्धारित करना है कि इसके रिसाव के विश्लेषण के लिए इसका उपयोग करना आवश्यक है या नहीं।

+0

हाँ, आप सही हैं :) मेरे अनुवर्ती प्रश्न पर कोई विचार? –

+3

@VladtheImpala: कुल आवंटन आंकड़े के बारे में चिंता न करें; कार्यक्रम के जीवनकाल में आवंटित स्मृति की कुल * राशि * है। यह कभी भी कम नहीं होता है, भले ही स्मृति को कचरा संग्रह से मुक्त किया जाता है, जैसा कि अक्सर हास्केल में होता है; प्रति सेकंड एकाधिक गीगाबाइट के आंकड़े असामान्य नहीं हैं। – ehird

+0

@ehird आह ठीक है, धन्यवाद। मुझे यकीन नहीं था कि यह सामान्य था या नहीं। –

5

कृपया, सादे String की (100 मी फ़ाइलें, खासकर जब आप कार्रवाई कर रहे हैं>) का उपयोग न करें। बस उन्हें ByteString के (या Data.Text) के साथ बदलें:

{-# LANGUAGE OverloadedStrings #-} 

import Control.Monad 
import System.Environment 
import qualified Data.ByteString.Lazy.Char8 as B 

main = do 
    filename <- liftM getArgs 
    contents <- liftM B.lines $ B.readFile filename 
    B.putStrLn . B.unlines . filter (B.isPrefixOf "import") $ contents 

और मुझे यकीन है, यह कई गुना तेजी से हो जाएगा।

UPD: आपके अनुवर्ती प्रश्न के संबंध में।
आवंटित स्मृति की मात्रा बाइटस्टर्स पर स्विच करते समय जादू गति से दृढ़ता से जुड़ा हुआ है।
String केवल एक सामान्य सूची है, इसके लिए प्रत्येक Char के लिए अतिरिक्त मेमोरी की आवश्यकता होती है: अगले तत्व, ऑब्जेक्ट हेडर इत्यादि के लिए सूचक, इस स्मृति को आवंटित करने और फिर वापस एकत्र करने की आवश्यकता है। इसके लिए बहुत सी कम्प्यूटेशनल पावर की आवश्यकता है।
दूसरी ओर, ByteStringभाग की एक सूची है, यानी स्मृति के निरंतर ब्लॉक (मुझे लगता है कि, 64 बाइट से कम नहीं)। यह आवंटन और संग्रह की संख्या को बहुत कम करता है, और कैश इलाके में भी सुधार करता है।

+0

बाइटस्ट्रिंग्स का उपयोग करने पर बिल्कुल सहमत ... मैं अपने उदाहरण में जोड़कर चीजों को और जटिल नहीं करना चाहता था। लेकिन हां, वे समय और स्मृति दोनों के मामले में एक * विशाल * बचत हैं: '78,832 बाइट अधिकतम निवास' के साथ ढेर में '81,617,024 बाइट आवंटित किए गए हैं और' MUT समय 0.08s (0.22s समाप्त हो गए हैं) 'समय। –