运行此程序会显示以下结果:
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
会花费更长时间,因为“并行化”List
需要将内容复制到并行数据结构?
答案 0 :(得分: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进行适当的基准测试需要丢掉前几个结果。
答案 1 :(得分: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 nanos,这就是为什么,在绿色检查答案中,.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
的成本很低。比较this other answer并列groupBy
中汇编的成本对我来说是ParNewbie
令人惊讶。
答案 2 :(得分:0)
其他人已经注意到由于非确定性的预热不确定性而难以在JVM上进行微基准测试。我想提出一个不同的话题。
并行集合框架需要谨慎使用。所有通过并行化提高软件速度的尝试均受Amdahl's Law的约束:使用并行处理器的程序的加速受到程序序列部分所需时间的限制。
因此,只有当可能使用它们的真实应用程序可靠(并且始终如一)进行基准测试以确定哪些部分值得并行尝试以及哪些部分不是并行时,才应用并行集合。幸运的是,在并行和顺序集合之间切换以比较它们的使用相对容易。
此外,使用并行程序来提高速度是使用并发来表达解决方案的一个相关但不同的问题。 Actors在Scala中提供此功能。 Go,Occam和其他语言依赖于CSP通信流程体系结构,而不是提供更细粒度和基于数学的并发表达式(目前还有在Scala中支持CSP的工作) )。通常,并发程序比并行集合的顺序程序更适合并行处理,主要是因为Amdahl定律。并行集合仅适用于相对较大的数据集和每个元素相对较重的处理负载。