Thanks Johannes for another test case!

Hotspot compiler investigation concludes this is due to inlining limits; an 
issue https://bugs.openjdk.org/browse/JDK-8364043 has been created. The guess 
for now is that limits are reached on different platforms inconsistently.
________________________________
From: core-libs-dev <core-libs-dev-r...@openjdk.org> on behalf of Johannes 
Döbler <j...@civilian-framework.org>
Sent: Thursday, July 24, 2025 12:33 PM
To: core-libs-dev@openjdk.org <core-libs-dev@openjdk.org>
Subject: Re: String.charAt vs StringBuilder.charAt performance

Hi Brett,

I ran your benchmark on a Windows11 Pro 24H2 / ARM64 / JDK 21 system  and can 
reproduce your findings that CharSequenceCharAtBenchmark.testString<ascii> has 
weaker performance.
I also added these benchmarks:
    @Benchmark
    public int testStringBuilderAsCS() {
        return test(this.stringBuilder);
    }

    @Benchmark
    public int testStringAsCS() {
        return test(this.string);
    }

    private int test(CharSequence sequence) {
        int sum = 0;
        for (int i=0, j=sequence.length(); i<j; ++i) {
            sum += sequence.charAt(i);
        }
        return sum;
    }

and see a total breakdown of  CharSequenceCharAtBenchmark.testStringAsCS

Benchmark                                             (data)  Mode  Cnt     
Score     Error  Units
CharSequenceCharAtBenchmark.testString                 ascii  avgt    5  
1647,439 ±  70,486  ns/op
CharSequenceCharAtBenchmark.testString             non-ascii  avgt    5   
939,780 ±  63,896  ns/op
CharSequenceCharAtBenchmark.testStringAsCS             ascii  avgt    5  
1657,796 ±  18,488  ns/op
CharSequenceCharAtBenchmark.testStringAsCS         non-ascii  avgt    5  
9400,447 ± 290,066  ns/op
CharSequenceCharAtBenchmark.testStringBuilder          ascii  avgt    5   
923,943 ±   6,130  ns/op
CharSequenceCharAtBenchmark.testStringBuilder      non-ascii  avgt    5   
941,507 ±  30,786  ns/op
CharSequenceCharAtBenchmark.testStringBuilderAsCS      ascii  avgt    5   
930,974 ±  33,187  ns/op
CharSequenceCharAtBenchmark.testStringBuilderAsCS  non-ascii  avgt    5   
945,983 ±  87,636  ns/op

Best
Johannes

On 22/07/2025 13:44, Brett Okken wrote:
It does look like this is windows specific. If I run on WSL, I get results 
similar to your linux-x64:

Benchmark                                         (data)  Mode  Cnt    Score    
 Error  Units
CharSequenceCharAtBenchmark.testString             ascii  avgt    3  679.294 ± 
302.947  ns/op
CharSequenceCharAtBenchmark.testString         non-ascii  avgt    3  702.071 ± 
926.959  ns/op
CharSequenceCharAtBenchmark.testStringBuilder      ascii  avgt    3  682.815 ± 
301.649  ns/op
CharSequenceCharAtBenchmark.testStringBuilder  non-ascii  avgt    3  678.169 ± 
810.276  ns/op

And I go back to the original version of the test, where String vs 
StringBuilder is defined by parameter and both assigned to same local variable 
as part of set up, that also shows no difference in wsl.
So it appears everything I observed is an artifact of Windows specific 
intrinsics?

Benchmark                            (data)       (source)  Mode  Cnt    Score  
   Error  Units
CharSequenceCharAtBenchmark.test      ascii         String  avgt    3  660.597 
± 146.405  ns/op
CharSequenceCharAtBenchmark.test      ascii  StringBuilder  avgt    3  659.395 
± 155.167  ns/op
CharSequenceCharAtBenchmark.test  non-ascii         String  avgt    3  647.955 
± 189.747  ns/op
CharSequenceCharAtBenchmark.test  non-ascii  StringBuilder  avgt    3  639.678 
± 146.923  ns/op

On Mon, Jul 21, 2025 at 5:13 PM Brett Okken 
<brett.okken...@gmail.com<mailto:brett.okken...@gmail.com>> wrote:
I am running Windows x64. Windows 11 Pro 24H2

Intel(R) Core(TM) i7-1370P

On Mon, Jul 21, 2025 at 4:59 PM Chen Liang 
<chen.l.li...@oracle.com<mailto:chen.l.li...@oracle.com>> wrote:
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<mailto:core-libs-dev-r...@openjdk.org>> on 
behalf of Brett Okken 
<brett.okken...@gmail.com<mailto:brett.okken...@gmail.com>>
Sent: Monday, July 21, 2025 4:01 PM
To: Roger Riggs <roger.ri...@oracle.com<mailto:roger.ri...@oracle.com>>
Cc: core-libs-dev@openjdk.org<mailto:core-libs-dev@openjdk.org> 
<core-libs-dev@openjdk.org<mailto: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