web-dev-qa-db-ja.com

StringBuilderチェーンパターンsb.append(x).append(y)が通常のsb.append(x)より速いのはなぜですか。 sb.append(y)?

非常に奇妙な結果を示すマイクロベンチマークがあります。

@BenchmarkMode(Mode.Throughput)
@Fork(1)
@State(Scope.Thread)
@Warmup(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS, batchSize = 1000)
@Measurement(iterations = 40, time = 1, timeUnit = TimeUnit.SECONDS, batchSize = 1000)
public class Chaining {

    private String a1 = "111111111111111111111111";
    private String a2 = "222222222222222222222222";
    private String a3 = "333333333333333333333333";

    @Benchmark
    public String typicalChaining() {
        return new StringBuilder().append(a1).append(a2).append(a3).toString();
    }

    @Benchmark
    public String noChaining() {
        StringBuilder sb = new StringBuilder();
        sb.append(a1);
        sb.append(a2);
        sb.append(a3);
        return sb.toString();
    }
}

両方のテストの結果が同じか、少なくとも非常に近いものになることを期待しています。ただし、違いはほぼ5倍です。

# Run complete. Total time: 00:01:41

Benchmark                  Mode  Cnt      Score     Error  Units
Chaining.noChaining       thrpt   40   8538.236 ± 209.924  ops/s
Chaining.typicalChaining  thrpt   40  36729.523 ± 988.936  ops/s

それがどのように可能かを誰かが知っていますか?

45

文字列連結_a + b + c_は、Javaプログラムで頻繁に使用されるパターンであるため、HotSpot JVMには特別な最適化があります:_-XX:+OptimizeStringConcat_これはデフォルトでオンです。

HotSpot JVMは、バイトコード内のnew StringBuilder().append()...append().toString()パターンを認識し、実際のJavaメソッドを呼び出すことなく、また中間オブジェクトを割り当てずに、最適化されたマシンコードに変換します。つまり、これは一種の複合JVMです本質的。

これが、この最適化のための ソースコード です。

一方、sb.append(); sb.append(); ...は特別に処理されません。このシーケンスは、通常のJavaメソッド呼び出しのようにコンパイルされます。

_-XX:-OptimizeStringConcat_でベンチマークを再実行すると、両方のバリアントでパフォーマンスが同じになります。

56
apangin