2012-04-20 3 views
13

Я пытаюсь использовать параллелизм в Haskell для конкретной оптимизации, в которой требуется только одно из двух значений, и в зависимости от ситуации, каждый может быть намного быстрее, чем другой.Совместимость Haskell - forkIO действительно недетерминирован?

Я думал, что могу просто запустить 2 потока с forkIO, а затем подождать, пока значение не будет размещено в MVar. Вот простой тест я написал для этого:

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) 

Собран с:

ghc --make -threaded Test 

Однако только случай с левого сек когда-либо достигнуто, хотя получение премьер должен занять достаточно много времени для makeInt нить для начала (и возврат 2 действительно не должен занимать столько времени). Почему это и как я это исправить?

+0

Как вы запускаете свой код? По умолчанию haskell использует световые потоки, а не реальные потоки ОС. Я не знаю подробностей, но это может изменить много политик планирования. –

+0

Возможно, вам не подходит ваш случай, но вы также можете изучить некоторые из выполненных работ по «спекулятивному параллелизму»: http://hackage.haskell.org/package/speculation – jberryman

+3

FYI, http://hackage.haskell.org/package/monad-par предоставляет довольно красивый, внешне чистый API параллелизма, который тем не менее позволяет явно указывать вилки, объединения и т. д. –

ответ

20

Проблема здесь лень. makeString просто вставляет thunk для вычисления show (primes !! 10000), который затем затем оценивается по основному потоку. Вставка thunk довольно быстро, так что это происходит, чтобы выиграть гонку в этом случае.

Чтобы заставить оценки произойти в потоке, вы можете изменить return к evaluate:

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

Это должно привести к makeInt, чтобы выиграть гонку в большинстве случаев (хотя это не гарантировано).

+1

Спасибо! Я полностью забыл объяснить лень. – Cubic

10

Да, потоки действительно не детерминированы (в GHC).

Просто ваш конкретный код структурирован и оптимизирован таким образом, что t1 всегда выигрывает. Нет никаких гарантий.

Если вы хотите попробовать массажировать его для получения другого результата, попробуйте включить оптимизацию (-O2) и/или используя несколько ядер (+RTS -N).

E.g. на моей машине, две трассы подряд:

$ 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 

Как Хаммар указывает, вы также можете структурировать свой код, чтобы заставить больше работать, чтобы занять место в потоке (или переключиться на using strict mvars).

+0

Спасибо - я даже не знал, что могу передавать параметры в RTS. – Cubic

Смежные вопросы