Voici quelques possibilités. D'abord, assurez-vous à 100% que vous exécutez réellement votre programme avec +RTS -N2 -RTS
. Je ne peux pas vous dire combien de fois j'ai été en train de benchmarker un programme parallèle et j'ai écrit :
stack exec myprogram +RTS -N2 -RTS
à la place de :
stack exec myprogram -- +RTS -N2 -RTS
et je me suis désespérément embrouillé. (La première version exécute le exécutable de la pile sur deux processeurs mais l'exécutable cible sur un seul !) Peut-être ajouter un print $ getNumCapabilities
au début de votre main
pour être sûr.
Après avoir confirmé que vous fonctionnez sur deux processeurs, le problème le plus probable est que votre implémentation ne fonctionne pas en espace constant et fait exploser le tas. Voici un programme de test simple que j'ai utilisé pour essayer de reproduire votre problème. (N'hésitez pas à utiliser vous-même mon superbe filtre d'upsampling).
module Main where
import Control.Concurrent.Async
import System.Environment
import qualified Data.ByteString as B
upsample :: FilePath -> IO ()
upsample fp = do c <- B.readFile fp
let c' = B.pack $ concatMap (replicate 4) $ B.unpack c
B.writeFile (fp ++ ".out") c'
upsampleFiles :: [FilePath] -> IO ()
upsampleFiles files = do
forConcurrently_ files $ upsample
main :: IO ()
main = upsampleFiles =<< getArgs -- sample all file on command line
Lorsque je l'ai fait fonctionner sur un seul fichier de test de 70 mégaoctets, il s'est exécuté en 14 secondes. Lorsque je l'ai exécuté sur deux copies en parallèle, il a fonctionné pendant plus d'une minute avant de commencer à échanger comme un fou, et j'ai dû le tuer. Après être passé à :
import qualified Data.ByteString.Lazy as B
il a fonctionné en 3,7 secondes sur un seul fichier, 7,8 secondes sur deux copies sur un seul processeur, et 4,0 secondes sur deux copies sur deux processeurs avec +RTS -N2
.
Assurez-vous que vous compilez avec les optimisations activées, profilez votre programme, et assurez-vous qu'il s'exécute dans un espace de tas constant (ou au moins raisonnable). Le programme ci-dessus s'exécute dans un espace de tas constant de 100k octets. Une version similaire qui utilise un ByteString
pour la lecture et le farniente ByteString
pour l'écriture lit tout le fichier en mémoire, mais le tas augmente presque immédiatement jusqu'à 70 mégaoctets (la taille du fichier) en une fraction de seconde et reste ensuite constant pendant le traitement du fichier.
Quelle que soit la complexité de votre filtre, si votre programme fait croître des gigaoctets de tas, l'implémentation est défectueuse et vous devrez la corriger avant de vous préoccuper des performances, parallèles ou autres.