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
src/java.base/share/classes/java/text/Format.java line 193:
> 191:
> 192: StringBuilder format(Object obj,
> 193: StringBuilder toAppendTo,
Can we change this type to `StringBuf`, like `StringBuf format(Object obj,
StringBuf toAppendTo, FieldPosition pos)`? Then we can remove a few
`StringBuilder` overloads by just using the `StringBuf` version.
src/java.base/share/classes/java/text/NumberFormat.java line 424:
> 422:
> 423: StringBuilder format(double number,
> 424: StringBuilder toAppendTo,
Same remark, `StringBuf format(double number, StringBuf toAppendTo,
FieldPosition pos)`
src/java.base/share/classes/java/text/SimpleDateFormat.java line 1034:
> 1032: @Override
> 1033: public AttributedCharacterIterator formatToCharacterIterator(Object
> obj) {
> 1034: StringBuilder sb = new StringBuilder();
On second thought, we can just make this `StringBuf sb =
StringBufFactory.of(new StringBuilder())` - or just add `StringBufFactory.of()`
which defaults to create a StringBuilder-backed buf.
src/java.base/share/classes/java/text/SimpleDateFormat.java line 1448:
> 1446: numberFormat.setMaximumIntegerDigits(maxDigits);
> 1447: if (buffer.isProxyStringBuilder()) {
> 1448: numberFormat.format((long)value, buffer.asStringBuilder(),
> DontCareFieldPosition.INSTANCE);
This `numberFormat` might be a user class; if that's the case, we might do
something like:
if (buffer.isProxyStringBuilder()) {
var nf = numberFormat; // field is protected, users can change it even with
races!
if (nf.isInternalSubclass()) {
nf.format((long)value, buffer.asStringBuilder(),
DontCareFieldPosition.INSTANCE);
} else {
// format to stringbuffer, and add that buffer to the builder
buffer.append(nf.format((long)value, new StringBuffer(),
DontCareFieldPosition.INSTANCE));
}
} else { // existing code
-------------
PR Review Comment: https://git.openjdk.org/jdk/pull/19513#discussion_r1655595282
PR Review Comment: https://git.openjdk.org/jdk/pull/19513#discussion_r1655596623
PR Review Comment: https://git.openjdk.org/jdk/pull/19513#discussion_r1655605616
PR Review Comment: https://git.openjdk.org/jdk/pull/19513#discussion_r1655603917