Haskell Language
Parallélisme
Recherche…
Paramètres
Type / Fonction | Détail |
---|---|
data Eval a | Eval est une Monade qui facilite la définition de stratégies parallèles |
type Strategy a = a -> Eval a | une fonction qui incarne une stratégie d'évaluation parallèle. La fonction parcourt son argument en évaluant les sous-expressions en parallèle ou en séquence |
rpar :: Strategy a | déclenche son argument (pour une évaluation en parallèle) |
rseq :: Strategy a | évalue son argument à la forme normale de la tête faible |
force :: NFData a => a -> a | évalue la structure entière de son argument en le réduisant à la forme normale avant de renvoyer l'argument lui-même. Il est fourni par le module Control.DeepSeq |
Remarques
Le livre de Simon Marlow , Programmation simultanée et parallèle à Haskell, est remarquable et couvre une multitude de concepts. Il est également très accessible même pour le plus récent programmeur Haskell. Il est fortement recommandé et disponible en PDF ou en ligne gratuitement.
Parallèle vs Concurrent
Simon Marlow le dit mieux :
Un programme parallèle est un programme qui utilise une multiplicité de matériel informatique (par exemple, plusieurs cœurs de processeur) pour effectuer un calcul plus rapidement. L'objectif est d'arriver à la réponse plus tôt, en déléguant différentes parties du calcul à différents processeurs exécutant en même temps.
En revanche, la concurrence est une technique de structuration de programme dans laquelle il existe plusieurs threads de contrôle. Conceptuellement, les threads de contrôle s'exécutent «en même temps»; c'est-à-dire que l'utilisateur voit ses effets entrelacés. Qu'elles soient exécutées en même temps ou non, c'est un détail d'implémentation. un programme simultané peut s'exécuter sur un seul processeur par exécution entrelacée ou sur plusieurs processeurs physiques.
Forme normale de tête faible
Il est important de savoir comment fonctionne l'évaluation paresseuse. La première section de ce chapitre présentera une introduction solide sur le WHNF et sa relation avec la programmation parallèle et simultanée.
L'éval monade
Le parallélisme dans Haskell peut être exprimé à l'aide de l' Eval
Monad de Control.Parallel.Strategies
, en utilisant les fonctions rpar
et rseq
(entre autres).
f1 :: [Int]
f1 = [1..100000000]
f2 :: [Int]
f2 = [1..200000000]
main = runEval $ do
a <- rpar (f1) -- this'll take a while...
b <- rpar (f2) -- this'll take a while and then some...
return (a,b)
En cours d'exécution au main
dessus s'exécutera et "retournera" immédiatement, tandis que les deux valeurs, a
et b
seront calculées en arrière-plan via rpar
.
Remarque: assurez-vous de compiler avec -threaded
pour que l'exécution en parallèle se produise.
rpar
rpar :: Strategy a
exécute la stratégie donnée (rappel: type Strategy a = a -> Eval a
) en parallèle:
import Control.Concurrent
import Control.DeepSeq
import Control.Parallel.Strategies
import Data.List.Ordered
main = loop
where
loop = do
putStrLn "Enter a number"
n <- getLine
let lim = read n :: Int
hf = quot lim 2
result = runEval $ do
-- we split the computation in half, so we can concurrently calculate primes
as <- rpar (force (primesBtwn 2 hf))
bs <- rpar (force (primesBtwn (hf + 1) lim))
return (as ++ bs)
forkIO $ putStrLn ("\nPrimes are: " ++ (show result) ++ " for " ++ n ++ "\n")
loop
-- Compute primes between two integers
-- Deliberately inefficient for demonstration purposes
primesBtwn n m = eratos [n..m]
where
eratos [] = []
eratos (p:xs) = p : eratos (xs `minus` [p, p+p..])
L'exécution de cette opération démontrera le comportement simultané:
Enter a number
12
Enter a number
Primes are: [2,3,5,7,8,9,10,11,12] for 12
100
Enter a number
Primes are: [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100] for 100
200000000
Enter a number
-- waiting for 200000000
200
Enter a number
Primes are: [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200] for 200
-- still waiting for 200000000
rseq
On peut utiliser rseq :: Strategy a
pour forcer un argument à Weak Head Normal Form:
f1 :: [Int]
f1 = [1..100000000]
f2 :: [Int]
f2 = [1..200000000]
main = runEval $ do
a <- rpar (f1) -- this'll take a while...
b <- rpar (f2) -- this'll take a while and then some...
rseq a
return (a,b)
Cela modifie subtilement la sémantique de l'exemple rpar
; alors que celui - ci retourneraient immédiatement tout en calculant les valeurs en arrière - plan, cet exemple attendre a
peut être évalué à WHNF.