2012-01-13 81 views
8

使用並行策略我有一個函數frequencyBy,我想並行。這裏有個簡單的測試案例:如何在Haskell

import Control.Parallel.Strategies 
import Control.DeepSeq 
import System.Environment 

frequencyBy :: (a -> b -> Bool) -> [a] -> [b] -> [(a,Int)] 
frequencyBy f as bs = map 
    (\a ->(a, foldr (\b -> if f a b then (+) 1 else id) 0 bs)) as 

main :: IO() 
main = do 
    x:xs <- getArgs 
    let result = frequencyBy (==) [1::Int .. 10000] [1 .. (read x)] `using` 
       parList rdeepseq 
    print $ product $ map snd $ result 

我想在frequencyBy並行運行的map。我試圖使用parList rdeepseq來實現這一點(main中的所有其他內容僅用於確保不是所有內容都得到了優化)。但是,這不起作用,兩個線程的工作量是一個線程在同一時間內的兩倍。我不明白我在這裏做錯了什麼。

+3

如果兩個線程做兩倍的同時多的工作,一個線程,並不意味着它是正確parallelising? – ehird 2012-01-13 14:57:59

回答

9

這可能是開銷放慢下來,這取決於有多大X是;如果你在每個火花中所做的工作與產生每個火花所花費的時間相當(當然還有計劃開銷等),那麼你會遇到問題。

你可以嘗試parListChunk,例如parListChunk 64 rdeepseq;您將不得不嘗試確定要使用哪個塊大小。儘管當前策略正在爲列表中的每個元素創建一個火花,但是parListChunk會爲列表中的某個大小的每個塊創建一個火花,並使用您在該塊的每個元素上按順序指定的策略。

順便說一句,frequencyBy中的foldr可能由於過多的thunk創建而放慢速度;像

frequencyBy :: (a -> b -> Bool) -> [a] -> [b] -> [(a,Int)] 
frequencyBy f as bs = map (\a -> (a, sum . map (const 1) . filter (f a) $ bs)) as 

應該解決這個問題。

當然,一如既往,確保你與-O2編譯並與+RTS -N運行。

+0

這是不一樣的代碼; OP的功能相當於總和。 map(const 1)$ filter(f a)bs'或'length $ filter(f a)bs',雖然對我來說這兩方面都沒有改進(並且使用'length'的速度要慢得多)。 – 2012-01-13 15:17:10

+0

'parListChunk 2 rdeepseq'已經完成了這個技巧,並且確保它在兩個線程上只花費了一半的時間(與一個線程相比)。然而,這看起來很奇怪,爲什麼評估1的塊會帶來很多開銷,而2塊則會導致完美的並行化? – user362382 2012-01-13 15:17:19

+0

我用'sum。 map(const 1)$ filter(f a)bs'之前,但我發現手動將它融合到一個'foldr'中的速度更快。 – user362382 2012-01-13 15:19:28

7

我認爲你的並行性太細。 parList嘗試並行評估每個元素,並且對於任何一個元素確實沒有太多的工作。

當我從parList更改爲parListChunk 500近50%的執行時間的增加;因爲我在雙核機器上的性能差不多。

僅供參考,我與x=20000測試。