Подтвердить что ты не робот

Почему шаблон привязки StringBuilder sb.append(x).append(y) быстрее обычного sb.append(x); sb.append(у)?

У меня есть microbenchmark, который показывает очень странные результаты:

@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();
    }
}

Я ожидаю, что результаты обоих тестов будут одинаковыми или, по крайней мере, очень близкими. Однако разница составляет почти 5x:

# 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

Кто-нибудь знает, как это возможно?

4b9b3361

Ответ 1

Конкатенация строк a + b + c является очень частым шаблоном в Java-программах, поэтому для HotSpot JVM предусмотрена специальная оптимизация: -XX:+OptimizeStringConcat, которая по умолчанию включена.

JVM HotSpot распознает шаблон new StringBuilder().append()...append().toString() в байтовом коде и переводит его в оптимизированный машинный код без вызова реальных методов Java и без выделения промежуточных объектов. То есть это своего рода составная JVM внутренняя.

Вот этот исходный код для этой оптимизации.

С другой стороны, sb.append(); sb.append(); ... не обрабатывается специально. Эта последовательность компилируется точно так же, как обычные вызовы методов Java.

Если вы повторите тест с -XX:-OptimizeStringConcat, производительность будет одинаковой для обоих вариантов.