我在Scala中对集合执行多个后续转换之间进行性能比较,这些转换是严格(急切执行评估)和非严格(懒惰执行评估)。
def time[R](block: => R): R = {
val t0 = System.nanoTime()
val result = block // call-by-name
val t1 = System.nanoTime()
println("Elapsed time: " + (t1 - t0)/1e9 + "s")
result
}
/* A view on a collection makes all transformations lazy, which makes it
possible to combine multiple transformations into one. */
// The non-lazy (eager) version:
time {
(1 to 1e7.toInt).map(_ + 2).map(x => {
if(x % 2 != 0) -x else x
}).sum
}
// The lazy version using a view:
time {
(1 to 1e7.toInt).view.map(_ + 2).map(x => {
if(x % 2 != 0) -x else x
}).force.sum
}
在我的笔记本电脑上,渴望版本的首次运行比懒惰版本慢。见下面的时间。
渴望版本:2.4秒
懒惰的情况:0.7秒
但是,从第二次运行开始,它们都需要 0.7秒。为什么呢?
答案 0 :(得分:3)
在分析代码时,通常不会测量第一次运行/迭代,称为冷运行,因为它会导致操作系统和/或运行时环境的一些初始成本。
对于在JVM上执行的Java和Scala代码,首先执行某些指令可能需要将类和方法加载和解析到内存中,从而产生一些开销。
JVM还将在第一次迭代期间用本机指令替换例如,一些数学运算,从而加快后续迭代的执行速度。
答案 1 :(得分:1)
获取准确的基准测试非常困难 - 如果上面的代码反映了用于获取这些结果的实际代码,则不太可能获得可靠的数据。
您应该使用真正的基准测试工具,例如https://github.com/ktoso/sbt-jmh。
一旦你确定性能存在显着差异,那么你就可以开始研究如何/为什么。