Tengo problemas para hacer que mi código se ejecute en paralelo. Es un generador 3D Delaunay que utiliza un algoritmo de conquista de & dividido denominado DeWall.Paralelismo en el algoritmo de dividir y conquistar
La función principal es:
deWall::[SimplexPointer] -> SetSimplexFace -> Box -> StateT DeWallSets IO ([Simplex], [Edge])
deWall p afl box = do
...
...
get >>= recursion box1 box2 p1 p2 sigma edges
...
...
llama a la función "recursividad" que podría llamar a la función DeWall espalda. Y es aquí donde aparece la oportunidad de parallización. El siguiente código muestra la solución secuencial.
recursion::Box -> Box -> [SimplexPointer] -> [SimplexPointer] -> [Simplex] -> [Edge] -> DeWallSets -> StateT DeWallSets IO ([Simplex], [Edge])
recursion box1 box2 p1 p2 sigma edges deWallSet
| null afl1 && null afl2 = return (sigma, edges)
| (null) afl1 = do
(s, e) <- deWall p2 afl2 box2
return (s ++ sigma, e ++ edges)
| (null) afl2 = do
(s,e) <- deWall p1 afl1 box1
return (s ++ sigma, e ++ edges)
| otherwise = do
x <- get
liftIO $ do
(s1, e1) <- evalStateT (deWall p1 afl1 box1) x
(s2, e2) <- evalStateT (deWall p2 afl2 box2) x
return (s1 ++ s2 ++ sigma, e1 ++ e2 ++ edges)
where afl1 = aflBox1 deWallSet
afl2 = aflBox2 deWallSet
mónadas estatales y IO se utilizan para tubo del estado y para generar UID para cada tetraedro encontrado usando MVar de. Mi primer intento fue agregar un forkIO pero no funciona. Da un resultado incorrecto debido a una falta de control durante la parte de fusión que no espera a que terminen ambos subprocesos. No sé cómo hacerlo esperar por ellos.
liftIO $ do
let
s1 = evalStateT (deWall p1 afl1 box1) x
s2 = evalStateT (deWall p2 afl2 box2) x
concatThread var (a1, b1) = takeMVar var >>= \(a2, b2) -> putMVar var (a1 ++ a2, b1 ++ b2)
mv <- newMVar ([],[])
forkIO (s1 >>= concatThread mv)
forkIO (s2 >>= concatThread mv)
takeMVar mv >>= \(s, e) -> return (s ++ sigma, e ++ edges)
lo tanto, mi siguiente intento fue usar una mejor estrategia paralela "par" y "PSEQ" que da el resultado correcto, pero no la ejecución en paralelo de acuerdo con threadScope.
liftIO $ do
let
s1 = evalStateT (deWall p1 afl1 box1) x
s2 = evalStateT (deWall p2 afl2 box2) x
conc = liftM2 (\(a1, b1) (a2, b2) -> (a1 ++ a2, b1 ++ b2))
(stotal, etotal) = s1 `par` (s2 `pseq` (s1 `conc` s2))
return (stotal ++ sigma, etotal ++ edges)
¿Qué estoy haciendo mal?
ACTUALIZACIÓN: De alguna manera este problema parece estar relacionado con la presencia de IO mónadas. En otra versión (anterior) sin mónada IO, solo Mónada de estado, la ejecución paralela se ejecuta con 'par'
y 'pseq'
. El GHC -sstderr da SPARKS: 1160 (69 converted, 1069 pruned)
.
recursion::Box -> Box -> [SimplexPointer] -> [SimplexPointer] -> [Simplex] -> [Edge] -> DeWallSets -> State DeWallSets ([Simplex], [Edge])
recursion p1 p2 sigma deWallSet
| null afl1 && null afl2 = return sigma
| (null) afl1 = do
s <- deWall p2 afl2 box2
return (s ++ sigma)
| (null) afl2 = do
s <- deWall p1 afl1 box1
return (s ++ sigma)
| otherwise = do
x <- get
let s1 = evalState (deWall p1 afl1 box1) x
let s2 = evalState (deWall p2 afl2 box2) x
return $ s1 `par` (s2 `pseq` (s1 ++ s2 ++ sigma))
where afl1 = aflBox1 deWallSet
afl2 = aflBox2 deWallSet
¿Alguien en la nube explica eso?
Esto resolvió mi problema. He hecho solo una pequeña corrección usando mv2 <- newEmptyMVar en lugar de mv <- newMVar ([], []). Muchas gracias Axman6 – LambdaStaal