String.charAt vs StringBuilder.charAt performance
Chen Liang
chen.l.liang at oracle.com
Mon Jul 21 21:59:53 UTC 2025
I finally came around and ran the benchmark on my linux-x64 device; however, I could not produce your results where String is significantly slower than StringBuilder.
This is the results I've got:
Benchmark (data) Mode Cnt Score Error Units
CharSequenceCharAtBenchmark.testString ascii avgt 5 668.649 ± 13.895 ns/op
CharSequenceCharAtBenchmark.testString non-ascii avgt 5 651.903 ± 7.240 ns/op
CharSequenceCharAtBenchmark.testStringBuilder ascii avgt 5 673.802 ± 26.260 ns/op
CharSequenceCharAtBenchmark.testStringBuilder non-ascii avgt 5 657.374 ± 35.785 ns/op
I think we might have more clue - are you testing on a macosx-aarch64 machine or some other platform? It might be that on some platforms, there are some problems in the hand-written assemblies for the intrinsics which contribute to this slowdown, instead of a problem with the C2 IR.
Chen
________________________________
From: core-libs-dev <core-libs-dev-retn at openjdk.org> on behalf of Brett Okken <brett.okken.os at gmail.com>
Sent: Monday, July 21, 2025 4:01 PM
To: Roger Riggs <roger.riggs at oracle.com>
Cc: core-libs-dev at openjdk.org <core-libs-dev at openjdk.org>
Subject: Re: String.charAt vs StringBuilder.charAt performance
Updating to have different test methods for each representation did remove the difference for the non-ascii String case for the jdk 21+ releases.
However, the ascii (latin) strings are still slower with String than StringBuilder.
How does C2 then handle something like StringCharBuffer wrapping a CharSequence for all of it's get operations:
https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/nio/StringCharBuffer.java#L88-L97
Which is then used by CharBufferSpliterator
https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/nio/CharBufferSpliterator.java
And by many CharsetEncoder impls when either source or destination is not backed by array (which would be the case if StringCharBuffer used):
https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/sun/nio/cs/UTF_8.java#L517
https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/sun/nio/cs/UnicodeEncoder.java#L81
jdk 17
Benchmark (data) Mode Cnt Score Error Units
CharSequenceCharAtBenchmark.testString ascii avgt 3 1429.358 ± 623.424 ns/op
CharSequenceCharAtBenchmark.testString non-ascii avgt 3 705.282 ± 233.453 ns/op
CharSequenceCharAtBenchmark.testStringBuilder ascii avgt 3 724.138 ± 267.346 ns/op
CharSequenceCharAtBenchmark.testStringBuilder non-ascii avgt 3 718.357 ± 864.066 ns/op
jdk 21
Benchmark (data) Mode Cnt Score Error Units
CharSequenceCharAtBenchmark.testString ascii avgt 3 1087.024 ┬▒ 235.082 ns/op
CharSequenceCharAtBenchmark.testString non-ascii avgt 3 687.520 ┬▒ 747.532 ns/op
CharSequenceCharAtBenchmark.testStringBuilder ascii avgt 3 672.802 ┬▒ 29.740 ns/op
CharSequenceCharAtBenchmark.testStringBuilder non-ascii avgt 3 689.964 ┬▒ 791.175 ns/op
jdk 25
Benchmark (data) Mode Cnt Score Error Units
CharSequenceCharAtBenchmark.testString ascii avgt 3 1176.057 ┬▒ 1157.979 ns/op
CharSequenceCharAtBenchmark.testString non-ascii avgt 3 697.382 ┬▒ 231.144 ns/op
CharSequenceCharAtBenchmark.testStringBuilder ascii avgt 3 692.970 ┬▒ 105.112 ns/op
CharSequenceCharAtBenchmark.testStringBuilder non-ascii avgt 3 703.178 ┬▒ 446.019 ns/op
jdk 26
Benchmark (data) Mode Cnt Score Error Units
CharSequenceCharAtBenchmark.testString ascii avgt 3 1132.971 ┬▒ 350.786 ns/op
CharSequenceCharAtBenchmark.testString non-ascii avgt 3 688.201 ┬▒ 175.797 ns/op
CharSequenceCharAtBenchmark.testStringBuilder ascii avgt 3 704.380 ┬▒ 101.763 ns/op
CharSequenceCharAtBenchmark.testStringBuilder non-ascii avgt 3 673.622 ┬▒ 51.462 ns/op
@Warmup(iterations = 2, time = 7, timeUnit = TimeUnit.SECONDS)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
@Fork(value = 1, jvmArgsPrepend = {"-Xms512M", "-Xmx512M"})
public class CharSequenceCharAtBenchmark {
@Param(value = {"ascii", "non-ascii"})
public String data;
private String string;
private StringBuilder stringBuilder;
@Setup(Level.Trial)
public void setup() throws Exception {
StringBuilder sb = new StringBuilder(3152);
for (int i=0; i<3152; ++i) {
char c = (char) i;
if ("ascii".equals(data)) {
c = (char) (i & 0x7f);
}
sb.append(c);
}
string = sb.toString();
stringBuilder = sb;
}
@Benchmark
public int testString() {
String sequence = this.string;
int sum = 0;
for (int i=0, j=sequence.length(); i<j; ++i) {
sum += sequence.charAt(i);
}
return sum;
}
@Benchmark
public int testStringBuilder() {
StringBuilder sequence = this.stringBuilder;
int sum = 0;
for (int i=0, j=sequence.length(); i<j; ++i) {
sum += sequence.charAt(i);
}
return sum;
}
}
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/core-libs-dev/attachments/20250721/408bcfee/attachment-0001.htm>
More information about the core-libs-dev
mailing list