Ich habe Probleme, meinen Code parallel auszuführen. Es handelt sich um einen 3D-Delaunay-Generator, der einen Divide-und-Conquer-Algorithmus namens DeWall verwendet.
Die Hauptfunktion ist:
deWall::[SimplexPointer] -> SetSimplexFace -> Box -> StateT DeWallSets IO ([Simplex], [Edge])
deWall p afl box = do
...
...
get >>= recursion box1 box2 p1 p2 sigma edges
...
...
Sie ruft die "recursion"-Funktion auf, die die Dewall-Funktion wieder aufrufen könnte. Hier bietet sich die Möglichkeit zur Parallelisierung. Der folgende Code zeigt die sequentielle Lösung.
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
State und IO Monads werden verwendet, um den Zustand zu übermitteln und eine UID für jeden Tetraeder zu generieren, der MVar's verwendet. Mein erster Versuch war es, ein forkIO hinzuzufügen, aber es funktioniert nicht. Es gibt einen falschen Output aufgrund eines Mangels an Kontrolle während des Zusammenführungsteils, der nicht auf das Beenden beider Threads wartet. Ich weiß nicht, wie ich machen kann, dass es auf sie wartet.
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)
Also war mein nächster Versuch, eine bessere parallele Strategie "par" und "pseq" zu verwenden, die das richtige Ergebnis liefert, aber laut threadScope keine parallele Ausführung bietet.
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)
Was mache ich falsch?
UPDATE: Irgendwie scheint dieses Problem mit der Präsenz von IO Monaden zusammenhängen. In einer anderen (alten) Version ohne IO-Monade, nur mit der State-Monade, wird die parallele Ausführung mit 'par'
und 'pseq'
durchgeführt. Der GHC -sstderr gibt SPARKS: 1160 (69 converted, 1069 pruned)
aus.
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
Kann mir jemand das erklären?