【问题标题】:why is the map function inherently parallel?为什么 map 函数本质上是并行的?
【发布时间】:2017-11-18 17:43:27
【问题描述】:

我正在阅读以下演示文稿:

http://www.idt.mdh.se/kurser/DVA201/slides/parallel-4up.pdf

并且作者声称 map 函数非常适合并行性(特别是他支持他在第 3 页或幻灯片 9 和 10 中的主张)。

如果给一个列表的每个值增加 +1 的问题,我可以看到循环遍历列表如何强制要求更改索引值,从而导致潜在的竞争条件问题。但我很好奇 map 函数如何更好地让程序员成功地并行编码。

是因为map是递归定义的吗?那么每个函数调用都可以抛出到不同的线程吗?

希望有人能提供一些细节,谢谢!

【问题讨论】:

  • 因为函数f对输入列表元素的每个应用程序都是独立从任何其他应用程序到任何其他元素的,所以它们都可以独立于彼此,即并行。假设的par_map 将分配存储来支持结果列表,并为列表中的每个元素e 触发一个新线程的执行,为它提供对需要使用@ 结果更新的位置的引用987654325@。当没有更多活动线程时,map 已完成。当然,您也可以让每个线程在 1000 es 的块上工作。

标签: scala parallel-processing functional-programming map-function


【解决方案1】:

map 函数将相同的纯函数应用于集合中的n 元素并聚合结果。将函数应用于集合成员的顺序无关紧要,因为根据定义,函数的返回值完全取决于输入。

【讨论】:

  • 是的,但是我们不能把列表分成不同的块,并在自己的线程上用循环命令式地处理每个块吗?这与上述有何不同?
  • 在功能上可能没什么...我认为这只是复杂性的问题。您描述的算法会更复杂......而您可以利用库来执行并行映射。至少我是这样解释他的观点的。
  • 我想我对地图的内部结构更感兴趣。如果 map 是递归定义的,那么我们将有: map(f(x), list) = if list is empty return the empty list else return f(head_of_list) concat with map(f(x),list.tail) ... . 如果 map 是递归定义的,那如何让 map 避免依次计算 f(head_of_list) 一个一个? (就像循环一样)
  • @H_1317 关键是,如果 API 只提供用于在列表上强制循环的工具,程序员将不得不自己实现并行性。虽然 map 的 API 允许程序员只提供他想要应用于每个元素的函数,然后 map 可以在内部做任何事情,并代表程序员处理并行性。
  • @Jasper-M 即使 API 没有定义 map,我能不能只定义我自己的内部使用循环的名为 map 的函数?这个特定的递归映射函数(如果有的话)对循环版本有什么好处?
【解决方案2】:

其他人已经解释了标准的map 实现不是并行的。

但在 Scala 中,既然你标记了它,你就可以像得到并行版本一样简单

val list = ... // some list
list.par.map(x => ...) // instead of list.map(x => ...)

另请参阅Parallel Collections OverviewParIterable 以及scala.collection.parallel 包中的其他类型的文档。

如果需要,您可以在https://github.com/scala/scala/blob/v2.12.1/src/library/scala/collection/parallel/ParIterableLike.scala 中找到并行map 的实现(查找def mapclass Map)。它需要非常重要的基础设施,当然不仅仅是采用顺序map 的递归定义并将其并行化。

如果一个人通过循环定义了地图,那将如何分解?

幻灯片最后以 F# 并行数组为例,在 https://github.com/fsharp/fsharp/blob/master/src/fsharp/FSharp.Core/array.fs#L266 你可以看到非并行实现有一个循环:

let inline map (mapping: 'T -> 'U) (array:'T[]) = 
     checkNonNull "array" array             
     let res : 'U[] = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked array.Length 
     for i = 0 to res.Length-1 do  
         res.[i] <- mapping array.[i] 
     res 

【讨论】:

  • 谢谢阿列克谢。你会称 map 的标准递归定义虽然仍然是顺序的,但对于创建易于使用的并行性至关重要?如果一个人通过循环定义了地图,那将如何分解? (我怀疑这些问题的答案可能比一条评论大,但任何高层次的概述都会很棒!)
  • 不,它不是必需的或特别相关的。幻灯片根本没有讨论递归实现,只讨论了 map 的规范可能并行实现。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-05-02
  • 1970-01-01
  • 2015-02-10
  • 2021-07-25
  • 2019-12-09
  • 2018-05-27
  • 1970-01-01
相关资源
最近更新 更多