C'est GC. Exécuté votre programme avec +RTS -s
et les résultats ont tout dit.
-N1
D:\>a +RTS -qa -N1 -s < lorem.txt
15470835
4,558,095,152 bytes allocated in the heap
1,746,720 bytes copied during GC
77,936 bytes maximum residency (118 sample(s))
131,856 bytes maximum slop
2 MB total memory in use (0 MB lost due to fragmentation)
Tot time (elapsed) Avg pause Max pause
Gen 0 8519 colls, 0 par 0.016s 0.021s 0.0000s 0.0001s
Gen 1 118 colls, 0 par 0.000s 0.004s 0.0000s 0.0001s
TASKS: 3 (1 bound, 2 peak workers (2 total), using -N1)
SPARKS: 0 (0 converted, 0 overflowed, 0 dud, 0 GC'd, 0 fizzled)
INIT time 0.000s ( 0.001s elapsed)
MUT time 0.842s ( 0.855s elapsed)
GC time 0.016s ( 0.025s elapsed)
EXIT time 0.016s ( 0.000s elapsed)
Total time 0.874s ( 0.881s elapsed)
Alloc rate 5,410,809,512 bytes per MUT second
Productivity 98.2% of total user, 97.4% of total elapsed
gc_alloc_block_sync: 0
whitehole_spin: 0
gen[0].sync: 0
gen[1].sync: 0
-N4
D:\>a +RTS -qa -N4 -s < lorem.txt
15470835
4,558,093,352 bytes allocated in the heap
1,720,232 bytes copied during GC
77,936 bytes maximum residency (113 sample(s))
160,432 bytes maximum slop
4 MB total memory in use (0 MB lost due to fragmentation)
Tot time (elapsed) Avg pause Max pause
Gen 0 8524 colls, 8524 par 4.742s 1.678s 0.0002s 0.0499s
Gen 1 113 colls, 112 par 0.031s 0.027s 0.0002s 0.0099s
Parallel GC work balance: 1.40% (serial 0%, perfect 100%)
TASKS: 6 (1 bound, 5 peak workers (5 total), using -N4)
SPARKS: 0 (0 converted, 0 overflowed, 0 dud, 0 GC'd, 0 fizzled)
INIT time 0.000s ( 0.001s elapsed)
MUT time 1.950s ( 1.415s elapsed)
GC time 4.774s ( 1.705s elapsed)
EXIT time 0.000s ( 0.000s elapsed)
Total time 6.724s ( 3.121s elapsed)
Alloc rate 2,337,468,786 bytes per MUT second
Productivity 29.0% of total user, 62.5% of total elapsed
gc_alloc_block_sync: 21082
whitehole_spin: 0
gen[0].sync: 0
gen[1].sync: 0
Les parties les plus importantes sont
Tot time (elapsed) Avg pause Max pause
Gen 0 8524 colls, 8524 par 4.742s 1.678s 0.0002s 0.0499s
et
Parallel GC work balance: 1.40% (serial 0%, perfect 100%)
Lorsque le commutateur -threaded
est activé, au moment de l'exécution, ghc fera de son mieux pour équilibrer autant que possible les tâches entre les threads. Votre programme entier est un processus séquentiel, le seul travail pouvant être déplacé vers d'autres threads est GC, alors que votre programme ne peut en fait pas être GCed en parallèle, donc ces threads attendent les uns les autres pour terminer leur travail, ce qui gaspille beaucoup de temps .
Si vous dites à l'exécution de ne pas équilibrer entre threads par +RTS -qm
alors parfois -N4 est aussi rapide que -N1.
bien * -fixé * ajoute un surcoût (et comme tu as dit que tu n'en as pas besoin avec ça) - pour aller plus vite j'essayerais probablement un IO non-paresseux ou un pipe/conduit (quelle est la taille de big.txt?) – Carsten
Merci @Carsten. Je vais essayer les tuyaux. 'big.txt' est pour l'analyse comparative et pourrait être illimité. Cela pourrait aider si ByteString utilisait des morceaux plus gros (ou plus petits, qui sait?). Mieux serait d'éviter l'allocation et d'utiliser un tampon fixe comme C si c'était possible. Je voudrais mettre un peu plus de traitement dans d'autres threads (tels que le comptage des caractères et des lignes) mais avec un tel overhead il n'y a aucun avantage à le faire. –
Le goulot d'étranglement est clairement IO et peut-être la mémoire ici; le CPU ne fait rien d'intéressant du tout. Le garbage collector peut bien être le coupable, je crois que le GC multithread est sensiblement plus compliqué. – leftaroundabout