2015-06-26 33 views
2

根據關於hackage的Control.Concurrent.MVar文檔,我們對使用MVar s有一個「疑難」。這是link使用Haskell的MVar包但嚴格執行seq

MVar說,當您使用putMVarMVar中放置某些東西時,如果您要放入的東西是一個巨大的thunk,接收線程將有繁瑣的評估工作,而不是發送線程。

除了這可能是煩人的或無根據的,爲了彌補這種情況,它指向我們的方向evaluate,其本身說使用seq。每個人最喜歡的haskell函數。

Evaluate的語義據說意思是這樣的:

evaluate x `seq` y ==> y 

所以我的問題是:爲什麼不會這樣評價在分叉線程!?!?!

concTreeMap :: (a -> b) -> BinaryTree a -> IO (BinaryTree b) 
concTreeMap f Leaf = return Leaf 
concTreeMap f (Branch v l r) = do 
    res <- newEmptyMVar 
    forkIO $ do 
       let fv = f v 
       evaluate fv `seq` (putMVar res fv) 
    v' <- takeMVar res 
    l' <- concTreeMap f l 
    r' <- concTreeMap f r 
    return (Branch v' l' r') 

編輯追加同等加速...

不知怎的,這等同於以下(不使用評價,但使用了序列)的答案......反正我覺得加快點是a)提供提示,關於thunk的評價和2 Haskell的運行時)從移動的認沽

concTreeMap :: (a -> b) -> BinaryTree a -> IO (BinaryTree b) 
concTreeMap f Leaf = return Leaf 
concTreeMap f (Branch v l r) = do 
    res <- newEmptyMVar 
    forkIO $ do { let fv = f v in fv `seq` putMVar res fv } 
    l' <- concTreeMap f l 
    r' <- concTreeMap f r 
    v' <- takeMVar res 
    return (Branch v' l' r') 
+2

您引用的那個方程式正是說''evaluate'seq' y''與'y'相同,即您使用'evaluate'什麼都不做。 –

回答

6

假設你定你的程序在切赫Pudlák的答案,你想從MVar服用。所以你沒有平行性,因爲運行concTreeMap的線程必須等待MVar已滿,這意味着等待分叉線程將fv放在MVar中,直到它對它進行評估之後纔會執行。同時,原來的線程什麼都不做。

沒你的意思寫

... 
    forkIO $ do 
       let fv = f v 
       evaluate fv `seq` (putMVar res fv) 
    l' <- concTreeMap f l 
    r' <- concTreeMap f r 
    v' <- takeMVar res   -- Note: this moved to after we do more work 
    return (Branch v' l' r') 

+0

你是一個活着的傳奇人物,現在我該如何將積分兌現給你們兩個呢哈哈?!?!? – lol

5

因爲使用seq強制的evaluate fv的評價帶走。這是一個IO行動,並且評估它是完全不同於運行它。

如果您認爲IO作爲一個配方,運行seq,評估它,只是確保配方完全寫下來,但運行實際上是使用配方產生的東西。

分叉部分應該是啓動線程把一個值後立即代替

forkIO $ do 
      let fv = f v 
      evaluate fv 
      putMVar res fv 
+0

我可以看到你在說什麼,但這還是比不同時做。我也覺得你可能沒有足夠的信息來診斷這個問題,儘管...嗯 – lol

+0

@lol你如何衡量速度?你的'f'有多複雜?你確定併發性是否被overhad(切換CPU上下文,使用'MVar's並不比並行化的增益大嗎? –

+0

'f'是一個階乘函數,我用'time'和googlers來計時在這裏,請記住在你的測試中使用'ghc -threaded'進行編譯,'O1','O2'等的控制 – lol