并行顺序执行IO操作

12

我有一个返回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_最后通过打印它们来请求实际值 - 但要打印的内容已经在并行中启动了,所以程序应该并行化。

在确认它确实使用了我所有的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单子内部的初学者之路的一个基础问题。我做错了什么?

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

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

完整的文件可以在这里找到(它有点长,但前几个“欧拉 X”函数应该足够代表性),我进行并行处理的主要文件是这个


没有更多的信息很难诊断。如果您使用 +RTS -s -N 运行它,转换/修剪/失败的火花的统计数据是什么?而且 f n 是否返回一个实际可以被点燃的惰性求值? - Daniel Fischer
@DanielFischer 我有点犹豫要发布完整的文件,因为它非常长(包括最小化的示例等)。我认为我的错误在并行代码中,所以我在问题中重点关注了这一点。现在我已经添加了一个新段落,还有 -s 统计数据(非常糟糕)。 - David
我不确定实际执行 I/O 操作的人是否会破坏它,但对于纯操作(不使用 Data.Permute,因为我没有安装它),我使用 parListChunk k 而不是 parList 取得了加速效果(以及更多转换后的火花) - 即使使用 parListChunk 1,尽管这会调用 parList - Daniel Fischer
@DanielFischer 是的,我也注意到了分块并行比较快。不管怎样,现在我不仅对如何使程序运行更快感兴趣,也对为什么它已经无法工作感兴趣 :-) - David
2个回答

8

本文内容涉及IT技术,需要翻译为中文。本文介绍了用于纯计算的并行执行策略。如果您的 f 必须返回一个 IO 值,则可以考虑使用 async 包。它提供了有用的组合器,用于并发运行 IO 操作。

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

import Control.Concurrent.Async

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

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

如果你想测试它,我在帖子末尾添加了完整的脚本。如果不需要测试:异步操作也会受到同样的性能损耗,但这是一个非常有前途的包,我之前忽视了它,所以非常感谢! - David

4

可以尝试使用parallel-io包。它可以让你将任何mapM_变成parallel_


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接