我为seqOp
提供了2个aggregate
函数,我希望它们返回相同的结果。他们没有。
此版本有效:
rdd.aggregate(0)((acc, article) => (acc + (if (article.mentionsLanguage(lang)) 1 else 0)), _ + _)
此版本不起作用:
def seqOp(acc: Int, article: WikipediaArticle): Int = {
acc + (if (article.mentionsLanguage(lang)) 1 else 0)
}
rdd.aggregate(0)(seqOp, _ + _)
由于某种原因,后一个版本被卡住了,不占用任何CPU,并且没有错误。对于我的一生,我看不到这些功能有何不同。我实际上是对lambda语法有误解吗?
答案 0 :(得分:2)
我猜想seqOp
不是嵌套函数,而是实际上是绑定到一些庞大对象的方法。可能是您实际上是在尝试向工作节点发送(acc, article) => this.seqOp(acc, article)
,其中this
是一些重对象,与驻留在主JVM上的甚至更重的对象图相关。这迫使您的主节点尝试序列化与定义该方法的对象相关的所有事物,并且从外部看,似乎计算甚至无法正确启动,因为主节点从未设法发送整个对象图到工作节点。
使用匿名函数语法时,它会变为类似以下内容:
rdd.aggregate(0)(
new Function2[Int, WikipediaArticle, Int] {
def apply(acc: Int, article: WikipediaArticle) =
(acc + (if (article.mentionsLanguage(lang)) 1 else 0))
},
_ + _
)
在这里,您可以立即看到从Function2
扩展过来的匿名本地类的实例没有任何其他对象的引用。确实,它甚至没有任何成员变量,因此实际上没有要序列化的东西(您需要知道的是该东西的类;它不携带任何额外的信息)。
但是当您在某些seqOp
上定义方法VeryLargeObject
class VeryLargeObject {
val referencesToMillionOtherObjects: Array[Any]
def seqOp(acc: Int, article: WikipediaArticle) = ...
}
,然后稍后尝试在您的seqOp
方法中使用aggregate
时,spark必须序列化VeryLargeObject
的实例及其所有传递依赖项,然后通过网络到工作节点。该过程可能不会在合理时间内终止,因此整个应用程序似乎被冻结了。
答案 1 :(得分:1)
RDD方法aggregate
期望将二进制运算符function
作为其seqOp
参数:
def aggregate[U](zeroValue: U)(seqOp: (U, T) => U, combOp: (U, U) => U): U
您在下面定义的是方法(不是函数):
def seqOp(acc: Int, article: WikipediaArticle): Int = {
acc + (if (article.mentionsLanguage(lang)) 1 else 0)
}
以下是将seqOp
定义为函数的方法:
val seqOp = (acc: Int, article: WikipediaArticle) => {
acc + (if (article.mentionsLanguage(lang)) 1 else 0)
}