如何在Haskell中使用并行策略

时间:2012-01-13 14:42:13

标签: haskell parallel-processing

我有一个函数frequencyBy我希望并行化。以下是一个简单的测试用例:

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

我想在map并行运行frequencyBy。我正在尝试使用parList rdeepseq来实现这一点(main中的所有其他内容只是为了确保不会对所有内容进行优化)。但是,这不起作用,两个线程的工作量是同一个线程同时执行的两倍。我不明白我在这里做错了什么。

2 个答案:

答案 0 :(得分:10)

可能是开销减慢了速度,取决于 x 的大小;如果你在每个火花中所做的工作与产生每个火花所需的时间相当(当然还有调度开销等),那么你就会遇到问题。

您可以尝试parListChunk,例如parListChunk 64 rdeepseq;你必须尝试找出要使用的块大小。虽然您当前的策略是为列表中的每个元素创建一个火花,但parListChunk会为列表中的每个特定大小的块创建一个火花,并使用您在该块的每个元素上按顺序指定的策略。

顺便说一下,foldr中的frequencyBy可能会因为过多的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进行竞争。

答案 1 :(得分:7)

我认为你的并行性太精细了。 parList尝试并行评估每个元素,并且任何一个元素的确没有那么多工作。

当我从parList更改为parListChunk 500时,执行时间增加了近50%;因为我在双核机器上,它的性能一样好。

作为参考,我正在使用x=20000进行测试。