2013-02-01 14 views
10

मैं हास्केल का उपयोग कर फ़ाइल में संग्रहीत अद्वितीय ब्लॉक गिनना चाहता हूं। ब्लॉक 512 की लंबाई के साथ लगातार बाइट्स है और लक्ष्य फ़ाइल का आकार कम से कम 1 जीबी है।हास्केल में कुशल हैश नक्शा कंटेनर?

यह मेरी प्रारंभिक कोशिश है।

import   Control.Monad 
import qualified Data.ByteString.Lazy as LB 
import   Data.Foldable 
import   Data.HashMap 
import   Data.Int 
import qualified Data.List   as DL 
import   System.Environment 

type DummyDedupe = Map LB.ByteString Int64 

toBlocks :: Int64 -> LB.ByteString -> [LB.ByteString] 
toBlocks n bs | LB.null bs = [] 
       | otherwise = let (block, rest) = LB.splitAt n bs 
          in block : toBlocks n rest 

dedupeBlocks :: [LB.ByteString] -> DummyDedupe -> DummyDedupe 
dedupeBlocks = flip $ DL.foldl' (\acc block -> insertWith (+) block 1 $! acc) 

dedupeFile :: FilePath -> DummyDedupe -> IO DummyDedupe 
dedupeFile fp dd = LB.readFile fp >>= return . (`dedupeBlocks` dd) . toBlocks 512 

main :: IO() 
main = do 
    dd <- getArgs >>= (`dedupeFile` empty) . head 
    putStrLn . show . (*512) . size $ dd 
    putStrLn . show . (*512) . foldl' (+) 0 $ dd 

यह काम करता है, लेकिन मैं इसके निष्पादन समय और स्मृति उपयोग से निराश हो गया। स्पष्ट रूप से जब मैंने नीचे सूचीबद्ध सी ++ और यहां तक ​​कि पायथन कार्यान्वयन की तुलना की, तो यह 3 ~ 5x धीमा था और 2 ~ 3x अधिक स्मृति स्थान का उपभोग किया।

import os 
import os.path 
import sys 

def dedupeFile(dd, fp): 
    fd = os.open(fp, os.O_RDONLY) 
    for block in iter(lambda : os.read(fd, 512), ''): 
     dd.setdefault(block, 0) 
     dd[block] = dd[block] + 1 
    os.close(fd) 
    return dd 

dd = {} 
dedupeFile(dd, sys.argv[1]) 

print(len(dd) * 512) 
print(sum(dd.values()) * 512) 

मैंने सोचा कि यह मुख्य रूप से hashmap कार्यान्वयन की वजह से था, और इस तरह hashmap, hashtables और unordered-containers के रूप में अन्य कार्यान्वयन की कोशिश की। लेकिन कोई ध्यान देने योग्य अंतर नहीं था।

कृपया इस कार्यक्रम को बेहतर बनाने में मेरी सहायता करें।

उत्तर

6

मुझे नहीं लगता कि आप पाइथन शब्दकोशों के प्रदर्शन को हरा सकते हैं। वे वास्तव में सी में लागू किए गए अनुकूलन के वर्षों के साथ लागू होते हैं, दूसरी ओर हैशप नया है और इतना अनुकूलित नहीं है। तो मेरी राय में 3x प्रदर्शन प्राप्त करना काफी अच्छा है। आप कुछ स्थानों पर हैकेल कोड अनुकूलित कर सकते हैं लेकिन फिर भी इससे कोई फर्क नहीं पड़ता। यदि आप अभी भी प्रदर्शन बढ़ाने के बारे में अशिष्ट हैं, तो मुझे लगता है कि आपको अपने कोड में एफएफआई के साथ एक अत्यधिक अनुकूलित सी लाइब्रेरी का उपयोग करना चाहिए।

यहाँ समान विचार विमर्श के कुछ

haskell beginners

+0

असल में, जो मुझे सबसे ज्यादा ख्याल है वह मेमोरी उपयोग है, मैं हास्केल हैशैप्स के अत्यधिक मेमोरी उपयोग को समझ नहीं पा रहा हूं। जैसे जब इनपुट फ़ाइल में केवल 600 एमबी अद्वितीय डेटा होता है, तो यह लगभग 1 जीबी मेमोरी या अधिक का उपयोग करता है। वैसे भी, आपके उत्तर और आलेख लिंक के लिए धन्यवाद। मुझे एफएफआई का उपयोग करने पर विचार करना चाहिए। – comatose

+4

@comatose, यह सिर्फ जीएचसी है। जीएचसी कचरा संग्रहण रणनीति एक प्रतिलिपि कलेक्टर का उपयोग करती है, जो वास्तव में तेज़ है, लेकिन इसमें 2x मेमोरी ओवरहेड है। – luqui

3

यह आपके उपयोग के आधार पर पूरी तरह से अप्रासंगिक हो सकता है, लेकिन मैं insertWith (+) block 1 के बारे में थोड़ा चिंतित हूँ। यदि आपकी गणना उच्च संख्या तक पहुंच जाती है, तो आप हैश मानचित्र की कोशिकाओं में थंक जमा करेंगे। इससे कोई फर्क नहीं पड़ता कि आपने ($!) का उपयोग किया है, जो केवल रीढ़ की हड्डी को मजबूर करता है - मान अभी भी आलसी हैं।

Data.HashMap कोई सख्त संस्करण insertWith' प्रदान करता है जैसे Data.Map करता है। toBlocks से सख्त ByteStrings की एक सूची

insertWith' :: (Hashable k, Ord k) => (a -> a -> a) -> k -> a 
            -> HashMap k a -> HashMap k a 
insertWith' f k v m = maybe id seq maybeval m' 
    where 
    (maybeval, m') = insertLookupWithKey (const f) k v m 

इसके अलावा, आप उत्पादन के लिए (लेकिन इनपुट नहीं) चाहते हो सकता है, जो तेजी से hashing कर देगा: लेकिन आप इसे लागू कर सकते हैं।

यह सब मुझे मिला है - हालांकि, मैं कोई प्रदर्शन गुरु नहीं हूं।

+1

मैं 512 बाइट्स को पकड़ने के लिए 'डेटा ब्लैक = ब्लैक {- # यूएनपीएक्स # -} वर्ड 64 ... बनाकर थोड़ा सा निचोड़ने में सक्षम था। यदि आप सख्त बाइटस्ट्रिंग पर स्विच करते हैं तो एक बड़ा प्रदर्शन वृद्धि होती है, लेकिन मुझे यकीन नहीं है कि कैश जैसे प्रभावों के कारण यह कितना है और आलसी बाइटस्टिंग के पुराने पुराने दासता के कारण कितना है समझदार संरेखण नहीं है (जो चिंताएं मैं क्योंकि यह ब्रैच, प्रतिलिपि, आदि का कारण बनता है)। आखिरकार, 'अनियंत्रित कंटेनर' ने सबसे अच्छा किया (4.8 सेकंड पाई बनाम 6.5 सेकेंड एचएस, लेकिन वह सख्त बाइटिंग्स था) जबकि 'हैशटेबल' कोई 'डालने के साथ' ऑपरेशन के कारण निराशाजनक था। –

+0

@luqui आपके उत्तर के लिए धन्यवाद, मैंने आपसे कुछ सीखा। असल में, 'अनधिकृत कंटेनर' में 'डेटा.शैश मैप.स्ट्रिटी' है और मैंने कोशिश की, लेकिन यह स्थिति को बेहतर और सख्त 'बाइटस्ट्रिंग' नहीं कर सका। 'toStrict' कुछ हद तक महंगा है। – comatose

+0

@ थॉमसएम। डूबुइसन धन्यवाद, मुझे यह कोशिश करनी चाहिए। – comatose

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