11

我有一个返回 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'

我对此的理由是,第一个mapM将类型绑定IO [Int]resultsresults'对包含的列表应用并行策略,mapM_最后通过打印它们来请求实际值 - 但是要打印的内容已经并行触发,所以程序应该并行化。

+RTS -N8在很高兴它确实使用了我所有的 CPU 之后,我注意到该程序在运行时效率低于没有任何 RTS 标志时(如挂钟时间) 。我能想到的唯一解释是第一个mapM必须对所有 IO 操作进行排序 - 即执行 - 已经完成,但这不会导致无效,而是使N8执行与未并行操作一样有效,因为所有工作都是由主线程。+RTS -N8 -s使用yield运行程序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

完整的文件可以在这里找到(有点长,但前几个“euler X”函数应该足够有代表性),我做并行的主文件就是这个

4

2 回答 2

7

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

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

import Control.Concurrent.Async

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

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

于 2012-10-28T17:58:50.573 回答
3

试试这个parallel-io包。它允许您将任何更改mapM_parallel_.

于 2012-10-29T17:53:35.533 回答