7

Groovy を少しいじっ--indyて、パフォーマンスに顕著な影響があるかどうかを確認するために、バブル ソートの実装を作成しました。

基本的に、1,000 個のランダムな整数のリストを 1,000 回ソートし、リストをソートするための平均実行時間を測定します。

リストの半分はInteger[]で、残りの半分はArrayList<Integer>です。

結果は本当に私を混乱させます:

$ groovyc BubbleSort.groovy
$ time java -cp ~/.gvm/groovy/current/embeddable/groovy-all-2.4.3.jar:. BubbleSort
Average: 22.926ms
Min: 11.202ms
[...] 26.48s user 0.84s system 109% cpu 25.033 total

$ groovyc --indy BubbleSort.groovy
$ time java -cp ~/.gvm/groovy/current/embeddable/groovy-all-2.4.3-indy.jar:. BubbleSort
Average: 119.766ms
Min: 68.251ms
[...] 166.05s user 1.52s system 135% cpu 2:03.82 total

ベンチマークの実行中の CPU 使用率を見ると、CPU 使用率は、--indyなしでコンパイルした場合よりもはるかに高くなっています。

CPU使用率

これに興味をそそられたので、もう一度ベンチマークを実行しましたが、今回は Yourkit エージェントと CPU トレースを有効にして実行しました。記録された呼び出しツリーは次のとおりです。

なし--indy: <code>--indy</code> なしのコール ツリー

--indy: <code>--indy</code> を使用したコール ツリー

パフォーマンス チャートは次のとおり--indyです。コードが非常に遅いため、タイム スケールが異なることに注意してください。

なし--indy(1 秒スケール): <code>--indy</code> なしのパフォーマンス

あり ( --indy60 年代スケール): <code>--indy</code> でのパフォーマンス

ご覧のとおり、CPU 使用率は、なしでコンパイルすると 1 つのコアの 100% (グラフでは 12.5%) で安定しますが、でコンパイルする--indyと 12.5% から ~35% の間で大きく変化し--indyます。さらに紛らわしいのは、Yourkit が 1 つのライブ スレッドしか報告しないことです (そして、私のコードはメイン スレッドのみを使用します)。

でコンパイルされたコード--indyも、開始時に多くのカーネル時間を使用しますが、これはしばらくすると低下し、0% で安定します。その時点で、コードは少し高速化され (ヒープ使用率の増加率が増加)、CPU 使用率が増加します。 .

誰かが私にこの動作を説明できますか?

バージョン:

$ groovy -v
Groovy Version: 2.4.3 JVM: 1.8.0_45 Vendor: Oracle Corporation OS: Linux

$ java -version
java version "1.8.0_45"
Java(TM) SE Runtime Environment (build 1.8.0_45-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, mixed mode)

BubbleSort.groovy:

class BubbleSort {
    final def array

    BubbleSort(final def array) {
        this.array = array
    }

    private void swap(int a, int b) {
        def tmp = array[a];
        array[a] = array[b]
        array[b] = tmp;
    }

    private void rise(int index) {
        for(int i = index; i > 0; i--) {
            if(array[i] < array[i - 1]) {
                swap(i, i-1)
            } else {
                break
            }
        }
    }

    void sort() {
        for(int i = 1; i < array.size(); i++) {
            rise i
        }
    }

    final static Random random = new Random()

    static void main(String[] args) {
        def n = 1000
        def size = 1000
        // Warm up
        doBenchmark 100, size
        def results = doBenchmark n, size
        printf("Average: %.3fms%n", results.total / 1e6 / n)
        printf("Min: %.3fms%n", results.min / 1e6)
    }

    private static def doBenchmark(int n, int size) {
        long total = 0
        long min = Long.MAX_VALUE
        n.times {
            def array = (1..size).collect { random.nextInt() }
            if(it % 2) {
                array = array as Integer[]
            }
            def start = System.nanoTime()
            new BubbleSort<Integer>(array).sort()
            def end = System.nanoTime()
            def time = end - start
            total += time
            min = Math.min min, time
        }
        return [total: total, min: min]
    }
}

動作に関連しない限り、バブル ソートの実装の最適化には興味がinvokedynamicありません。ここでの目的は、可能な限り最高のパフォーマンスを発揮するバブル ソートを記述することではなく、--indyパフォーマンスに大きな悪影響を与える理由を理解することです。

アップデート:

私はコードを JRuby に変換し、同じことを試してみましたが、結果は似ていますが、JRuby は以下を使用しないとそれほど速くはありませんinvokedynamic

$ JAVA_OPTS="-Djruby.compile.invokedynamic=false" jruby bubblesort.rb
Average: 78.714ms
Min: 35.000ms

$ JAVA_OPTS="-Djruby.compile.invokedynamic=true" jruby bubblesort.rb
Average: 136.287ms
Min: 92.000ms

更新 2:

リストを半分の時間に変更するコードを削除すると、Integer[]パフォーマンスが大幅に向上しますが、なしでも高速--indyです。

$ groovyc BubbleSort.groovy
$ java -cp ~/.gvm/groovy/current/embeddable/groovy-all-2.4.3.jar:. BubbleSort
Average: 29.794ms
Min: 26.577ms

$ groovyc --indy BubbleSort.groovy
$ java -cp ~/.gvm/groovy/current/embeddable/groovy-all-2.4.3-indy.jar:. BubbleSort
Average: 37.506ms
Min: 33.555ms

JRuby で同じことを行うと、invokedynamicより高速になります。

$ JAVA_OPTS="-Djruby.compile.invokedynamic=false" jruby bubblesort.rb
Average: 34.388ms
Min: 31.000ms

$ JAVA_OPTS="-Djruby.compile.invokedynamic=true" jruby bubblesort.rb
Average: 20.785ms
Min: 18.000ms
4

1 に答える 1