寻找优化程序的方法[Scala / Spark]

时间:2018-03-18 19:31:45

标签: scala apache-spark math

我有一个RDD,看起来如下:

  

((tag_1,set_1),(tag_2,set_2)),...,((tag_M,set_M),(tag_L,set_L)),...

对于来自RDD的每一对,我将计算表达式

expression

对于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倍以上的数据,但我甚至无法在较小的数据上进行操作。一。

提前谢谢!

2 个答案:

答案 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    |
+---------------------------------------+
做这样的事情可能会帮助你达到理想的表现。

希望这对你有所帮助,如果有效,请告诉我!