1

运行此程序显示以下结果:

object ParallelTest {
  def main(args: Array[String]) {
    val start = System.nanoTime()
    val list = (1 to 10000).toList.par
    println("with par: elapsed: " + (System.nanoTime() - start) / 1000000 + " milliseconds")

    val start2 = System.nanoTime()
    val list2 = (1 to 10000).toList
    println("without par: elapsed: " + (System.nanoTime() - start2) / 1000000 + " milliseconds")
  } 
}

.

with par: elapsed: 238 milliseconds 
without par: elapsed: 0 milliseconds

如果我理解这些结果,使用par需要更长的时间,因为“并行化” aList需要将内容复制到并行数据结构?

4

3 回答 3

1

当我将它加载到我的 REPL 并执行ParallelTest.main(Array())两次时:

scala> ParallelTest.main(Array())
with par: elapsed: 23 milliseconds
without par: elapsed: 1 milliseconds

scala> ParallelTest.main(Array())
with par: elapsed: 1 milliseconds
without par: elapsed: 0 milliseconds

您所看到的几乎所有内容都是 JIT 预热。Hotspot 在第一次循环之后优化了相关方法,我们在接下来的三个迭代中看到了好处。在 JVM 上进行适当的基准测试需要丢弃前几个结果。

于 2013-08-29T02:30:48.263 回答
1

我和下一个黑客一样对毫无意义的微基准感到好奇,所以这里展示了为什么结果是有意义的,为什么放在哪里很重要par以及为什么 OP 的猜想是正确的(如果方法有缺陷):

scala> import System.nanoTime
import System.nanoTime

scala> def timed(op: =>Unit) = { val t0=nanoTime;op;println(nanoTime-t0) }
timed: (op: => Unit)Unit

scala> val data = (1 to 1000000).toList
data: List[Int] = List(1, 2, 3, 4,...

scala> timed(data.par)
85333715

scala> timed(data.par)
40952638

scala> timed(data.par)
40134628

在我的机器上,构建一个 10k 的小列表与调用它所需的时间相同par,大约 400k 纳秒,这就是为什么在绿色选中的答案中,.toList.par四舍五入.toList到零。

OTOH,按顺序构建一个 1m 的大列表更具可变性。

scala> 1 to 100 foreach (_ => timed((1 to 1000000).toList))

在某处损失十倍。我还没有查看这是否是由于重新分配、垃圾收集、内存架构或其他原因。

但有趣的是,这很容易做到:

scala> 1 to 100 foreach (_ => timed((1 to 1000000).par.to[ParVector]))

在这个测试中,ParRange边缘超出了顺序Range并且比data.par. (在我的机器上。)

对我来说有趣的是,这里没有并行计算。

这一定意味着ParVector并行组装的成本很低。比较另一个答案,其中并行组装的成本groupBy令我感到惊讶ParNewbie

于 2013-08-29T11:46:33.017 回答
0

其他人评论说,由于不确定的预热不确定性,在 JVM 上进行微基准测试很困难。我想提出一个不同的话题。

并行集合框架需要谨慎使用。所有通过并行化提高软件速度的尝试都受到阿姆达尔定律的约束:使用并行处理器的程序的加速受到程序顺序部分所需的时间的限制。

因此,仅当可能使用它们的实际应用程序可以可靠地(并且始终如一地!)对并行集合进行基准测试以确定哪些部分值得并行尝试而哪些不值得尝试时,才应用并行集合非常重要。幸运的是,在并行和顺序集合之间切换以比较它们的使用相对容易。

此外,使用并行程序来提高速度是一个相关但不同于使用并发表达解决方案的问题。Actor在 Scala 中提供了这一功能。GoOccam和其他语言依赖于CSP通信进程架构,而不是提供更细粒度和基于数学的并发表达(目前在 Scala 中也有支持 CSP 的工作)。通常,并发程序比具有并行集合的顺序程序更适合并行处理,这主要是因为阿姆达尔定律。只有在数据集相对较大且每个元素的处理负载相对较重的情况下,并行集合才会被证明是有用的。

于 2013-08-29T10:22:59.607 回答