I have a benchmark :
@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 StringConcatTest {
private int aInt;
@Setup
public void prepare() {
aInt = 100;
}
@Benchmark
public String emptyStringInt() {
return "" + aInt;
}
@Benchmark
public String valueOfInt() {
return String.valueOf(aInt);
}
}
And here is result :
Benchmark Mode Cnt Score Error Units
StringConcatTest.emptyStringInt thrpt 40 66045.741 ± 1306.280 ops/s
StringConcatTest.valueOfInt thrpt 40 43947.708 ± 1140.078 ops/s
It shows that concatenating of empty string with integer number is 30% faster than calling String.value(100). I understand that "" + 100 converted to
new StringBuilder().append(100).toString()
and -XX:+OptimizeStringConcat
optimization is applied that makes it fast. What I do not understand is why valueOf
itself is slower than concatenation.
Can someone explain what exactly is happening and why "" + 100 is faster. What magic does OptimizeStringConcat
make?
As you've mentioned, HotSpot JVM has
-XX:+OptimizeStringConcat
optimization that recognizes StringBuilder pattern and replaces it with highly tuned hand-written IR graph, whileString.valueOf()
relies on general compiler optimizations.I've found the following key differences by analyzing the generated assembly code:
char[]
array created for the result string, while the array created byInteger.toString
is cleared after allocation just like any other regular object.Integer.getChars
uses table lookup with the related array bounds check etc.There are other minor differences in the implementation of
PhaseStringOpts::int_getChars
vs.Integer.getChars
, but I guess they are not that significant for performance.BTW, if you take a bigger number (e.g. 1234567890), the performance difference will be negligible because of an extra loop in
Integer.getChars
that converts two digits at once.