【问题标题】:How does the fold action work in Spark?折叠动作在 Spark 中是如何工作的?
【发布时间】:2018-01-20 16:27:25
【问题描述】:

下面我有一个 Spark fold 操作的 Scala 示例:

val rdd1 = sc.parallelize(List(1,2,3,4,5), 3)
rdd1.fold(5)(_ + _)

这会产生输出35。有人可以详细解释一下这个输出是如何计算的吗?

【问题讨论】:

  • 你知道.... 文档明确将其指定为 zeroValue 而不是 initialValue。而且5 看起来不像zero

标签: scala apache-spark fold


【解决方案1】:

取自 Scaladocs here(重点是我的):

@param zeroValue 每个累加结果的初始值 op 运算符的分区,以及 结合不同的结果 op 运算符的分区 - 这通常是中性的 元素(例如Nil 用于列表连接或0 用于求和)

在您的情况下,zeroValue 被添加了四次(每个分区一个,在组合分区结果时加一个)。所以结果是:

(5 + 1) + (5 + 2 + 3) + (5 + 4 + 5) + 5 // (extra one for combining results)

【讨论】:

  • 我知道了(用于合并结果的额外一个,感谢您的回答。但是折叠动作的意义是什么,我的意思是在哪些情况下,我应该选择折叠而不是减少
  • 好吧,基本上 fold 让您可以选择提供 initialValue 而 reduce 不提供。
【解决方案2】:

zeroValue 为每个分区添加一次,并且应该是一个中性元素 - 如果是 +,它应该是 0。确切的结果将取决于分区的数量,但它相当于:

rdd1.mapPartitions(iter => Iterator(iter.foldLeft(zeroValue)(_ + _))).reduce(_ + _)

所以:

val rdd1 = sc.parallelize(List(1,2,3,4,5),3)

将数据分配为:

scala> rdd1.glom.collect
res1: Array[Array[Int]] = Array(Array(1), Array(2, 3), Array(4, 5))

整个表达式相当于:

(5 + 1) + (5 + 2 + 3) + (5 + 4 + 5)

plus 5 for jobResult.

【讨论】:

  • 但是 (5 + 1) + (5 + 2 + 3) + (5 + 4 + 5) 等于 30 而不是 35
【解决方案3】:

您知道 Spark RDD 执行分布式计算。

所以,这里的这一行,

val rdd1 = sc.parallelize(List(1,2,3,4,5), 3)

告诉 Spark 它需要在这个 RDD 中支持 3 个分区,这将使它能够使用 3 个独立的执行器并行运行计算。

现在,这条线在这里,

rdd1.fold(5)(_ + _)

告诉 spark 以 5 作为初始值折叠所有这些分区,然后以 5 作为初始值再次折叠来自 3 个执行器的所有这些分区结果。

一个普通的 Scala 等价物可以写成,

val list = List(1, 2, 3, 4, 5)
val listOfList = list.grouped(2).toList
val listOfFolds = listOfList.map(l => l.fold(5)(_ + _))
val fold = listOfFolds.fold(5)(_ + _)

所以...如果您在 RDD 上使用 fold,您需要提供 zero value

但是你会问 - 为什么或何时有人会使用 fold 而不是 reduce

您的困惑在于您对zero value 的看法。问题是,RDD[T] 的 zero value 并不完全取决于我们的类型 T,还取决于计算的性质。所以你的zero value 不需要是0

让我们考虑一个简单的例子,我们要在 RDD 中计算 "largest number greater than 15" or "15"

我们可以使用reduce 做到这一点吗?答案是不。但是我们可以使用fold 来做到这一点。

val n15GT15 = rdd1.fold(15)({ case (acc, i) => Math.max(acc, i) })

【讨论】:

  • 如果我正确理解了您的示例,忽略 reduce 并不完全正确:list.reduce{ (acc, x) => val m = math.max(acc, x); if (m > 15) m else 15 }
  • 是的。但是,你正在做额外的if检查你的combine函数,如果你使用fold,这本可以避免。
  • 同意fold 在您的示例中是一个更好的选择。我的观点是,它可以通过reduce 来完成。
  • 并非总是如此,它只是在这个例子中解决了。在某些情况下它可能是可行的,但成本会非常高,在某些情况下是不可能的。每个reduce 都可以用fold 完成,但不能反过来。
  • 也许我不清楚。我只是想指出,您的示例不是证明您的 Can we do that with reduce? NO 声明的好案例。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-27
  • 1970-01-01
  • 1970-01-01
  • 2017-02-14
相关资源
最近更新 更多