Scala 2.8 vs 2.9基准测试,结果奇怪

时间:2012-06-26 14:15:46

标签: scala optimization benchmarking scala-2.8 scala-2.9

前段时间,我写了一些代码来决定用新值更新可变变量的方法(但不创建新对象)更快。一种方法使用临时值,并且有额外的复制,而其他方法则没有。

代码示例#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

在我的电脑上(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中的速度要慢得多。

1 个答案:

答案 0 :(得分:0)

首先,ideone运行时将驻留在内存中,并且JVM可能会有一些其他用户倾斜的统计信息,导致它构造一些稍微奇怪的本机代码。我很想忽视任何由性能明智而产生的东西,因为有很多未知的东西会影响基准测试。甚至有可能他们有更改版本以更具体地满足他们的需求。