Haskell Language
parallel~~POS=TRUNC
Sök…
parametrar
Typ / funktion | Detalj |
---|---|
data Eval a | Eval är en Monad som gör det lättare att definiera parallella strategier |
type Strategy a = a -> Eval a | en funktion som förkroppsligar en parallell utvärderingsstrategi. Funktionen går igenom (delar av) dess argument och utvärderar subexpressions parallellt eller i följd |
rpar :: Strategy a | gnister sitt argument (parallellt för utvärdering) |
rseq :: Strategy a | utvärderar sitt argument till svag huvud normal form |
force :: NFData a => a -> a | utvärderar hela strukturen i argumentet, reducerar det till normal form innan argumentet returneras. Den tillhandahålls av modulen Control.DeepSeq |
Anmärkningar
Simon Marlows bok , Concurrent and Parallel Programming in Haskell, är enastående och täcker en mängd begrepp. Det är också mycket tillgängligt för även den nyaste Haskell-programmeraren. Det rekommenderas starkt och finns tillgängligt i PDF eller online gratis.
Parallell kontra samtidigt
Simon Marlow uttrycker det bäst :
Ett parallellt program är ett som använder en mängd beräkningshårdvara (t.ex. flera processorkärnor) för att utföra en beräkning snabbare. Målet är att komma fram till svaret tidigare genom att delegera olika delar av beräkningen till olika processorer som kör samtidigt.
Däremot är samtidighet en programstruktureringsteknik där det finns flera styrtrådar. Konceptuellt utför styrtrådarna "samtidigt"; det vill säga användaren ser deras effekter sammanflätade. Huruvida de faktiskt körs samtidigt eller inte är en implementeringsdetalj; ett samtidigt program kan köras på en enda processor genom interleaved exekvering eller på flera fysiska processorer.
Svagt huvud Normal form
Det är viktigt att vara medveten om hur lat utvärdering fungerar. Det första avsnittet i detta kapitel kommer att ge en stark introduktion till WHNF och hur detta relaterar till parallell och samtidig programmering.
Eval Monad
Parallelism i Haskell kan uttryckas med hjälp av Eval
Monad från Control.Parallel.Strategies
, med hjälp av rpar
och rseq
funktionerna (bland andra).
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)
Om du kör main
ovan körs och "återgår" omedelbart, medan de två värdena, a
och b
beräknas i bakgrunden genom rpar
.
Obs: se till att du kompilerar med -threaded
för att parallellkörning ska ske.
RPAR
rpar :: Strategy a
genomför den givna strategin (återkall: type Strategy a = a -> Eval a
) parallellt:
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..])
Att köra detta kommer att visa det samtidiga beteendet:
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
Vi kan använda rseq :: Strategy a
att tvinga ett argument till svagt huvud 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)
Detta förändrar subtilt semantiken i rpar
exemplet; Det sistnämnda skulle återvända omedelbart medan beräkningen av värdena i bakgrunden väntar detta exempel tills a
kan utvärderas till WHNF.