我有一个返回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]
的内容绑定到results
,results'
对包含的列表应用了并行策略,而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
+RTS -s -N
运行它,转换/修剪/失败的火花的统计数据是什么?而且f n
是否返回一个实际可以被点燃的惰性求值? - Daniel Fischer-s
统计数据(非常糟糕)。 - DavidData.Permute
,因为我没有安装它),我使用parListChunk k
而不是parList
取得了加速效果(以及更多转换后的火花) - 即使使用parListChunk 1
,尽管这会调用parList
。 - Daniel Fischer