java - example - ¿Por qué el patrón de encadenamiento StringBuilder sb.append(x).append(y) es más rápido que el estándar sb.append(x); sb.append(y)?
stringbuilder java example (1)
La concatenación de cadenas a + b + c
es un patrón muy frecuente en los programas de Java, por lo que HotSpot JVM tiene una optimización especial para él: -XX:+OptimizeStringConcat
que está activado por defecto.
HotSpot JVM reconoce el new StringBuilder().append()...append().toString()
patrón new StringBuilder().append()...append().toString()
en el bytecode y lo traduce al código máquina optimizado sin llamar a los métodos reales de Java y sin asignar objetos intermedios. Es decir, este es un tipo de compuesto JVM intrínseco.
Aquí está el código fuente para esta optimización.
Por otro lado, sb.append(); sb.append(); ...
sb.append(); sb.append(); ...
sb.append(); sb.append(); ...
no se maneja especialmente. Esta secuencia se compila al igual que las llamadas a un método Java normal.
Si vuelve a ejecutar el benchmark con -XX:-OptimizeStringConcat
, el rendimiento será el mismo para ambas variantes.
Tengo un microbenchmark que muestra resultados muy extraños:
@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();
}
}
Estoy esperando que los resultados de ambas pruebas sean los mismos o al menos muy cercanos. Sin embargo, la diferencia es casi 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
¿Alguien sabe cómo eso es posible?