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

标签 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

在我的 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 中这么慢。

最佳答案

一方面,ideone 运行时将驻留在内存中,JVM 可能有一些被其他用户歪曲的统计数据,这导致它构建了一些稍微奇怪的 native 代码。我很想忽略从性能方面产生的任何东西,因为有太多未知的东西会影响基准测试。他们甚至有可能得到一个修改版本来更具体地满足他们的需求。

关于Scala 2.8 与 2.9 基准测试,奇怪的结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11209336/

相关文章:

scala - 在Scala中调用注释 'methods'

scala var args 案例类

regex - 在Scala中的两个字符串之间提取字符串

matlab - 如何 'copy'矩阵而不在内存中创建导致内存溢出的临时矩阵?

c# - 优化套接字字节数据

performance - Google Analytics 有性能开销吗?

scala - 通过 'foreach' 在 List[JsObject] 上组成不可变列表

benchmarking - 使用 Optaplanner 对实时规划算法进行基准测试

Linux:CPU 基准测试需要更长的时间和不同的 CPU 利用率级别

python - Pyarrow:将流读入 p​​andas 数据帧高内存消耗