为什么我的 Haskell 代码没有出现在并行 运行 中

Why does my Haskell code not appear to run in Parallel

我正在尝试解决斯坦福大学 coursera 在线课程的 2 和算法问题。我需要在列表中找到所有不同的对 x+y,总和为 t 范围内的值 [-10000 .. 10000]。我知道有更有效的实现,但我认为现在是尝试进行一些 Haskell 并行编程的好时机。

我试图通过在两个不同的线程(我认为称为火花)中循环一半的范围来实现并行化。我的代码如下:

module Main where

import Data.List
import qualified Data.Map as M
import Debug.Trace
import Control.Parallel (par,pseq)

main :: IO ()
main = interact run

range :: [Int]
range = [negate 10000..10000]

emptyMap :: M.Map Int Bool
emptyMap = M.fromList $ zip [] []

run :: String -> String
run xs = let parsedInput = map (read :: String -> Int) $ words xs
             hashMap = M.fromList $ zip parsedInput (repeat True)
             pcalc r = map (\t -> trace (show t) (countVals hashMap parsedInput t)) r
             bot = pcalc (take (div (length range) 2) range)
             top = pcalc (drop (div (length range) 2) range)
             out = top `par` bot `pseq` (sum bot + sum top)
          in show out

countVals :: M.Map Int Bool -> [Int] -> Int -> Int
countVals m ks t = foldl' go 0 ks
  where go acum x = if M.lookup y m == Just True
                            && y /= x
                            then 1
                            else acum
                              where y = t - x

你可以看到我有两个变量 topbot 我试图通过

并行计算它们
out = top `par` bot `pseq` (sum bot + sum top)

我认为 other stack overflow 答案是推荐的。但是,当我编译 运行 时,我似乎只看到 bot 变量的踪迹。

% stack ghc --package parallel -- -threaded Main.hs                                           
[1 of 1] Compiling Main             ( Main.hs, Main.o )
Linking Main ...
% ./Main +RTS -N8 < input.txt                                                                   
-10000
-9999
-9998
-9997
-9996
...

而我期待的是:

% ./Main +RTS -N8 < input.txt                                                                    
-10000
0
-9999
1
-9998
2
-9997
-9996
...

有人可以帮忙指出我到底做错了什么吗?谢谢

让我们重点关注这部分:

bot = pcalc (take (div (length range) 2) range)
top = pcalc (drop (div (length range) 2) range)
out = top `par` bot `pseq` (sum bot + sum top)

这里,bottop是列表。当我们 seqpseqpar 一个值时,我们会对其求值;由于 Haskell 是惰性的,当达到“弱头部范式”时评估停止,即直到第一个构造函数出现在结果中。对于列表值,这意味着它们被缩减为 []unevaluatedHead : unevaluatedTail.

因此,top `par` bot `pseq` ... 仅并行计算列表的第一个单元格,而不是其全部内容。当我们对它们求和时,整个列表只会在 pseq 之后进行评估,但那是 运行 只有一个核心。

要强制代码并行,我们可以并行化求和:

sumBot = sum bot
sumTop = sum top
out = sumBot `par` sumTop `pseq` sumBot + sumTop

由于计算 WHNF 的总和需要计算整个列表,这应该适当地并行计算。