Votre bug est pas lié à la votre code, il est de votre utilisation de l' -fext-core
d'imprimer la sortie de la compilation du Noyau Externe format. Il suffit de ne pas le faire (voir le noyau, j'utilise ghc-core).
Compiler avec -O2
et -threaded
:
$ ghc -O2 -rtsopts --make A.hs -threaded
[1 of 1] Compiling Main ( A.hs, A.o )
Linking A ...
Puis courir avec +RTS -N4
, par exemple, pour utiliser 4 threads:
$ time ./A +RTS -N4
[0.0,0.0,8.4375e-3,8.4375e-3,50.009375,50.009375,100.0,100.0]
./A 0.00s user 0.00s system 85% cpu 0.008 total
De sorte qu'il se termine trop vite pour voir la suite. Je vais augmenter votre m
et p
paramètres de 1k et 3k
$ time ./A +RTS -N2
./A +RTS -N2 3.03s user 1.33s system 159% cpu 2.735 total
Donc, oui, il ne s'exécutent en parallèle. 1,6 x sur un 2 core de la machine, lors d'une première tentative.
Si elle est ou non efficace est une autre question. Utilisez +RTS-s, vous pouvez voir l'exécution stats:
TÂCHES: 4 (1 lié, 3 pic de travailleurs (3 au total), aide -N2)
Nous avons donc eu 3 threads s'exécutant en parallèle (2 probablement pour l'algo, l'un pour le gestionnaire d'e / s).
Vous pouvez réduire le temps d'exécution par le réglage de la table de paramètres. E. g. en utilisant -A
nous pouvons réduire GC-dessus, et le rendement authentiques parallèle accélérations.
$ time ./A +RTS -N1 -A100M
./A +RTS -N1 -A100M 1.99s user 0.29s system 99% cpu 2.287 total
$ time ./A +RTS -N2 -A100M
./A +RTS -N2 -A100M 2.30s user 0.86s system 147% cpu 2.145 total
Vous pouvez améliorer les performances numérique, parfois à l'aide de la LLVM backend. Cela semble être le cas ici aussi:
$ ghc -O2 -rtsopts --make A.hs -threaded -fforce-recomp -fllvm
[1 of 1] Compiling Main ( A.hs, A.o )
Linking A ...
$ time ./A +RTS -N2 -A100M
./A +RTS -N2 -A100M 2.09s user 0.95s system 147% cpu 2.065 total
Rien de spectaculaire, mais vous êtes l'amélioration des temps d'exécution sur votre seule version filetée, et je n'ai pas modifié votre code d'origine en quelque sorte. Pour vraiment améliorer les choses, vous aurez besoin de profil et de les optimiser.
Revisiter l'Un des drapeaux, nous pouvons apporter à la fois vers le bas encore plus loin en utilisant un te sentir plus ajusté lié sur le thread initial de l'allocation de la zone.
$ ghc -Odph -rtsopts --make A.hs -threaded -fforce-recomp -fllvm
$ time ./A +RTS -N2 -A60M -s
./A +RTS -N2 -A60M 1.99s user 0.73s system 144% cpu 1.880 total
Ainsi apporté à 1.8 s de 2,7 (30% d'amélioration) à l'aide de la parallèle de l'exécution, le LLVM backend, et en faisant attention GC drapeaux. Vous pouvez regarder l'indicateur GC surface afin de trouver l'optimum:
Avec le creux autour de -A64 -N2
idéal pour le jeu de données de taille.
Je voudrais également fortement envisager d'utiliser le manuel de l'élimination des sous-expressions redondantes dans votre noyau, pour éviter de recalculer les choses à l'excès.
Comme Alp suggère, pour voir le comportement d'exécution du programme, compiler threadscope (à partir de Hackage) et l'exécuter comme suit:
$ ghc -O2 -fllvm -rtsopts -threaded -eventlog --make A.hs
$ ./A +RTS -ls -N2 -A60M
Et vous obtenez un événement de trace de vos deux cœurs comme suit:
Alors que ce passe ici? Vous avez une première période (0,8 s) de temps d'installation -- l'allocation de votre grande liste et l'encodage dans un repa tableau -- comme vous pouvez le voir par la mono-thread entrelacement de GC et de l'exécution. Ensuite, il y a un autre 0.8 s de quelque chose sur un seul core, avant votre travail parallèle se produit pour la dernière 300ms.
Donc, même si votre algorithme réel peut être la parallélisation de bien, tous les environs de configuration de test de coeur des marais du résultat. Si nous sérialiser vos dataset, puis il suffit de charger l'arrière à partir du disque, nous pouvons obtenir un meilleur comportement:
$ time ./A +RTS -N2 -A60M
./A +RTS -N2 -A60M 1.76s user 0.25s system 186% cpu 1.073 total
et maintenant votre profil semble plus sain:
Cela ressemble beaucoup! Très peu de GC (98.9% de productivité), et mes deux cœurs qui s'exécutent en parallèle et heureusement.
Donc, finalement, nous pouvons voir que vous obtenez un bon parallélisme:
Avec 1 core, 1.855 s
$ time ./A +RTS -N1 -A25M
./A +RTS -N1 -A25M 1.75s user 0.11s system 100% cpu 1.855 total
et avec 2 cœurs, 1.014 s
$ time ./A +RTS -N2 -A25M
./A +RTS -N2 -A25M 1.78s user 0.13s system 188% cpu 1.014 total
Maintenant, plus précisément de répondre à vos questions:
- Est-il plus idiomatiques façon de le faire dans le repa?
En général, repa code devrait consister en parallèle traverals, des consommateurs et de produit, et inlinable fonctions du noyau. Donc, tant que vous faites cela, alors le code est probablement idiomatiques. En cas de doute, regardez le tutoriel. Je préfère en général la marque de votre travailleur d'amandes (comme f
) comme inline.
- Sera la méthode ci-dessus, les cours en parallèle?
Le code s'exécute en parallèle, si vous utilisez parallèle combinators comme computeP
ou les différentes cartes et les plis. Donc oui, il faut et ne s'exécutent en parallèle.
- Comment puis-je déterminer si mon
le code est vraiment générer quelque chose qui sera exécuté dans
en parallèle?
Généralement, vous le savez a priori parce que vous utilisez les opérations en parallèle. En cas de doute, exécutez le code et d'observer l'accélération. Il peut être nécessaire d'optimiser le code.