El paral·lelisme és una tècnica que permet executar múltiples càlculs simultàniament, aprofitant els recursos de maquinari disponibles, com ara múltiples nuclis de CPU. En Haskell, el paral·lelisme es pot aconseguir de diverses maneres, utilitzant biblioteques i funcions específiques. Aquest tema cobrirà els conceptes bàsics del paral·lelisme en Haskell, incloent-hi exemples pràctics i exercicis per ajudar-te a comprendre com implementar paral·lelisme en els teus programes.
Conceptes Clau
-
Paral·lelisme vs Concurrència:
- Paral·lelisme: Executar múltiples càlculs simultàniament per reduir el temps total d'execució.
- Concurrència: Gestionar múltiples tasques alhora, però no necessàriament executant-les simultàniament.
-
Eines per al Paral·lelisme en Haskell:
par
ipseq
- Estratègies de paral·lelisme (
Control.Parallel.Strategies
) async
iawait
(Control.Concurrent.Async
)
Exemples Pràctics
Exemple 1: Utilitzant par
i pseq
El mòdul Control.Parallel
proporciona les primitives par
i pseq
per indicar al compilador que certs càlculs es poden realitzar en paral·lel.
import Control.Parallel (par, pseq) -- Funció per calcular la suma de quadrats de dos llistes en paral·lel sumOfSquares :: [Int] -> [Int] -> Int sumOfSquares xs ys = let sumX = sum (map (^2) xs) sumY = sum (map (^2) ys) in sumX `par` (sumY `pseq` (sumX + sumY)) main :: IO () main = print $ sumOfSquares [1..1000000] [1..1000000]
Explicació:
par
indica quesumX
es pot calcular en paral·lel amb el càlcul desumY
.pseq
assegura quesumY
es calcula abans de combinar els resultats.
Exemple 2: Utilitzant Estratègies de Paral·lelisme
El mòdul Control.Parallel.Strategies
proporciona una manera més estructurada de definir com es poden paral·lelitzar els càlculs.
import Control.Parallel.Strategies (parList, rseq, using) -- Funció per calcular la suma de quadrats de una llista en paral·lel sumOfSquares :: [Int] -> Int sumOfSquares xs = sum (map (^2) xs `using` parList rseq) main :: IO () main = print $ sumOfSquares [1..1000000]
Explicació:
parList rseq
aplica l'estratègia de paral·lelisme a cada element de la llista.using
aplica l'estratègia especificada a la llista.
Exemple 3: Utilitzant async
i await
El mòdul Control.Concurrent.Async
permet crear tasques asíncrones que es poden esperar posteriorment.
import Control.Concurrent.Async (async, wait) -- Funció per calcular la suma de quadrats de dos llistes en paral·lel sumOfSquares :: [Int] -> [Int] -> IO Int sumOfSquares xs ys = do a1 <- async $ return $ sum (map (^2) xs) a2 <- async $ return $ sum (map (^2) ys) sumX <- wait a1 sumY <- wait a2 return (sumX + sumY) main :: IO () main = do result <- sumOfSquares [1..1000000] [1..1000000] print result
Explicació:
async
crea una tasca asíncrona.wait
espera que la tasca asíncrona es completi i retorna el resultat.
Exercicis Pràctics
Exercici 1: Paral·lelitzar el Càlcul de Factorials
Escriu una funció que calculi el factorial de dos nombres en paral·lel utilitzant par
i pseq
.
import Control.Parallel (par, pseq) factorial :: Int -> Int factorial 0 = 1 factorial n = n * factorial (n - 1) factorialParallel :: Int -> Int -> Int factorialParallel x y = let factX = factorial x factY = factorial y in factX `par` (factY `pseq` (factX + factY)) main :: IO () main = print $ factorialParallel 10 20
Exercici 2: Utilitzant Estratègies de Paral·lelisme
Escriu una funció que calculi la suma de cubs d'una llista en paral·lel utilitzant parList
i rseq
.
import Control.Parallel.Strategies (parList, rseq, using) sumOfCubes :: [Int] -> Int sumOfCubes xs = sum (map (^3) xs `using` parList rseq) main :: IO () main = print $ sumOfCubes [1..1000000]
Resum
En aquesta secció, hem après els conceptes bàsics del paral·lelisme en Haskell i hem vist com utilitzar par
i pseq
, estratègies de paral·lelisme, i async
i await
per paral·lelitzar càlculs. Els exercicis pràctics proporcionats t'ajudaran a consolidar aquests conceptes i a aplicar-los en els teus propis programes. En la següent secció, explorarem la concurrència en Haskell, que és una tècnica relacionada però diferent del paral·lelisme.