我有一个RDD,看起来如下:
((tag_1,set_1),(tag_2,set_2)),...,((tag_M,set_M),(tag_L,set_L)),...
对于来自RDD的每一对,我将计算表达式
对于k = 0,..,3并找到总和:p(0)+ ... p(3)。对于每对对,n_1是第一对中的集合的长度,n_2是第二对中的集合的长度。
现在我写了以下内容:
val N = 1000
pairRDD.map({
case ((t1,l1), (t2,l2)) => (t1,t2, {
val n_1 = l1.size
val n_2 = l2.size
val vals = (0 to 3).map(k => {
val P1 = (0 to (n_2-k-1))
.map(j => 1 - n_1/(N-j.toDouble))
.foldLeft(1.0)(_*_)
val P2 = (0 to (k-1))
.map(j => (n_1-j.toDouble)*(n_2-j.toDouble)/(N-n_2+k.toDouble-j.toDouble)/(k.toDouble-j.toDouble) )
.foldLeft(1.0)(_*_)
P1*P2
})
vals.sum.toDouble
})
})
问题是它似乎工作得很慢,我希望scala / spark有一些我不知道的功能,这可以减少执行时间。
编辑:
1)首先,我有一个包含2列的csv文件:tag和message_id。对于每个标签,我都可以找到可以找到它的消息并创建如上所述的对(tagIdsZipped)。代码为here
2)然后我想计算每对的表达式并将其写入文件。 实际上,我也想过滤结果,但它会更长,所以我甚至都没有尝试过。
3)不,实际上我没有,但问题发生了,当我尝试使用此代码时,之前我做了以下事情:
val tagPairsWithMeasure: RDD[(Tag, Tag, Measure)] = tagIdsZipped.map({
case ((t1,l1), (t2,l2)) => (t1,t2, {
val numer = l1.intersect(l2).size
val denom = Math.sqrt(l1.size)*Math.sqrt(l2.size)
numer.toDouble / denom
})
})
一切正常。 (见4))
4)在我1)中描述的文件中,有大约2500万行(~1.2 GB)。我在Xeon E5-2673 @ 2.4GHz和32 GB RAM上进行计算。用我在3)中描述的函数执行代码花了大约1.5小时。我知道,现在有更多的操作,但是花了大约3个小时,只完成了大约25%的任务。主要问题是我将需要使用大约3倍以上的数据,但我甚至无法在较小的数据上进行操作。一。
提前谢谢!
答案 0 :(得分:0)
正如已经提到的,Spark没有太多改进。
我在这里看到的最大问题是使用range.map
。
(0 to (n_2-k-1))
会创建一个Range
对象。
在其上调用map
会创建Vector
分配大量内存。
最简单的解决方案是使用迭代器,因为foldLeft
是一个流友好的函数:
(0 to (n_2-k-1)).iterator
代替(0 to (n_2-k-1))
使用var
s,循环和数组尝试重写它也是有意义的,因为循环内的计算非常便宜。但它是最后一次机会的武器。
答案 1 :(得分:0)
tagIdsDF
+-----------------------------+
|tag1 | set1 |tag2 | set2 |
+-----------------------------+
|tag_1 |set_1 |tag_2 |set_2 |
|... |
|tag_M |set_M |tag_L |set_L |
+-----------------------------+
并定义一个UDF来计算总和:
val pFun = udf((l1:Seq[Double], l2:Seq[Double]) => {
val n_1 = l1.size
val n_2 = l2.size
val vals = (0 to 3).map(k => {
val P1 = (0 to (n_2-k-1))
.map(j => 1 - n_1/(N-j.toDouble))
.foldLeft(1.0)(_*_)
val P2 = (0 to (k-1))
.map(j => (n_1-j.toDouble)*(n_2-j.toDouble)/(N-n_2+k.toDouble-j.toDouble)/(k.toDouble-j.toDouble) )
.foldLeft(1.0)(_*_)
P1*P2
})
vals.sum.toDouble
})
请注意,您不需要传递tag_1 / tag_2,因为此信息位于结果数据框中,然后您可以这样调用它:
val tagWithMeasureDF = tagIdsDF.withColumn("measure", pFun($"set1", $"set2"))
你得到这个df:
tagWithMeasureDF
+-----------------------------+---------+
|tag1 | set1 |tag2 | set2 | measure |
+---------------------------------------+
|tag_1 |set_1 |tag_2 |set_2 | m1 |
|... ... ...|
|tag_M |set_M |tag_L |set_L | mn |
+---------------------------------------+
做这样的事情可能会帮助你达到理想的表现。
希望这对你有所帮助,如果有效,请告诉我!