On Wed, 26 Jun 2024 09:43:32 GMT, lingjun-cg <d...@openjdk.org> 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