我有一个函数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
中的所有其他内容只是为了确保不会对所有内容进行优化)。但是,这不起作用,两个线程的工作量是同一个线程同时执行的两倍。我不明白我在这里做错了什么。
答案 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
进行测试。