On Wed, 26 Jun 2024 09:43:32 GMT, lingjun-cg <[email protected]> wrote:
>> ### Performance regression of DecimalFormat.format
>> From the output of perf, we can see the hottest regions contain atomic
>> instructions. But when run with JDK 11, there is no such problem. The
>> reason is the removed biased locking.
>> The DecimalFormat uses StringBuffer everywhere, and StringBuffer itself
>> contains many synchronized methods.
>> So I added support for some new methods that accept StringBuilder which is
>> lock-free.
>>
>> ### Benchmark testcase
>>
>> @BenchmarkMode(Mode.AverageTime)
>> @Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS)
>> @Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
>> @State(Scope.Thread)
>> @OutputTimeUnit(TimeUnit.NANOSECONDS)
>> public class JmhDecimalFormat {
>>
>> private DecimalFormat format;
>>
>> @Setup(Level.Trial)
>> public void setup() {
>> format = new DecimalFormat("#0.00000");
>> }
>>
>> @Benchmark
>> public void testNewAndFormat() throws InterruptedException {
>> new DecimalFormat("#0.00000").format(9524234.1236457);
>> }
>>
>> @Benchmark
>> public void testNewOnly() throws InterruptedException {
>> new DecimalFormat("#0.00000");
>> }
>>
>> @Benchmark
>> public void testFormatOnly() throws InterruptedException {
>> format.format(9524234.1236457);
>> }
>> }
>>
>>
>> ### Test result
>> #### Current JDK before optimize
>>
>> Benchmark Mode Cnt Score Error Units
>> JmhDecimalFormat.testFormatOnly avgt 50 642.099 ? 1.253 ns/op
>> JmhDecimalFormat.testNewAndFormat avgt 50 989.307 ? 3.676 ns/op
>> JmhDecimalFormat.testNewOnly avgt 50 303.381 ? 5.252 ns/op
>>
>>
>>
>> #### Current JDK after optimize
>>
>> Benchmark Mode Cnt Score Error Units
>> JmhDecimalFormat.testFormatOnly avgt 50 351.499 ? 0.761 ns/op
>> JmhDecimalFormat.testNewAndFormat avgt 50 615.145 ? 2.478 ns/op
>> JmhDecimalFormat.testNewOnly avgt 50 209.874 ? 9.951 ns/op
>>
>>
>> ### JDK 11
>>
>> Benchmark Mode Cnt Score Error Units
>> JmhDecimalFormat.testFormatOnly avgt 50 364.214 ? 1.191 ns/op
>> JmhDecimalFormat.testNewAndFormat avgt 50 658.699 ? 2.311 ns/op
>> JmhDecimalFormat.testNewOnly avgt 50 248.300 ? 5.158 ns/op
>
> lingjun-cg has updated the pull request incrementally with one additional
> commit since the last revision:
>
> 8333396: Performance regression of DecimalFormat.format
Thanks for all the updates. Looks good to me pending the other's comments.
(BTW, the benchmark can go to -> _test/micro/org/openjdk/bench/java/text/_ as
opposed to _test/jdk/java/text_)
src/java.base/share/classes/java/text/CompactNumberFormat.java line 569:
> 567: @Override
> 568: StringBuilder format(Object number,
> 569: StringBuilder toAppendTo,
nit: indentation seems odd here/not consistent with the other corresponding
method.
src/java.base/share/classes/java/text/SimpleDateFormat.java line 1330:
> 1328:
> 1329: int num = (value / 60) * 100 + (value % 60);
> 1330: if (buffer.isProxyStringBuilder()) {
If we made the implementations of `StringBuf` records, we could use record
patterns and do away with the `isProxyStringBuilder()` method. Although we
would have to change the visibility of the implementations, so maybe not...
src/java.base/share/classes/java/text/StringBufFactory.java line 29:
> 27:
> 28: /**
> 29: * StringBufFactory create {code Format.StringBuf}'s implements that
Nit: Just to improve some grammar, how about something like,
* {@code StringBufFactory} creates implementations of {@code Format.StringBuf},
* which is an interface with the minimum overlap required to support {@code
StringBuffer}
* and {@code StringBuilder} in {@code Format}. This allows for {@code
StringBuilder} to be used
* in place of {@code StringBuffer} to provide performance benefits for JDK
internal
* {@code Format} subclasses.
src/java.base/share/classes/java/text/StringBufFactory.java line 37:
> 35: final class StringBufFactory {
> 36:
> 37: static Format.StringBuf of(StringBuffer sb) {
At least for the factory class, let's just `import
java.text.Format.StringBuf;`, so we don't have to use the full name everywhere.
src/java.base/share/classes/java/text/StringBufFactory.java line 45:
> 43: }
> 44:
> 45: private static class StringBufferImpl implements Format.StringBuf {
The implementations may be more concise as a `record class`
-------------
PR Review: https://git.openjdk.org/jdk/pull/19513#pullrequestreview-2144190566
PR Review Comment: https://git.openjdk.org/jdk/pull/19513#discussion_r1656424171
PR Review Comment: https://git.openjdk.org/jdk/pull/19513#discussion_r1656446980
PR Review Comment: https://git.openjdk.org/jdk/pull/19513#discussion_r1656412306
PR Review Comment: https://git.openjdk.org/jdk/pull/19513#discussion_r1656413186
PR Review Comment: https://git.openjdk.org/jdk/pull/19513#discussion_r1656420349