【问题标题】:Performance of scala parallel collection processingscala并行收集处理的性能
【发布时间】:2015-04-14 09:18:36
【问题描述】:

我有需要一次处理数千条记录的情况。有时,它可能有数百条记录,可能多达 30000 条记录。我正在考虑使用 scala 的并行集合。因此,为了了解其中的区别,我编写了一个简单的 pgm,如下所示:

object Test extends App{
  val list = (1 to 100000).toList
  Util.seqMap(list)
  Util.parMap(list)
}

object Util{
  def seqMap(list:List[Int]) = {
    val start = System.currentTimeMillis
    list.map(x => x + 1).toList.sum
    val end = System.currentTimeMillis
    println("time taken =" + (end - start))
    end - start
  }
  def parMap(list:List[Int]) = {
    val start = System.currentTimeMillis
    list.par.map(x => x + 1).toList.sum
    val end = System.currentTimeMillis
    println("time taken=" + (end - start))
    end - start
  }
}

我预计并行运行会更快。但是,我得到的输出是

time taken =32
time taken=127

机器配置

Intel i7 processor with 8 cores
16GB RAM
64bit Windows 8

我做错了什么?这不是并行映射的正确方案吗?

【问题讨论】:

    标签: scala parallel-processing scala-collections


    【解决方案1】:

    问题在于您正在执行的操作非常快(只需添加两个整数),以至于执行并行化的开销大于收益。仅当操作较慢时,并行化才真正有意义。

    这样想:如果你有 8 个朋友,你在一张纸上给每个人一个整数,并告诉他们加一个,把结果写下来,然后还给你,你会在之前记录下来给它们下一个整数,您将花费大量时间来回传递消息,以至于您可以更快地完成所有添加。

    另外:永远不要在 List 上使用.par,因为并行化过程必须将整个列表复制到并行集合中,然后再将整个复制出来。如果你使用 Vector,那么它就不必做这些额外的工作。

    【讨论】:

    • “另外:永远不要在列表上做 .par ”。比方说,我将数千条记录作为一个列表,这是否意味着比先转换为向量然后对其进行 par 更快?
    • 如果你在 List 上调用 .par ,它会在它可以做任何事情之前强制复制整个列表,然后它必须将它复制回来以便它可以返回一个 List 给你。您手动进行转换可能不会让它变得更好,但如果您使用 Vector,那么它不必进行任何复制。我实际上建议不要使用 List; Vector 显然对基本上所有事情都更好。
    • 还有一个疑问。如果我在地图操作中有一些数据库查询怎么办。并行化该地图会对其产生积极的帮助吗?例如: val someVal list.par.map { item=> //对 'item' 做一些操作并得到一些结果 'res' //用结果 'res' 查询数据库 } 我有几个场景,我需要生成一些报告。地图内部的逻辑有点复杂,但是在得到复杂的逻辑之后,我需要用那个结果调用数据库。所以想知道,在这种情况下是否会有任何 pblms 使用并行映射。 (注意:我现在没有使用连接池。)
    【解决方案2】:

    事实证明,并行化列表的开销比实际按顺序处理 x + 1 操作更耗时。

    但考虑一下这个修改,其中我们包含一个大约耗时超过 1 毫秒的操作,

    case class Delay() {
      Thread.sleep(1)
    }
    

    替换

    list.map(x => x + 1).toList.sum
    

    list.map(_ => Delay()).toList
    

    现在是 val list = (1 to 10000).toList(注意 10000 而不是 100000),在四核 8GB 机器上,

    scala> Util.parMap(list)
    time taken=3451
    res4: Long = 3451
    
    scala> Util.seqMap(list)
    time taken =10816
    res5: Long = 10816
    

    我们可以推断(更好,猜测)对于具有耗时操作的大型集合,与顺序集合处理相比,并行化集合的开销不会显着影响经过的时间。

    【讨论】:

      【解决方案3】:

      如果您正在执行基准测试,请考虑使用 JMH 之类的工具来避免您可能遇到的所有问题,前提是您以程序显示的方式对其进行测量。例如,JIT 可能会显着改变您的结果,但仅在经过一些迭代之后。

      根据我的经验,如果输入不够大,并行集合通常会更慢:如果输入很小,则初始拆分和最后的“组合”不会得到回报。

      所以再次进行基准测试,使用不同大小的列表(尝试 30 000、100 000 和 1 000 000)。

      此外,如果您进行数值处理,请考虑使用Array(而不是List)和while(而不是map)。这些对于底层 JVM 来说是“更原生的”(= 更快),而在您的情况下,您可能正在测量垃圾收集器的性能。至于Array,您可以“就地”存储操作的结果。

      【讨论】:

        【解决方案4】:

        并行集合在执行需要一些时间的操作之前初始化线程。

        因此,当您通过元素数量较少的并行集合执行操作或操作耗时较短时,并行集合的执行速度会变慢

        【讨论】:

          猜你喜欢
          • 2017-04-03
          • 1970-01-01
          • 2012-08-14
          • 1970-01-01
          • 1970-01-01
          • 2012-07-19
          • 1970-01-01
          • 2017-06-11
          • 1970-01-01
          相关资源
          最近更新 更多