<i18n dev> RFR: 8365675: Add String Unicode Case-Folding Support [v6]
Xueming Shen
sherman at openjdk.org
Wed Oct 29 02:03:14 UTC 2025
On Tue, 28 Oct 2025 20:15:26 GMT, Xueming Shen <sherman at openjdk.org> wrote:
>> With the following changes
>>
>> - Use int[] / code point storage instead of char[] / char for folding data.
>> - Add a fast path for Latin-1 vs. Latin-1 comparisons.
>> - Speed up the UTF-16 path as well.
>>
>> The only remaining “slow” path is Latin-1 vs. UTF-16 for now.
>> Would appreciate some eagle eyes on the new fast-path implementation. The tests suggest it’s working as expected :-)
>>
>> **StringCompareToFoldCase.latin1UTF16 avgt 15 23.959 ± 2.357 ns/op
>> StringCompareToFoldCase.latin1UTF16EQ avgt 15 22.455 ± 0.104 ns/op
>> StringCompareToFoldCase.latin1UTF16EQFC avgt 15 32.782 ± 0.962 ns/op
>> StringCompareToFoldCase.latin1UTF16FC avgt 15 32.581 ± 0.725 ns/op**
>>
>>
>> Benchmark Mode Cnt Score Error Units
>> StringCompareToFoldCase.asciiLower avgt 15 17.983 ± 0.208 ns/op
>> StringCompareToFoldCase.asciiLowerEQ avgt 15 9.986 ± 0.254 ns/op
>> StringCompareToFoldCase.asciiLowerEQFC avgt 15 10.781 ± 0.161 ns/op
>> StringCompareToFoldCase.asciiLowerFC avgt 15 10.274 ± 0.136 ns/op
>> StringCompareToFoldCase.asciiUpperLower avgt 15 12.465 ± 0.409 ns/op
>> StringCompareToFoldCase.asciiUpperLowerEQ avgt 15 10.961 ± 0.407 ns/op
>> StringCompareToFoldCase.asciiUpperLowerEQFC avgt 15 9.157 ± 0.166 ns/op
>> StringCompareToFoldCase.asciiUpperLowerFC avgt 15 9.228 ± 0.254 ns/op
>> StringCompareToFoldCase.asciiWithDFFC avgt 15 57.603 ± 1.540 ns/op
>> StringCompareToFoldCase.greekLower avgt 15 39.593 ± 0.128 ns/op
>> StringCompareToFoldCase.greekLowerEQ avgt 15 39.249 ± 0.032 ns/op
>> StringCompareToFoldCase.greekLowerEQFC avgt 15 13.993 ± 0.346 ns/op
>> StringCompareToFoldCase.greekLowerFC avgt 15 14.030 ± 0.454 ns/op
>> StringCompareToFoldCase.greekUpperLower avgt 15 7.137 ± 0.130 ns/op
>> StringCompareToFoldCase.greekUpperLowerEQ avgt 15 7.496 ± 0.104 ns/op
>> StringCompareToFoldCase.greekUpperLowerEQFC avgt 15 6.203 ± 0.316 ns/op
>> StringCompareToFoldCase.greekUpperLowerFC avgt 15 6.235 ± 0.256 ns/op
>> StringCompareToFoldCase.latin1UTF16 avgt 15 23.959 ± 2.357 ns/op
>> StringCompareToFoldCase.latin1UTF16EQ avgt 15 22.455 ± 0.104 ns/op
>> StringCompareToFoldCase.latin1UTF16EQFC avgt 15 32.782 ± 0.962 ns/op
>> StringCompareToFoldCase.latin1UTF16FC avgt 15 32.581 ± 0.725 ns/op
>> StringCompare...
>
> Experimenting with Arrays.mismatch at the beginning of the array iteration as
>
>
> int k = ArraysSupport.mismatch(value, other, lim);
> if (k < 0)
> return len - olen;
> for (; k < lim; k++) {
> ....
> }
>
>
> The benchmark results suggest that it does help 'dramatically' when the compared strings share with the same prefix. For example those "UpperLower" test cases (which shares the same upper cases text prefix. However it is also relatively expensive, with a 20%-ish overhead when the strings do not share the same string text but are case-insensitively equals. I would suggest let's leave it out for now?
>
> ### With Arrays.mismatch
>
>
> Benchmark Mode Cnt Score Error Units
> StringCompareToFoldCase.asciiLower avgt 15 15.044 ± 0.751 ns/op
> StringCompareToFoldCase.asciiLowerEQ avgt 15 10.033 ± 0.366 ns/op
> StringCompareToFoldCase.asciiLowerEQFC avgt 15 12.094 ± 0.288 ns/op
> StringCompareToFoldCase.asciiLowerFC avgt 15 12.513 ± 0.290 ns/op
> StringCompareToFoldCase.asciiUpperLower avgt 15 11.716 ± 0.471 ns/op
> StringCompareToFoldCase.asciiUpperLowerEQ avgt 15 11.120 ± 0.458 ns/op
> StringCompareToFoldCase.asciiUpperLowerEQFC avgt 15 7.544 ± 0.103 ns/op
> StringCompareToFoldCase.asciiUpperLowerFC avgt 15 7.384 ± 0.167 ns/op
> StringCompareToFoldCase.asciiWithDFFC avgt 15 54.949 ± 1.260 ns/op
> StringCompareToFoldCase.greekLower avgt 15 39.492 ± 0.124 ns/op
> StringCompareToFoldCase.greekLowerEQ avgt 15 39.266 ± 0.071 ns/op
> StringCompareToFoldCase.greekLowerEQFC avgt 15 28.049 ± 0.292 ns/op
> StringCompareToFoldCase.greekLowerFC avgt 15 28.272 ± 0.115 ns/op
> StringCompareToFoldCase.greekUpperLower avgt 15 7.103 ± 0.052 ns/op
> StringCompareToFoldCase.greekUpperLowerEQ avgt 15 7.439 ± 0.079 ns/op
> StringCompareToFoldCase.greekUpperLowerEQFC avgt 15 2.716 ± 0.138 ns/op
> StringCompareToFoldCase.greekUpperLowerFC avgt 15 2.628 ± 0.051 ns/op
> StringCompareToFoldCase.latin1UTF16 avgt 15 23.147 ± 0.094 ns/op
> StringCompareToFoldCase.latin1UTF16EQ avgt 15 22.626 ± 0.081 ns/op
> StringCompareToFoldCase.latin1UTF16EQFC avgt 15 38.453 ± 0.697 ns/op
> StringCompareToFoldCase.latin1UTF16FC avgt 15 38.464 ± 0.441 ns/op
> StringCompareToFoldCase.supLower avgt 15 54.443 ± 0.162 ns/op
> StringCompareToFoldCase....
### Long: packing 1:M-count + 1-3 folding codepoints
https://cr.openjdk.org/~sherman/casefolding_long/
The performance is slightly better, but not as good as I would have expected. The access to codepoint from the long looks a little clumsy, but the logic looks smooth. need more work. opinion?
Benchmark Mode Cnt Score Error Units
StringCompareToFoldCase.asciiLower avgt 15 15.487 ± 0.298 ns/op
StringCompareToFoldCase.asciiLowerEQ avgt 15 10.005 ± 0.368 ns/op
StringCompareToFoldCase.asciiLowerEQFC avgt 15 10.755 ± 0.160 ns/op
StringCompareToFoldCase.asciiLowerFC avgt 15 10.349 ± 0.155 ns/op
StringCompareToFoldCase.asciiUpperLower avgt 15 12.188 ± 0.278 ns/op
StringCompareToFoldCase.asciiUpperLowerEQ avgt 15 10.901 ± 0.551 ns/op
StringCompareToFoldCase.asciiUpperLowerEQFC avgt 15 9.218 ± 0.165 ns/op
StringCompareToFoldCase.asciiUpperLowerFC avgt 15 9.335 ± 0.404 ns/op
StringCompareToFoldCase.asciiWithDFFC avgt 15 37.010 ± 0.518 ns/op
StringCompareToFoldCase.greekLower avgt 15 39.572 ± 0.098 ns/op
StringCompareToFoldCase.greekLowerEQ avgt 15 39.317 ± 0.104 ns/op
StringCompareToFoldCase.greekLowerEQFC avgt 15 20.428 ± 0.243 ns/op
StringCompareToFoldCase.greekLowerFC avgt 15 19.623 ± 0.141 ns/op
StringCompareToFoldCase.greekUpperLower avgt 15 7.105 ± 0.048 ns/op
StringCompareToFoldCase.greekUpperLowerEQ avgt 15 7.462 ± 0.092 ns/op
StringCompareToFoldCase.greekUpperLowerEQFC avgt 15 6.518 ± 0.128 ns/op
StringCompareToFoldCase.greekUpperLowerFC avgt 15 6.593 ± 0.240 ns/op
StringCompareToFoldCase.latin1UTF16 avgt 15 23.130 ± 0.152 ns/op
StringCompareToFoldCase.latin1UTF16EQ avgt 15 22.606 ± 0.089 ns/op
StringCompareToFoldCase.latin1UTF16EQFC avgt 15 29.574 ± 0.348 ns/op
StringCompareToFoldCase.latin1UTF16FC avgt 15 29.691 ± 0.445 ns/op
StringCompareToFoldCase.supLower avgt 15 55.027 ± 0.676 ns/op
StringCompareToFoldCase.supLowerEQ avgt 15 55.784 ± 0.368 ns/op
StringCompareToFoldCase.supLowerEQFC avgt 15 24.984 ± 0.157 ns/op
StringCompareToFoldCase.supLowerFC avgt 15 24.865 ± 0.139 ns/op
StringCompareToFoldCase.supUpperLower avgt 15 14.538 ± 0.144 ns/op
StringCompareToFoldCase.supUpperLowerEQ avgt 15 14.728 ± 0.206 ns/op
StringCompareToFoldCase.supUpperLowerEQFC avgt 15 9.932 ± 0.121 ns/op
StringCompareToFoldCase.supUpperLowerFC avgt 15 9.870 ± 0.119 ns/op
-------------
PR Review Comment: https://git.openjdk.org/jdk/pull/27628#discussion_r2471532292
More information about the i18n-dev
mailing list