【问题标题】:Sequencing IO actions in parallel并行排序 IO 操作
【发布时间】:2012-10-28 15:04:01
【问题描述】:

我有一个返回 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_ 最后通过打印请求实际值它们 - 但要打印的内容已经并行触发,因此程序应该并行化。

在对它确实使用了我所有的 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

【问题讨论】:

  • 如果不看更多就很难诊断。如果您使用+RTS -s -N 运行它,转换/修剪/失败火花的统计数据是什么? f n 是否返回实际上可以触发的 thunk?
  • @DanielFischer 我有点犹豫是否发布完整文件,因为它很长(最小示例等)。我认为我的错误出在并行代码中,所以我在我的问题中专注于这一点。我现在添加了一个新段落,还有 -s 统计数据(太糟糕了)。
  • 我不确定那些真正做 I/O 的人是否会破坏它,但对于纯粹的人(不使用Data.Permute,因为我没有安装它),我得到了一个使用parListChunk k 而不是parList 加速(以及更多转换的火花) - 即使使用parListChunk 1,尽管这会调用parList
  • @DanielFischer 是的,我也注意到分块并行性更快。无论如何,现在我不仅对如何使我的程序运行得更快感兴趣,而且对它为什么还不能正常工作感兴趣:-)

标签: haskell io parallel-processing


【解决方案1】:

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

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

import Control.Concurrent.Async

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

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

【讨论】:

  • 如果您想要对其进行测试,我在帖子末尾添加了完整的脚本。如果不是:Async 会受到同样的性能影响,但它是一个看起来很有前途的包,到目前为止我一直忽略它,非常感谢!
【解决方案2】:

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

【讨论】:

    猜你喜欢
    • 2016-06-11
    • 1970-01-01
    • 2011-12-21
    • 2018-07-03
    • 1970-01-01
    • 2011-05-13
    • 2017-07-16
    • 1970-01-01
    相关资源
    最近更新 更多