首页 文章

如何在Haskell中使用并行策略

提问于
浏览
9

我有一个函数 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

我想在 frequencyBy 中并行运行 map . 我正在尝试使用 parList rdeepseq 实现这一点( main 中的所有其他内容只是为了确保不是所有内容都被优化掉了) . 但是,这并不能理解我在这里做错了什么 .

2 回答

  • 10

    可能是开销减慢了速度,取决于x的大小;如果你工作're doing in each spark is comparable to the time it takes to spawn each spark (and of course there'的调度开销等),那么你会遇到问题 .

    您可以尝试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 运行 .

  • 7

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

    当我从 parList 变为 parListChunk 500 时,执行时间增加了近50%;因为我和它一样好 .

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

相关问题