我正在尝试在Haskell中学习并发性和并行性。 我刚刚开始了Simon Marlow的书。不过一切都很清楚。 互联网上没有很多(如果有的话)简单的例子。
这只是一个简单的主要搜索功能,用于学习目的。 这可以并行化吗?如果是的话,有人可以用这个功能展示一个例子。
main = mapM_ print [x | x <- [2..], isPrime x]
isPrime :: Integer -> Bool
isPrime n = odd n && all (\ f -> n `mod` f /= 0) [3..(ceiling $ sqrt $ fromIntegral n)]
我知道我可以使用策略并行地映射列表。 例如,我如何以8个批次测试列表中的因子,并行进行8个测试? 我想要一个例子。
感谢。
答案 0 :(得分:1)
此答案也可在http://lpaste.net/143207
的.lhs文件中找到基本思想是每当你有一个表达式:
map f xs
您可以将其替换为:
parMap strat f xs
将地图计算产生为火花,这样就可以了
在线程运行时并行执行。
strat
的典型选择是rdeepseq
- 有关其他选项,请参阅Basic Strategies。
问题是每次调用f
都会产生
火花可能不符合成本效益。实现任何加速
你可能需要组织工作,以便产生火花
用于在列表的一系列元素上调用f
。
让我们这样写isPrime
:
-- original version
isPrime0 n = and $ map notfactor [2..limit]
where limit = div n 2
notfactor f = mod n f /= 0
(我故意扩展了因子测试范围,所以我们没有 必须使用大质数进行测试。)
第一个想法是简单地将map
更改为parMap rdeepseq
:
-- spawn each call to notfactor as a spark
isPrime1 n = and $ parMap rdeepseq notfactor [2..limit]
where limit = div n 2
notfactor f = mod n f /= 0
但是,如果你对此进行基准测试,你会发现 这比连续版本慢得多。
下一个想法是将范围[2..limit]
分解为
像这样的少量块:
-- evaluate the notfactor calls in ranges -- not parallized
isPrime2 n = and $ map (\r -> all notfactor r) ranges
where limit = div n 2 -- ceiling $ sqrt $ fromIntegral n
notfactor f = mod n f /= 0
ranges = chunks 3 limit 10
这里chunks a b k
是一个分裂的函数
将[a..b]
列入k
相等大小的范围。
要获得平行版本,我们会更改map
电话
进入parMap rdeepseq
:
-- evaluate the notfacto calls in ranges - parallelized
isPrime3 n = and $ parMap rdeepseq (\r -> all notfactor r) ranges
where limit = div n 2
notfactor f = mod n f /= 0
ranges = chunks 3 limit 10
主要15485863和。的一些粗略时间(以秒为单位)
RTS选项-N1
与-N2
:
-N1 -N2
isPrime0 0.624 0.673
isPrime1 12.--- 12.---
isPrime2 0.573 0.603
isPrime3 0.563 0.365
正如您所看到的,isPrime3
确实表现出一些加速。 isPrime1
的时间是因为它与isPrime3
相比产生了数百万个火花,只产生了10个火花。
为了完整性,这里是chunks
的代码和。{
程序驱动程序。
-- divide a range into equal size chunks
chunks :: Integer -> Integer -> Integer -> [[Integer]]
chunks a b k =
let (q,r) = divMod (b - a) k
sizes = replicate (fromIntegral r) (q+1) ++ replicate (fromIntegral (k-r)) q
go x [] = []
go x (y:ys) = [x..x+y-1] : go (x+y) ys
in go a sizes
main :: IO ()
main = do
( which : ps : _ ) <- getArgs
let p = read ps
case which of
"0" -> print $ isPrime0 p
"1" -> print $ isPrime1 p
"2" -> print $ isPrime2 p
"3" -> print $ isPrime3 p