2012-04-20 10 views
13

Sto tentando di utilizzare la concorrenza in Haskell per un'ottimizzazione specifica in cui è richiesto solo uno dei due valori e, a seconda della situazione, uno può essere molto più veloce da creare rispetto all'altro.Concorrenza di Haskell - forkIO è davvero non deterministico?

Ho pensato di poter eseguire solo 2 thread con forkIO e quindi attendere che un valore venga inserito in un MVar. Ecco un semplice test che ho scritto per questo:

import Control.Concurrent 

main = do out <- newEmptyMVar 
      t1 <- forkIO (makeString out) 
      t2 <- forkIO (makeInt out) 
      v <- takeMVar out 
      killThread t1 
      killThread t2 
      case v of 
       Left s -> putStrLn s 
       Right i -> putStrLn $ show i 

makeString out = do s <- return (show (primes !! 10000)) 
        putMVar out $ Left s 

makeInt out = do i <- return 2 
       putMVar out $ Right i 

primes = sieve [2..] 
where sieve (x:xs) = x : (sieve $ filter ((/=0).(flip mod x)) xs) 

Compilato con:

ghc --make -threaded Test 

Tuttavia, unico caso la sinistra s è mai raggiunto, anche se sempre il primo dovrebbe prendere abbastanza a lungo per la MAKEINT thread per iniziare (e restituire 2 in realtà non dovrebbe richiedere molto tempo). Perché è così, e come posso risolvere questo?

+0

Come si esegue il codice? Per impostazione predefinita haskell utilizza thread leggeri anziché veri thread del sistema operativo. Non conosco i dettagli, ma questo potrebbe cambiare molte politiche di pianificazione. –

+0

Potrebbe non adattarsi al tuo caso, ma potresti anche esaminare alcuni dei lavori sul "parallelismo speculativo" in corso: http://hackage.haskell.org/package/speculation – jberryman

+3

FYI, http://hackage.haskell.org/package/monad-par espone un'API di parallelismo piuttosto piacevole, esternamente pura che tuttavia ti consente di dichiarare in modo esplicito fork, join, ecc. –

risposta

20

Il problema qui è la pigrizia. Il makeString sta semplicemente inserendo un thunk per calcolare show (primes !! 10000), che poi viene valutato dal thread principale in seguito. Inserire il thunk è abbastanza veloce, quindi in questo caso capita di vincere la gara.

Per forzare la valutazione avvenire entro il filo, è possibile modificare return-evaluate:

makeString out = do s <- evaluate $ show (primes !! 10000) 
        putMVar out $ Left s 

Questo dovrebbe causare makeInt per vincere la gara, nella maggior parte dei casi (anche se non è garantita).

+1

Grazie! Ho completamente dimenticato di spiegare la pigrizia. – Cubic

10

Sì, i thread in realtà non sono deterministici (in GHC).

Accade semplicemente che il tuo particolare codice sia strutturato e ottimizzato in modo tale che t1 vinca sempre. Non ci sono garanzie.

Se si desidera provare a eseguire il massaggio per produrre un risultato diverso, provare ad attivare le ottimizzazioni (-O2) e/o utilizzare più core (+RTS -N).

E.g. sulla mia macchina, due piste di fila:

$ ghc -O2 -threaded --make A.hs -rtsopts -fforce-recomp 
[1 of 1] Compiling Main    (A.hs, A.o) 
Linking A.exe ... 
$ ./A +RTS -N2 
2 
$ ./A +RTS -N2 
104743 

Come sottolinea Hammar, è anche possibile strutturare il codice per forzare più di lavoro che si terrà nel thread (o passare a using strict mvars).

+0

Grazie - non sapevo nemmeno di poter passare i parametri all'RTS. – Cubic

Problemi correlati