简单素数搜索函数中的并行性

时间:2015-10-17 09:38:26

标签: haskell

我正在尝试在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个测试? 我想要一个例子。

感谢。

1 个答案:

答案 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