【问题标题】:Parallel maximum computation并行最大计算
【发布时间】:2015-08-04 03:50:16
【问题描述】:

我有一组问题想要并行评估。这些问题使用与此非常相似的简单表达式类型来表达:

-- Expressions are either a constant value or two expressions
-- combined using a certain operation
data Expr 
    = Const NumType
    | Binary BinOp Expr Expr

-- The possible operations
data BinOp = Add | Sub | Mul | Div
    deriving (Eq)

这些表达式是动态构建的,应该计算出可能有效或无效的特定结果。这表示为遇到无效结果时停止计算的 monad。

data Result a
    = Val { val :: a }
    | Exc { exc :: String }

instance Monad Result where
    return = Val
    (Exc e) >>= _ = (Exc e)
    (Val v) >>= g = g v

要确定每个已解决问题的值,我有两个相关功能:

eval :: Expr -> Result NumType
score :: Expr -> NumType

最后,我解决了将返回[Expr] 的函数。这导致我的主要功能目前看起来像这样:

main :: IO ()
main = do
    strAvailableNumbers <- getLine
    strTargetNumber <- getLine
    let numbers = parseList strAvailableNumbers 
        target = parseTargetNumber strTargetNumber in
            sequence $ map (print) $ 
                solveHeuristic1 (Problem target numbers) [Add] [Sub] ++
                solveHeuristic2 (Problem target numbers) 

    return ()

基本思想是我从标准输入读取一个数字列表和一个目标数字,然后在标准输出上打印表达式。

但我有两个问题想要解决,但我不太确定它们之间的相关性:

  • 这些启发式算法完全不知道彼此,因此不知道他们的解决方案的score 是否高于其他任何方法。我想为 map 函数引入某种状态,只打印新的Expr,如果它的分数高于之前打印的Expr

  • 我想并行执行这些计算,并尝试使用(parMap rseq) 而不是map,使用-threaded 选项编译并使用+RTS -N2 运行它。结果是运行时间从 5 秒增加到 7 秒。不是我所期望的,尽管time 显示 CPU 利用率更高。我想我没有正确使用parMap 或使用++ 做错了什么。那么我将如何并行运行一个独立函数列表,每个函数返回一个元素列表?

更新:创建了gist with complete source code

【问题讨论】:

  • 使用rdeepseq 代替rseq 有什么不同吗?
  • 这给了我No instance for (NFData (IO ())) arising from a use of rdeepseq'`。
  • 您介意发布所有代码吗(也许在链接中?)我有一个想法,但我想先测试一下
  • @MarcusRiemer 嗯,你在哪里使用rseq?我在问题中给出的代码中没有看到它。如果您想并行运行IO,也许您需要async 包。
  • @jozefg:我上传了一个相当粗略的版本到gist.github.com/anonymous/f85737334dac2c710f00

标签: haskell parallel-processing


【解决方案1】:

这里的问题是使用seq 评估IO 操作几乎没有任何作用。因此,您只是按顺序运行,开销稍大。

你可以折射事物,让它们再次纯净

main :: IO ()
main = do
    mapM_ (`seq` print "found it") -- make sure we're not 
                                   -- benchmarking printing stuff
          . concat
          . parMap rdeepseq (solve [1..10000000])
          $ [42, 42]

    return ()

并添加NFData 的实例以使用rdeepseq,这将全面评估事物

instance NFData BinOp -- Binop is just an enum, WHNF = NF

instance NFData Expr where
  rnf (Const a) = a `deepseq` ()
  rnf (Binary b e1 e2) = b `deepseq` e1 `deepseq` e2 `deepseq` ()

现在如果我们运行它,我们会得到...一个stackoverflow。我充分增加了我们搜索的大小,以便实际花费足够长的时间来进行基准测试,现在将两个结构完全加载到内存中会破坏堆栈。将堆栈大小提高到我们不会炸毁所有内容的程度,使用-N2 比不使用时快 40%(3 秒对 5 秒)。我会考虑预期的结果。运行此程序时,我可以直观地看到 2 个内核短暂跃升至 100%。

最终编译顺序

> ghc -O2 -threaded -rtsops bench.hs
> ./bench +RTS -K10000000 -N2

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多