【发布时间】:2017-01-06 14:59:09
【问题描述】:
我的确切示例是并行化这种树聚合,其中信息从叶子流向根:
aggregate :: ([a] -> a) -> Tree a -> Tree a
aggregate _ (Node x []) = Node x []
aggregate aggregator (Node _ children) =
let agChildren = map (aggregate aggregator) children in
Node (aggregator $ map (\(Node y _) -> y) agChildren) agChildren
我希望聚合器函数的每个应用程序都在不同的线程上进行处理。所以我想改变上面的代码,让它生成一个依赖任务树并将它们提供给线程池。
我不希望节点上的线程受到影响,等待子线程完成。相反,这个等待线程应该去计算树中其他可用的子节点。同时为每个节点运行一个线程也太慢了。我的树可以有数百个节点,而我的机器只有 8 个核心:他们会花时间调度而不是计算。我需要一个仅在其他任务完成时才使用任务的线程池。
正如 ErikR 下面提到的,parMap 似乎正是这样做的。我尝试了它并用strat 64 +RTS -N2 执行它,以获得完全相同的计算时间。这是代码(为了测试性能而进行愚蠢的计算),你明白为什么时间没有改变吗?
slowAggregate :: [Int] -> Int
slowAggregate l = let s = sum l in
sum [a + b + c | a <- [0..s], b <- [0..s], c <- [0..s] ]
bigTree :: Tree Int
bigTree = Node 0 $ map (\x -> Node x []) [71..78]
aggregate :: NFData a => ([a] -> a) -> Tree a -> Tree a
aggregate _ (Node x []) = Node x []
aggregate aggregator (Node _ children) =
let agChildren = parMap rdeepseq (aggregate aggregator) children in
Node (aggregator $ map (\(Node y _) -> y) agChildren) agChildren
main = timeIt $ let (Node y _) = aggregate slowAggregate bigTree in print y
【问题讨论】:
-
顺便说一句 - "Parallel and Concurrent Programming in Haskell" 这本书是关于这个主题的优秀书籍。
-
我已经读过这一章了:chimera.labs.oreilly.com/books/1230000000929/ch03.html。我们没有在任何地方指定策略的线程数,所以我想他们会去一个线程池。但我不确定。
标签: haskell parallel-processing