2013-10-23 2 views
10

थोड़ा अभ्यास के रूप में, मैंने हैकेल में निम्नलिखित शब्द गिनती कार्यक्रम बनाया है। यह टेक्स्ट फ़ाइल में अलग-अलग शब्दों की गणना करता है, और 50 आवृत्तियों को उनके आवृत्तियों के साथ आउटपुट करता है।क्या अशुद्ध शब्द का उपयोग किए बिना मेरे शब्द गिनती प्रोग्राम को तेजी से बनाने का कोई तरीका है?

import qualified Data.Map as Map 
import Data.List.Split 
import Data.List 
import Data.Ord 

-- Count words 
count = Map.toList . foldl' increment Map.empty 
    where 
     increment dict k = Map.insert k (1 + Map.findWithDefault 0 k dict) dict 

-- Sort the counts 
countAndSort = sortBy (flip $ comparing snd) . count 

-- Pretty printing 
pp :: Show a => [(String,a)] -> IO() 
pp = putStrLn . foldl' format "" where 
    format text (x,y) = text ++ "\n" ++ x ++ "\t" ++ show y 

main = readFile "pg13951.txt" >>= pp . take 50 .countAndSort . splitOn " " 

समस्या यह है कि यह 16 बार मेरी अजगर कार्यान्वयन की तुलना में धीमी एक परिवर्तनशील dict के साथ है वह यह है कि:

def increment(dic,word): 
    dic[word] = dic.get(word,0) + 1 
    return dic 

print sorted(reduce(increment,open("pg13951.txt").read().split(),{}).items(),key=lambda e:-e[1])[:50] 

मुझे लगता है कि समस्या यह है कि GHC constanstly नए नक्शे जब realocating है की वजह से है यह वही एक बार फिर से उपयोग कर सकता है। रन-टाइम statitistics आवंटन का एक बहुत दिखाने:

$ ghc -rtsopts count.hs 
$ ./count +RTS -sstderr 

de  7682 
et  4423 
la  4238 
<snip> 
d'Artagnan  511 
M.  502 
c'est 443 
d'Artagnan,  443 

    705,888,048 bytes allocated in the heap 
    655,511,720 bytes copied during GC 
    139,823,800 bytes maximum residency (10 sample(s)) 
     1,049,416 bytes maximum slop 
      287 MB total memory in use (0 MB lost due to fragmentation) 

            Tot time (elapsed) Avg pause Max pause 
    Gen 0  1366 colls,  0 par 2.16s 2.26s  0.0017s 0.0072s 
    Gen 1  10 colls,  0 par 2.86s 3.09s  0.3093s 1.5055s 

    INIT time 0.00s ( 0.00s elapsed) 
    MUT  time 3.18s ( 3.36s elapsed) 
    GC  time 5.02s ( 5.36s elapsed) 
    EXIT time 0.00s ( 0.00s elapsed) 
    Total time 8.20s ( 8.72s elapsed) 

    %GC  time  61.2% (61.4% elapsed) 

    Alloc rate 221,831,366 bytes per MUT second 

    Productivity 38.8% of total user, 36.5% of total elapsed 

मेरे सवाल यह है: वहाँ इस कार्यक्रम बनाने के लिए एक रास्ता है इस तरह, आईओ इकाई में काम कर रहे अस्थायी डेटा संरचनाओं का उपयोग कर के रूप में गंदा चाल का सहारा के बिना, बेहतर प्रदर्शन, आदि। ?

पुनश्च: डेटा फ़ाइल निम्न URL पर उपलब्ध है: http://www.gutenberg.org/cache/epub/13951/pg13951.txt

+0

अगर आप Data.Map.Strict का उपयोग यह कैसा प्रदर्शन करता है? यदि आप Data.Map के स्रोत को देखते हैं, तो आप देखेंगे कि डिफ़ॉल्ट कार्यान्वयन Data.Map.Lazy है और आपको Data.Map.Strict का उपयोग करना चाहिए यदि "आपको अंततः संग्रहीत सभी मानों की आवश्यकता होगी" और "संग्रहीत मूल्य आलसी गणना करने के लिए बड़े वर्चुअल डेटा संरचनाओं का प्रतिनिधित्व न करें "। मुझे लगता है कि आपके मामले का बिल्कुल वर्णन करता है। – itsbruce

+0

@itsbruce: मैंने कोशिश की, लेकिन यह ज्यादा नहीं बदला। @ शांग के जवाब के अनुसार, मुझे लगता है कि सबसे बड़ा मुद्दा 'Data.Text' के बजाय 'स्ट्रिंग' का उपयोग कर रहा था। मैं आज रात अधिक परीक्षण करूंगा। –

+1

आप कोड को समानांतर भी कर सकते हैं, wordcount मानचित्र के लिए एक सामान्य उदाहरण है रेडियस-शैली गणना; आप अपनी मशीन – jev

उत्तर

18

यहाँ कुछ त्वरित और आसान अनुकूलन नहीं है जो मैंने कोशिश कर रहे हैं। मेरी मशीन पर

मूल संस्करण:

real 0m1.539s 
user 0m1.452s 
sys 0m0.076s 
  1. insert का उपयोग करने और foldl' आप fromListWith का उपयोग शब्द गिनती करने के लिए कर सकते हैं के बजाय।

    count = Map.toList . Map.fromListWith (+) . flip zip (repeat 1) 
    

    यह दो गुना तेजी से है।

    real 0m0.687s 
    user 0m0.648s 
    sys 0m0.032s 
    
  2. String प्रकार पात्रों बनाता है जो तार बल्कि सुरुचिपूर्ण लेकिन अक्षम जोड़ तोड़ की एक लिंक्ड सूची है। कुशल स्ट्रिंग हैंडलिंग प्राप्त करने के लिए हम Text प्रकार का उपयोग कर सकते हैं। मैं फ़ंक्शन को unlines foldl' के बजाय words का उपयोग splitOn का उपयोग मूल विभाजन के लिए pp फ़ंक्शन को फिर से लिखता हूं।

    {-# LANGUAGE OverloadedStrings #-} 
    
    import Data.Monoid 
    import Data.Text (Text) 
    import qualified Data.Text as T 
    import qualified Data.Text.IO as T 
    
    pp :: Show a => [(Text,a)] -> IO() 
    pp = T.putStrLn . T.unlines . map format where 
        format (x,y) = x <> "\t" <> (T.pack $ show y) 
    
    main = T.readFile "pg13951.txt" >>= pp . take 50 .countAndSort . T.words 
    

    फिर से, पिछले चरण के जितना तेज़।

    real 0m0.330s 
    user 0m0.316s 
    sys 0m0.008s 
    
  3. Map

    import qualified Data.Map.Strict as Map 
    

    की सख्त संस्करण का उपयोग करें के बारे में 20% की गति में वृद्धि

    real 0m0.265s 
    user 0m0.252s 
    sys 0m0.008s 
    
+1

बदले में 'परिवर्तन' का उपयोग करें 'ढूंढें + डालने' बेहतर होना चाहिए – josejuan

+0

बहुत बहुत धन्यवाद! यह वही है जो मैं प्राप्त करने की उम्मीद कर रहा था। –

+1

ध्यान दें कि 'शब्द' 'splitOn" से थोड़ा अलग परिणाम देता है "क्योंकि यह सभी सफेद जगहों (लाइनफ़ीड्स समेत) पर विभाजित होता है। हालांकि, मुझे लगता है कि यह इस मामले में वांछित व्यवहार है। – shang

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