3

少し前に、可変変数を新しい値で(ただし、新しいオブジェクトを作成せずに)更新する方法が高速であるかどうかを判断するためのコードを作成しました。1つの方法は一時的な値を使用し、余分なコピーがあり、他の方法は使用しません。

コードサンプル#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

(はい、テストの長さを増やしても、比率は同じであり、コンパイラーのオプションは重要ではありません。)

このテストはAndroidOS2.3.4を搭載した携帯電話で実行しましたが、勝者は同じです。しかし、ご覧のとおり、Ideone.comでは逆のことが起こり、2番目の方法ははるかに遅くなります。では、実際にそこで何が起こっているのか説明できますか?そして、Scalaコンパイラの(これら2つの)バージョンのどちらが正しく機能していますか?それは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

また、サイクル数に関係なく、3回目の実行は他のすべての実行よりも長くなります。それは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倍高速です)。それで、元の質問はまだ生きています、なぜそれがscala2.9でとても遅いのですか?

4

1 に答える 1

0

一つには、ideoneランタイムはメモリに常駐し、JVMには他のユーザーによって歪められた統計があり、それが少し奇妙なネイティブコードを構築する原因になっている可能性があります。ベンチマークに影響を与える未知のものがたくさんあるので、パフォーマンスの面でそれから出てくるものはすべて無視したくなるでしょう。より具体的にニーズに合わせて変更されたバージョンを持っている可能性もあります。

于 2012-06-26T14:21:46.817 に答える