前段时间,我写了一些代码来决定用新值(但不创建新对象)更新可变变量的哪种方法更快。一种方法使用临时值并且有额外的复制,而另一种则没有。
代码示例 #1 - 重新分配
class Test(var x: Float, var y: Float) {
@inline final def :=(x: Float, y: Float) = {
this.x = x
this.y = y
}
//...
@inline final def :=(o: Test) = { //reassign from arg "o"
x = o.x
y = o.y
}
//...
}
object Benchmark {
//...
val tmp = new Test(0, 0)
@inline final def calc_something_for_reassign(a: Float, b: Float) = {
tmp := (a, b)
tmp.something_m //this is a simple method that changes the object
tmp
}
//...
}
代码示例 #2 - 无需重新分配
class Test(var x: Float, var y: Float) {
@inline final def := (x: Float, y: Float) = //it's the same as in sample #1
//...
@inline final def := (of: (Test) => Unit) = { //apply mutating function "of"
of(this)
}
//...
}
object Benchmark {
//...
@inline final def calc_something_for_forwarding(a: Float, b: Float) = {
(result: Test) => {
result := (a, b)
result.something_m
}
}
}
完整代码在这里:http: //ideone.com/A62Ts
在我的 PC(scala 编译器 v. 2.8,jre7)上,结果看起来像预期的那样:
reassignment: 0.046sec; Result:42.0, 3.0
forwarding: 0.007sec; Result:42.0, 3.0
forwarding: 0.006sec; Result:42.0, 3.0
reassignment: 0.044sec; Result:42.0, 3.0
(是的,如果我增加测试长度,比例是一样的,编译器选项无关紧要。)
我在装有 Android OS 2.3.4 的手机上执行了此测试,获胜者仍然相同。但是,正如我们所见,在 Ideone.com 上发生了相反的事情,第二种方法要慢得多。那么你能解释一下,那里实际发生了什么吗?哪个(这两个)版本的 Scala 编译器工作正常?它是 scala-2.9.1 中的错误吗?
更新:在 scala 2.9 测试打印以下内容(尝试了很多次,没有太大变化):
reassignment: 0.047sec; Result:42.0, 3.0
forwarding: 0.032sec; Result:42.0, 3.0
forwarding: 0.219sec; Result:42.0, 3.0
reassignment: 0.045sec; Result:42.0, 3.0
并且对于任意数量的循环,第三次运行比所有其他运行都长。会导致GC吗?或者为什么还会发生这种奇怪的事情?如果我改为按b、b、a、b、b、a的顺序调用它们,那么就不会有这样奇怪的峰:
println(Benchmark.run_b(Int.MaxValue/10))
println(Benchmark.run_b(Int.MaxValue/10))
println(Benchmark.run_a(Int.MaxValue/10))
println(Benchmark.run_b(Int.MaxValue/10))
println(Benchmark.run_b(Int.MaxValue/10))
println(Benchmark.run_a(Int.MaxValue/10))
scala 2.8 中的结果(稳定):
forwarding: 0.012sec; Result:42.0, 3.0
forwarding: 0.012sec; Result:42.0, 3.0
reassignment: 0.347sec; Result:42.0, 3.0
forwarding: 0.011sec; Result:42.0, 3.0
forwarding: 0.005sec; Result:42.0, 3.0
reassignment: 0.333sec; Result:42.0, 3.0
scala 2.9 中的结果(稳定):
forwarding: 0.184sec; Result:42.0, 3.0
forwarding: 0.179sec; Result:42.0, 3.0
reassignment: 0.354sec; Result:42.0, 3.0
forwarding: 0.17sec; Result:42.0, 3.0
forwarding: 0.169sec; Result:42.0, 3.0
reassignment: 0.342sec; Result:42.0, 3.0
然而,在 scala 2.9 中重新分配的方法与在 scala 2.8 中一样长,但在 scala 2.9 中,快速方法大约慢 10 倍(但仍然比慢方法快大约 2 倍)。所以最初的问题仍然存在,为什么它在 scala 2.9 中要慢得多。