优化Scala中的列表处理

时间:2014-01-06 02:19:07

标签: performance scala optimization scala-collections

现在处理来自Mixpanel API的大量Json数据。使用一个小数据集,它是轻而易举的,下面的代码运行得很好。但是,大型数据集需要相当长的时间来处理,因此我们开始看到超时。

我的Scala优化技能相当差,所以我希望有人可以用更快的方式来处理大型数据集的以下内容。请解释为什么,因为它有助于我自己理解Scala。

val people = parse[mp.data.Segmentation](o)
val list = people.data.values.map(b => 
  b._2.map(p => 
    Map(
      "id" -> p._1, 
      "activity" -> p._2.foldLeft(0)(_+_._2)
    )
  )
)
.flatten
.filter{ behavior => behavior("activity") != 0 }
.groupBy(o => o("id"))
.map{ case (k,v) => Map("id" -> k, "activity" -> v.map( o => o("activity").asInstanceOf[Int]).sum) }

那个Segmentation类:

case class Segmentation(
  val legend_size: Int,
  val data: Data
)

case class Data(
  val series: List[String],
  val values: Map[String, Map[String, Map[String, Int]]]
)

感谢您的帮助!

编辑:按要求提供样本数据

{"legend_size": 4, "data": {"series": ["2013-12-17", "2013-12-18", "2013-12-19", "2013-12-20", "2013-12-21", "2013-12-22", "2013-12-23", "2013-12-24", "2013-12-25", "2013-12-26", "2013-12-27", "2013-12-28", "2013-12-29", "2013-12-30", "2013-12-31", "2014-01-01", "2014-01-02", "2014-01-03", "2014-01-04", "2014-01-05", "2014-01-06"], "values": {"afef4ac12a21d5c4ef679c6507fe65cd": {"id:twitter.com:194436690": {"2013-12-20": 0, "2013-12-29": 0, "2013-12-28": 0, "2013-12-23": 0, "2013-12-22": 0, "2013-12-21": 1, "2013-12-25": 0, "2013-12-27": 0, "2013-12-26": 0, "2013-12-24": 0, "2013-12-31": 0, "2014-01-06": 0, "2014-01-04": 0, "2014-01-05": 0, "2014-01-02": 0, "2014-01-03": 0, "2014-01-01": 0, "2013-12-30": 0, "2013-12-17": 0, "2013-12-18": 0, "2013-12-19": 0}, "id:twitter.com:330103796": {"2013-12-20": 0, "2013-12-29": 0, "2013-12-28": 0, "2013-12-23": 0, "2013-12-22": 0, "2013-12-21": 0, "2013-12-25": 0, "2013-12-27": 0, "2013-12-26": 1, "2013-12-24": 0, "2013-12-31": 0, "2014-01-06": 0, "2014-01-04": 0, "2014-01-05": 0, "2014-01-02": 0, "2014-01-03": 0, "2014-01-01": 0, "2013-12-30": 0, "2013-12-17": 0, "2013-12-18": 0, "2013-12-19": 0}, "id:twitter.com:216664121": {"2013-12-20": 0, "2013-12-29": 0, "2013-12-28": 0, "2013-12-23": 1, "2013-12-22": 0, "2013-12-21": 0, "2013-12-25": 0, "2013-12-27": 0, "2013-12-26": 0, "2013-12-24": 0, "2013-12-31": 0, "2014-01-06": 0, "2014-01-04": 0, "2014-01-05": 0, "2014-01-02": 0, "2014-01-03": 0, "2014-01-01": 0, "2013-12-30": 0, "2013-12-17": 0, "2013-12-18": 0, "2013-12-19": 0}, "id:twitter.com:414117608": {"2013-12-20": 0, "2013-12-29": 0, "2013-12-28": 1, "2013-12-23": 0, "2013-12-22": 0, "2013-12-21": 0, "2013-12-25": 0, "2013-12-27": 0, "2013-12-26": 0, "2013-12-24": 0, "2013-12-31": 0, "2014-01-06": 0, "2014-01-04": 0, "2014-01-05": 0, "2014-01-02": 0, "2014-01-03": 0, "2014-01-01": 0, "2013-12-30": 0, "2013-12-17": 0, "2013-12-18": 0, "2013-12-19": 0}}}}}

要回答Millhouse的问题,目的是总结每个日期以提供描述每个ID的“活动”总量的数字。 “ID”的格式为id:twitter.com:923842

2 个答案:

答案 0 :(得分:1)

我不知道您的处理的完整程度,您正在进行的管道,服务器所承受的压力或您为接收信息而设置的线程配置文件的类型。但是,假设您已正确地将I / O与CPU绑定任务分开,并且您向我们展示的内容严格受CPU约束,请尝试将.par添加到第一个Map。

people.data.values.par.map(b =>

作为第一关,看看你是否可以获得一些性能提升。我没有看到处理所需的任何特定顺序,这告诉我并行化的成熟。

修改

在玩并行化之后,我想补充一点,修改TaskSupport对这种情况很有帮助。您可以修改并行化集合的tasksupport

import scala.collection.parallel._
val pc = mutable.ParArray(1, 2, 3)
pc.tasksupport = new ForkJoinTaskSupport(
  new scala.concurrent.forkjoin.ForkJoinPool(2))

请参阅http://www.scala-lang.org/api/2.10.3/index.html#scala.collection.parallel.TaskSupport

答案 1 :(得分:0)

我有一些可能会有所帮助的建议。

  1. 我会尝试在程序中尽早移动filter命令 可能。由于您的数据包含许多日期,您的活动为0 会看到改进这样做。最好的解决方案可能是 在解析json数据时对此进行测试。如果这是不可能的 把它作为第一个陈述。

  2. 我理解它的方式你最终会找到一种方法来查找总数 给定id的总和。我建议你用id的地图代表这个 总计。另外scala List类有一个sum函数。 我想出了这段代码:

      

    val originalList_IdToAggregate = people.data.values.map(p =>(p._2._1,   p._2._2.sum));

    它可能与您的项目不匹配,但我认为这几乎是您所需要的。 如果你需要制作一个这样的地图,你只需将地图附加到最后。

  3. 如果这没有给你足够的速度,你可以创建自己的聚合解析器 和解析只解析这种类型的json。 如果您使用解析器组合器,则在scala中编写解析器非常容易。 请记住尽可能早地扔掉你不需要的东西而不是扔掉 太多深层分支,这应该是一个内存占用少的快速解决方案。

  4. 至于平行,这可能是一个好主意。我不太了解 你的应用程序告诉你什么是最好的方法,但它可能是可能的 隐藏在成本下处理数据的计算成本 传输数据。尝试平衡解析和io over multiple 线程,看看你是否能实现这一目标。