J'essaie de paralléliser un traceur de rayons. Cela signifie que j'ai une très longue liste de petits calculs. Le programme vanilla s'exécute sur une scène spécifique en 67,98 secondes et 13 Mo d'utilisation totale de la mémoire et 99,2% de productivité.
Dans ma première tentative, j'ai utilisé la stratégie parallèle parBuffer
avec une taille de tampon de 50. J'ai choisi parBuffer
parce qu'elle parcourt la liste seulement aussi vite que les étincelles sont consommées, et ne force pas le dos de la liste comme parList
, ce qui utiliserait beaucoup de mémoire puisque la liste est très longue. Avec -N2
, il a fonctionné en un temps de 100,46 secondes et 14 Mo d'utilisation totale de la mémoire et une productivité de 97,8%. Les informations sur l'étincelle sont:SPARKS: 480000 (476469 converted, 0 overflowed, 0 dud, 161 GC'd, 3370 fizzled)
La grande proportion d'étincelles pétillantes indique que la granularité des étincelles était trop petite, alors j'ai ensuite essayé d'utiliser la stratégie parListChunk
, qui divise la liste en morceaux et crée une étincelle pour chaque morceau. J'ai obtenu les meilleurs résultats avec une taille de morceau de 0.25 * imageWidth
. Le programme a fonctionné en 93,43 secondes et 236 Mo d'utilisation totale de la mémoire et 97,3% de productivité. Les informations d'allumage est: SPARKS: 2400 (2400 converted, 0 overflowed, 0 dud, 0 GC'd, 0 fizzled)
. Je crois que l'utilisation beaucoup plus grande de la mémoire est due au fait que parListChunk
la colonne vertébrale de la liste est forcée.
Ensuite, j'ai essayé d'écrire ma propre stratégie en divisant paresseusement la liste en morceaux, puis en les transmettant parBuffer
et en concaténant les résultats.
concat $ withStrategy (parBuffer 40 rdeepseq) (chunksOf 100 (map colorPixel pixels))
Cela a duré 95,99 secondes et 22 Mo d'utilisation totale de la mémoire et 98,8% de productivité. Cela a été un succès dans le sens où toutes les étincelles sont converties et l'utilisation de la mémoire est beaucoup plus faible, mais la vitesse n'est pas améliorée. Voici une image d'une partie du profil Eventlog.
Comme vous pouvez le voir, les threads sont arrêtés en raison de débordements de tas. J'ai essayé d'ajouter +RTS -M1G
ce qui augmente la taille du tas par défaut jusqu'à 1 Go. Les résultats n'ont pas changé. J'ai lu que le thread principal Haskell utilisera la mémoire du tas si sa pile débordait, j'ai donc également essayé d'augmenter la taille de la pile par défaut, +RTS -M1G -K1G
mais cela n'a pas non plus d'impact.
Y a-t-il autre chose que je puisse essayer? Je peux publier des informations de profilage plus détaillées pour l'utilisation de la mémoire ou le journal des événements si nécessaire, je n'ai pas tout inclus car il s'agit de beaucoup d'informations et je ne pensais pas que tout était nécessaire.
EDIT: Je lisais sur le support multicœur Haskell RTS , et cela parle de l'existence d'un HEC (Haskell Execution Context) pour chaque noyau. Chaque HEC contient, entre autres, une zone d'allocation (qui fait partie d'un seul tas partagé). Chaque fois qu'une zone d'allocation d'un HEC est épuisée, un ramasse-miettes doit être effectué. Le semble être une option RTS pour le contrôler, -A. J'ai essayé -A32M mais je n'ai vu aucune différence.
EDIT2: Voici un lien vers un repo github dédié à cette question . J'ai inclus les résultats du profilage dans le dossier de profilage.
EDIT3: Voici le bit de code pertinent:
render :: [([(Float,Float)],[(Float,Float)])] -> World -> [Color]
render grids world = cs where
ps = [ (i,j) | j <- reverse [0..wImgHt world - 1] , i <- [0..wImgWd world - 1] ]
cs = map (colorPixel world) (zip ps grids)
--cs = withStrategy (parListChunk (round (wImgWd world)) rdeepseq) (map (colorPixel world) (zip ps grids))
--cs = withStrategy (parBuffer 16 rdeepseq) (map (colorPixel world) (zip ps grids))
--cs = concat $ withStrategy (parBuffer 40 rdeepseq) (chunksOf 100 (map (colorPixel world) (zip ps grids)))
Les grilles sont des flottants aléatoires qui sont précalculés et utilisés par colorPixel. Le type de colorPixel
est:
colorPixel :: World -> ((Float,Float),([(Float,Float)],[(Float,Float)])) -> Color
Strategy
. J'aurais dû choisir un meilleur mot. En outre, le problème de débordement de tas se produit avec parListChunk
et parBuffer
aussi.
concat $ withStrategy …
? Je ne peux pas reproduire ce comportement dans6008010
, qui est le commit le plus proche de votre modification.