我目前正在编写一个程序,我决定是使用groupByKey,然后是连接还是只是一个连接。
基本上我有一个RDD,每个键有很多值,另一个RDD每个键只有一个值,但是这个值非常大。我的问题是当我将这些值加在一起时,会产生很多大值的副本(一个用于每个较小值的单个实例)或者只能保留一个大值的副本,同时提供对所有值的引用原始价值观。
基本上我有这样的情况:
val InvIndexes:RDD[(Int,InvertedIndex)] //InvertedIndex is very large
val partitionedVectors:RDD[(Int, Vector)]
val partitionedTasks:RDD[(Int, (Iterator[Vector], InvertedIndex))] = partitionedvectors.groupByKey().join(invIndexes)
val similarities = partitionedTasks.map(//calculate similarities)
我的问题是,在执行此操作之前,代码之间是否存在任何重大的空间复杂性差异:
val InvIndexes:RDD[(Int,InvertedIndex)]
val partitionedVectors:RDD[(Int, Vector)]
val partitionedTasks:RDD[(Int, (Vector, InvertedIndex))] = partitionedvectors.join(invIndexes)
val similarities = partitionedTasks.map(//calculate similarities)
答案 0 :(得分:1)
从技术上讲,应该没有复制。 join
基本上是cogroup
,后跟flatMapValues
,具有嵌套的for
理解。假设单个共同分组元素如下所示:
val pair = (1, (
Seq(Vectors.dense(Array(1.0)), Vectors.dense(Array(2.0))),
Seq(Vectors.dense(Array(3.0)), Vectors.dense(Array(4.0)))
))
后续操作相当于:
val result = pair match {
case (k, (xs, ys)) => xs.flatMap(x => ys.map(y => (k, (x, y))))
}
并且不会复制预期的矢量:
require(result(0)._2._1 eq result(1)._2._1)
只要数据在内存中处理或整个分区被序列化/反序列化(例如在collect
中),就应保留此状态,但我个人不会依赖于此。即使您忽略了低级别的实现细节,简单的shuffle也可能需要完整的副本。