并行排序IO操作

时间:2012-10-28 15:04:02

标签: haskell io parallel-processing

我有一个返回IO动作的函数,

f :: Int -> IO Int

我想为参数的多个值并行计算这个函数。我的天真实施如下:

import Control.Parallel.Strategies

vals = [1..10]
main = do
      results <- mapM f vals
      let results' = results `using` parList rseq
      mapM_ print results'

我的理由是,第一个mapMIO [Int]类型绑定到resultsresults'将并行策略应用于包含列表,mapM_ 1}}最终通过打印它们来请求实际值 - 但是要打印的内容已经并行引发,因此程序应该并行化。

在确实使用了我所有的CPU之后感到高兴,我发现当使用+RTS -N8运行时,程序效率较低(如挂钟时间),而不是没有任何RTS标志。我能想到的唯一解释是第一个mapM必须排序 - 即执行 - 已经完成所有IO操作,但这不会导致无效,但要使N8执行与不平行的,因为所有工作都是由主线程完成的。使用+RTS -N8 -s运行程序会产生SPARKS: 36 (11 converted, 0 overflowed, 0 dud, 21 GC'd, 4 fizzled),这肯定不是最佳的,但遗憾的是我无法理解它。

我想我已经在Haskell并行化或IO monad的内部找到了初学者的垫脚石之一。我做错了什么?

背景信息:f n是一个返回Project Euler问题n的解决方案的函数。由于其中许多都有要读取的数据,因此我将结果放入IO monad中。它可能是什么样子的一个例子是

-- Problem 13: Work out the first ten digits of the sum of one-hundred 50-digit numbers.

euler 13 = fmap (first10 . sum) numbers
      where
            numbers = fmap (map read . explode '\n') $ readFile "problem_13"
            first10 n
                  | n < 10^10 = n -- 10^10 is the first number with 11 digits
                  | otherwise  = first10 $ n `div` 10

可以找到完整的文件here(它有点长,但前几个“euler X”函数应该具有足够的代表性),我执行并行性的主文件是this one

2 个答案:

答案 0 :(得分:7)

策略是并行执行纯计算。如果您的f确实必须返回IO值,请考虑使用async包。它为同时运行IO个动作提供了有用的组合器。

对于您的用例,mapConcurrently看起来很有用:

import Control.Concurrent.Async

vals = [1..10]
main = do
  results <- mapConcurrently f vals
  mapM_ print results

(我没有测试过,因为我不知道你的f到底是什么。)

答案 1 :(得分:2)

试用parallel-io套餐。它允许您将任何mapM_更改为parallel_