2012-04-20 13 views
13

Estoy tratando de usar concurrencia en Haskell para una optimización específica en la que solo se requiere uno de dos valores, y dependiendo de la situación, cualquiera de los dos puede ser mucho más rápido de crear que el otro.concurrencia Haskell - ¿es forkIO realmente no determinista?

Pensé que podría simplemente ejecutar 2 hilos con forkIO, y luego esperar hasta que se coloque un valor en un MVar. He aquí una sencilla prueba que he escrito para esto:

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) 

Compilado con:

ghc --make -threaded Test 

Sin embargo, sólo es cada vez alcanzó caso la Izquierda s, aunque para conseguir el primer debería tener el tiempo suficiente para la makeInt hilo para comenzar (y return 2 realmente no debería tomar tanto tiempo). ¿Por qué es eso y cómo soluciono esto?

+0

¿Cómo se ejecuta el código? Por defecto, haskell usa hilos ligeros en lugar de hilos del sistema operativo real. No sé los detalles, pero esto puede cambiar muchas políticas de programación. –

+0

Puede que no se ajuste a su caso, pero también puede consultar parte del trabajo sobre el "paralelismo especulativo" que se está realizando: http://hackage.haskell.org/package/speculation – jberryman

+3

FYI, http://hackage.haskell.org/package/monad-par expone una API de paralelismo bastante agradable, externamente pura que, no obstante, te permite indicar explícitamente tenedores, combinaciones, etc. –

Respuesta

20

El problema aquí es la pereza. El makeString solo está insertando un procesador para calcular show (primes !! 10000), que luego será evaluado por el hilo principal después. Insertar el procesador es bastante rápido, por lo que resulta que gana la carrera en este caso.

Para forzar la evaluación a suceder dentro de la rosca, se puede cambiar a returnevaluate:

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

Esto debería hacer makeInt para ganar la carrera en la mayoría de los casos (aunque no está garantizado).

+1

¡Gracias! Me olvidé por completo de dar cuenta de la pereza. – Cubic

10

Sí, los hilos realmente no son deterministas (en GHC).

Sucede que su código particular está estructurado y optimizado de tal manera que t1 siempre gana. No hay garantías

Si desea intentar realizar un masaje para producir un resultado diferente, intente activar las optimizaciones (-O2) y/o el uso de varios núcleos (+RTS -N).

E.g. en mi máquina, dos carreras en una 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 

Como hammar señala, también se puede estructurar su código para forzar más trabajo que tendrá lugar en el hilo (o cambiar a using strict mvars).

+0

Gracias, ni siquiera sabía que podía pasar parámetros al RTS. – Cubic

Cuestiones relacionadas