为什么Haskell列表理解不能并行执行?

为什么Haskell列表理解不能并行执行?,haskell,parallel-processing,list-comprehension,Haskell,Parallel Processing,List Comprehension,我正在做欧拉21题的作业,我有以下理解: amicableNumberSums = [ x+y | x<-[1..10000], y <-[1..10000], (amicable x y)] (编辑)两种建议方法的性能: Ørjan Johansen's method: 218.79s elapsed parallel (4 cores + 4 hyperthreading) 279.37s elapsed sequential

我正在做欧拉21题的作业,我有以下理解:

amicableNumberSums = [ x+y | x<-[1..10000], y <-[1..10000], (amicable x y)]
(编辑)两种建议方法的性能:

Ørjan Johansen's method: 218.79s elapsed parallel (4 cores + 4 hyperthreading)
                         279.37s elapsed sequential (single core)
bheklilr's method: 247.82s elapsed parallel (4 cores + 4 hyperthreading)
                   274.10s elapsed sequential (single core)
Original method: 278.69s elapsed

这并不像我希望的那么快,但我现在有了问题的正确答案,在我学到更多Haskell之前,这就足够了。

这里有一个简单的例子:

simple = 1 : 1 : [a + b | a <- simple, b <- simple]
或者并行计算列表的块:

print $ amicableNumberSums `using` parListChunk 64 rdeepseq
请记住,您必须使用
seq
和co.在正确的时间将数据输入NF


Control.Parallel.Strategies
公开的API使您能够定义与数据结构本身甚至其他算法完全分离的纯数据结构并行计算的不同方式。这与大多数其他编程语言形成了鲜明的对比,它们迫使您将并行性与其他算法,甚至是结构的构造方式紧密耦合。我强烈推荐阅读Simon Marlow的(在线免费!),这本书在解释它的工作原理和使用方法方面比我做得好得多。

@bheklillr的回答处理了并行化策略的一般方法,但正如我在上面的评论中所暗示的,原始列表理解的编写方式迫使所有的
友好的
测试在基于它的
parList
策略能够到达它的元素并开始评估它们之前进行,因此我认为@bheklillr的代码在这个特定情况下不太管用

这是我的改进建议。您需要重写列表理解,以便将工作划分为定义良好的独立块,例如,通过组合中间列表中的
x
值。例如,它可以等效地写成

concat [[ x+y | y <-[1..10000], (amicable x y)] | x <- [1..10000]]

(我在这里使用
rdeepseq
,因为预压缩列表中的元素也是列表,我们希望计算它们的所有元素,这些元素都是整数。)

没有副作用,但您对列表的理解要求结果按特定顺序排列,因此,并行计算并不像你想象的那么简单。“使用parList打印$amicableNumberSums
是否应该在没有任何其他更改的情况下工作?”?当我尝试它时,我得到了问题编辑中看到的错误。
parList
不是一个
Strategy
,它是一个从单个元素的策略生成列表策略的函数。您需要使用
parList rseq
或类似工具。也就是说,我认为
parList…
不会像这样直接与
amicleNumberSums
一起工作。这是因为它要求在开始计算列表中的元素之前先计算列表的脊线,并且列表理解的定义是这样的,即一个元素在被检查为友好总和之前不会被放入列表中。@Scytheon3对此表示抱歉,我已经有一段时间没有使用
parList
,完全忘记了我需要
rdeepseq
或其他一些策略来实现它。奥扬·约翰森指出了我的错误,这是正确的(值得投几票)。
print $ amicableNumberSums `using` parListChunk 64 rdeepseq
concat [[ x+y | y <-[1..10000], (amicable x y)] | x <- [1..10000]]
concat ([[ x+y | y <-[1..10000], (amicable x y)] | x <- [1..10000]]
        `using` parList rdeepseq)