io - आईएसओ की बड़ी फाइलें हैंस्केल में: प्रदर्शन समस्या




lazy-evaluation bytestring (2)

मैं बड़ी फ़ाइलों पर हास्केल का इस्तेमाल करने की कोशिश कर रहा हूं बाइट के बाद मैं एक इनपुट फ़ाइल बाइट ब्राउज़ करना चाहता हूं, और बाइट के बाद एक आउटपुट बाइट जेनरेट करना चाहता हूं। बेशक मुझे IO को उचित आकार के ब्लॉक (कुछ KB) के साथ बफर करने की आवश्यकता है। मैं यह नहीं कर सकता, और मुझे तुम्हारी मदद की ज़रूरत है कृपया

import System 
import qualified Data.ByteString.Lazy as BL 
import Data.Word  
import Data.List

main :: IO () 
main =     
    do         
        args <- System.getArgs         
        let filename = head args         
        byteString <- BL.readFile filename         
        let wordsList = BL.unpack byteString         
        let foldFun acc word = doSomeStuff word : acc
        let wordsListCopy = foldl' foldFun [] wordsList
        let byteStringCopy = BL.pack (reverse wordsListCopy)
        BL.writeFile (filename ++ ".cpy") byteStringCopy
    where
        doSomeStuff = id

मैं इस फ़ाइल TestCopy.hs नाम देता TestCopy.hs , फिर निम्न करें:

$ ls -l *MB
-rwxrwxrwx 1 root root 10000000 2011-03-24 13:11 10MB
-rwxrwxrwx 1 root root  5000000 2011-03-24 13:31 5MB
$ ghc --make -O TestCopy.hs 
[1 of 1] Compiling Main             ( TestCopy.hs, TestCopy.o )
Linking TestCopy ...
$ time ./TestCopy 5MB

real    0m5.631s
user    0m1.972s
sys 0m2.488s
$ diff 5MB 5MB.cpy
$ time ./TestCopy 10MB 

real    3m6.671s
user    0m3.404s
sys 1m21.649s
$ diff 10MB 10MB.cpy 
$ time ./TestCopy 10MB +RTS -K500M -RTS

real    2m50.261s
user    0m3.808s
sys 1m13.849s
$ diff 10MB 10MB.cpy 
$ 

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

प्रेसिजन: यह एक विश्वविद्यालय परियोजना के लिए है और मैं फाइल कॉपी करने की कोशिश नहीं कर रहा हूँ doSomeStuff फ़ंक्शन संपीड़न / डीकंप्रेसन कार्रवाई करेगा जो मुझे अनुकूलित करना है।


चिपक गई इनपुट प्रसंस्करण के लिए मैं गणक पैकेज का उपयोग करेगा।

import Data.Enumerator
import Data.Enumerator.Binary (enumFile)

हम bytestrings का उपयोग करें

import Data.ByteString as BS

और आईओ

import Control.Monad.Trans (liftIO)
import Control.Monad (mapM_)
import System (getArgs)

आपका मुख्य कार्य निम्न की तरह दिख सकता है:

main =
  do (filepath:_) <- getArgs
     let destination
     run_ $ enumFile filepath $$ writeFile (filepath ++ ".cpy")

enumFile 4096 बाइट प्रति हिस्सा पढ़ता है और इन्हें लिखने के लिए पास करता है, जो इसे नीचे लिखता है।

enumWrite के रूप में परिभाषित किया गया है:

enumWrite :: FilePath -> Iteratee BS.ByteString IO ()
enumWrite filepath =
  do liftIO (BS.writeFile filepath BS.empty)   -- ensure the destination is empty
     continue step
  where
  step (Chunks xs) =
    do liftIO (mapM_ (BS.appendFile filepath) xs)
       continue step
  step EOF         = yield () EOF

जैसा कि आप देख सकते हैं, चरण फ़ंक्शन बाइटस्टेस के खंड लेता है और उन्हें गंतव्य फ़ाइल में जोड़ता है। इन खंडों में स्ट्रीम बी एस। बायटेस्टिंग है, जहां स्ट्रीम को परिभाषित किया गया है:

data Stream a = Chunks [a] | EOF

एक EOF चरण समाप्त होने पर, उपज ()

इस पर अधिक विस्तृत पढ़ने के लिए मैं व्यक्तिगत रूप से माइकल स्नोयमंस ट्यूटोरियल की सिफारिश करता हूं

संख्याएँ

$ time ./TestCopy 5MB
./TestCopy 5MB  2,91s user 0,32s system 96% cpu 3,356 total

$ time ./TestCopy2 5MB
./TestCopy2 5MB  0,04s user 0,03s system 93% cpu 0,075 total

यह काफी सुधार है अब अपने गुना को लागू करने के लिए आप संभवतः एक एनमेरेटी लिखना चाहते हैं, जिसका प्रयोग इनपुट स्ट्रीम को बदलने के लिए किया जाता है। सौभाग्य से पहले से ही गणक पैकेज में परिभाषित एक मानचित्र फ़ंक्शन मौजूद है, जिसे आपकी आवश्यकता के लिए संशोधित किया जा सकता है, यानी यह राज्य को जारी रखने के लिए संशोधित किया जा सकता है।

मध्यवर्ती परिणाम के निर्माण पर

आप रिवर्स ऑर्डर में शब्दों का निर्माण करते हैं और इसे बाद में रिवर्स करते हैं। मुझे लगता है कि अंतर सूचियां एक बेहतर काम करती हैं, क्योंकि इस तथ्य के कारण केवल ओ (1) समय में संलग्न होता है कि केवल एक फ़ंक्शन संरचना शामिल है मुझे यकीन नहीं है कि क्या वे अधिक स्थान लेते हैं, हालांकि। यहां अंतर सूचियों का एक कठिन स्केच है:

type DList a = [a] -> [a]

emptyList :: DList a
emptyList = id

snoc :: DList a -> a -> DList a
snoc dlist a = dlist . (a:)

toList :: DList a -> [a]
toList dlist = dlist []

यह जवाब शायद अब और जरूरी नहीं है, लेकिन मैंने इसे पूर्णता के लिए जोड़ा।


मैं इसे ले जाता हूं यह हैकेल में बड़ी फाइल पढ़ना है? कल से।

बस "-O" के बजाय "-rtsopts -O2" के साथ संकलन करने का प्रयास करें

आप का दावा है "मैं बाइट के बाद एक इनपुट फ़ाइल बाइट ब्राउज़ करना चाहता हूं, और बाइट के बाद एक आउटपुट बाइट जेनरेट करना चाहता हूं।" लेकिन आपका कोड किसी भी आउटपुट को बनाने का प्रयास करने से पहले संपूर्ण इनपुट पढ़ता है। यह सिर्फ लक्ष्य का बहुत प्रतिनिधि नहीं है

मेरे सिस्टम के साथ मुझे "ghc -rtsopts --make-o2 b.hs" दे रही है

(! 741)-> time ./b five
real 0m2.789s user 0m2.235s sys 0m0.518s
(! 742)-> time ./b ten
real 0m5.830s user 0m4.623s sys 0m1.027s

जो अब मेरे लिए रैखिक दिखता है