Without looking at C2 IRs, I think there are a few potential culprits we
can look into:
1. JDK-8351000 and JDK-8351443 updated StringBuilder
2. Sequence field is read in the loop; I wonder if making it an explicit
immutable local variable changes anything here.

On Sat, Jul 19, 2025 at 2:34 PM Brett Okken <brett.okken...@gmail.com>
wrote:

> I was looking at the performance of StringCharBuffer for various
> backing CharSequence types and was surprised to see a significant
> performance difference between String and StringBuffer. I wrote a
> small jmh which shows that the String implementation of charAt is
> significantly slower than StringBuilder. Is this expected?
>
> Benchmark                            (data)      (source)  Mode  Cnt
>   Score       Error  Units
> CharSequenceCharAtBenchmark.test      ascii        String  avgt    3
> 2537.311 ┬▒  8952.197  ns/op
> CharSequenceCharAtBenchmark.test      ascii  StringBuffer  avgt    3
> 852.004 ┬▒  2532.958  ns/op
> CharSequenceCharAtBenchmark.test  non-ascii        String  avgt    3
> 5115.381 ┬▒ 13822.592  ns/op
> CharSequenceCharAtBenchmark.test  non-ascii  StringBuffer  avgt    3
> 836.230 ┬▒  1154.191  ns/op
>
>
>
> @Measurement(iterations = 3, time = 5, timeUnit = TimeUnit.SECONDS)
> @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;
>
>     @Param(value = {"String", "StringBuffer"})
>     public String source;
>
>     private CharSequence sequence;
>
>     @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);
>         }
>
>         switch(source) {
>             case "String":
>                 sequence = sb.toString();
>                 break;
>             case "StringBuffer":
>                 sequence = sb;
>                 break;
>             default:
>                 throw new IllegalArgumentException(source);
>         }
>     }
>
>     @Benchmark
>     public int test() {
>         int sum = 0;
>         for (int i=0, j=sequence.length(); i<j; ++i) {
>             sum += sequence.charAt(i);
>         }
>         return sum;
>     }
> }
>

Reply via email to