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-r...@openjdk.org> on behalf of Brett Okken 
<brett.okken...@gmail.com>
Sent: Monday, July 21, 2025 4:01 PM
To: Roger Riggs <roger.ri...@oracle.com>
Cc: core-libs-dev@openjdk.org <core-libs-dev@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;
    }
}

Reply via email to