Als kleine Übung habe ich das folgende Wortzählprogramm in Haskell gemacht. Es zählt einzelne Wörter in einer Textdatei und gibt die 50 häufigsten zusammen mit ihren Häufigkeiten aus.Gibt es eine Möglichkeit, mein Wortzählprogramm schneller zu machen, ohne unreine Tricks zu verwenden?
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 " "
Das Problem ist, dass es, dass es 16-mal ist langsamer als mein Python-Implementierung mit einem wandelbaren 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]
Ich denke, das Problem auf die Tatsache zurückzuführen ist, dass ghc constanstly ist realocating neue Karten, wenn es könnte dasselbe immer und immer wieder verwenden. Laufzeit statitistics zeigt viele Zuteilungen:
$ 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
Meine Frage ist: Gibt es eine Möglichkeit, dieses Programm zu machen einen bessere Leistung, ohne zu schmutzigen Tricks zurückgreifen, wie in dem IO-Monade, arbeitete mit änderbaren Datenstrukturen, etc. ?
PS: die Datendatei unter folgender URL verfügbar: http://www.gutenberg.org/cache/epub/13951/pg13951.txt
Wie es funktioniert ausführen, wenn Sie Data.Map.Strict verwenden? Wenn Sie sich die Quelle für Data.Map ansehen, sehen Sie, dass die Standardimplementierung Data.Map.Lazy ist und dass Sie Data.Map.Strict verwenden sollten, wenn Sie schließlich alle gespeicherten Werte und die gespeicherten Werte benötigen stellen keine großen virtuellen Datenstrukturen dar, die faul berechnet werden müssen ". Scheint mir das genau zu beschreiben. – itsbruce
@itsbruce: Ich habe es versucht, aber es hat sich nicht viel geändert. Laut @ shangs Antwort war das größte Problem die Verwendung von 'String' anstelle von' Data.Text'. Ich werde heute Abend mehr testen. –
Sie könnten auch den Code parallelisieren, Wordcount ist ein typisches Beispiel für eine Berechnung im mapReduce-Stil; Sie können auch versuchen, mit '-hreaded' zu kompilieren und mit' + RTS-N 'zu laufen, um mehr Kerne auf Ihrem Rechner zu verwenden –
jev