Re: RFR: 8333396: Performance regression of DecimalFormat.format [v16]
On Fri, 5 Jul 2024 16:54:41 GMT, Justin Lu wrote: > Following up on my previous comment regarding the _untrue wording_. > > I no longer think we can just split the implementation and specification > portion of this PR into two issues, since if we backport the implementation > only, the specification would now be violated for the back-ported releases. > We may have to just include the specification in this issue as well, (which > will require a CSR and no longer means we can backport this change). A CSR request created: https://bugs.openjdk.org/browse/JDK-8335834 - PR Comment: https://git.openjdk.org/jdk/pull/19513#issuecomment-2212946822
Re: RFR: 8333396: Performance regression of DecimalFormat.format [v16]
On Tue, 2 Jul 2024 02:13:50 GMT, lingjun-cg 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.0"); >> } >> >> @Benchmark >> public void testNewAndFormat() throws InterruptedException { >> new DecimalFormat("#0.0").format(9524234.1236457); >> } >> >> @Benchmark >> public void testNewOnly() throws InterruptedException { >> new DecimalFormat("#0.0"); >> } >> >> @Benchmark >> public void testFormatOnly() throws InterruptedException { >> format.format(9524234.1236457); >> } >> } >> >> >> ### Test result >> Current JDK before optimize >> >> Benchmark Mode CntScore 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 CntScore Error Units >> JmhDecimalFormat.testFormatOnlyavgt 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 CntScore Error Units >> JmhDecimalFormat.testFormatOnlyavgt 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: > > 896: Performance regression of DecimalFormat.format Following up on my previous comment regarding the _untrue wording_. I no longer think we can just split the implementation and specification portion of this PR into two issues, since if we backport the implementation only, the specification would now be violated for the back-ported releases. We may have to just include the specification in this issue as well, (which will require a CSR and no longer means we can backport this change). - Changes requested by jlu (Committer). PR Review: https://git.openjdk.org/jdk/pull/19513#pullrequestreview-2161032118
Re: RFR: 8333396: Performance regression of DecimalFormat.format [v16]
On Tue, 2 Jul 2024 02:13:50 GMT, lingjun-cg 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.0"); >> } >> >> @Benchmark >> public void testNewAndFormat() throws InterruptedException { >> new DecimalFormat("#0.0").format(9524234.1236457); >> } >> >> @Benchmark >> public void testNewOnly() throws InterruptedException { >> new DecimalFormat("#0.0"); >> } >> >> @Benchmark >> public void testFormatOnly() throws InterruptedException { >> format.format(9524234.1236457); >> } >> } >> >> >> ### Test result >> Current JDK before optimize >> >> Benchmark Mode CntScore 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 CntScore Error Units >> JmhDecimalFormat.testFormatOnlyavgt 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 CntScore Error Units >> JmhDecimalFormat.testFormatOnlyavgt 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: > > 896: Performance regression of DecimalFormat.format Thanks @lingjun-cg for your updates. I went through the newest version and the implementation changes look OK to me. It is a little easier to review without the intermediate StringBuilder method. It is a large change, so it is good there are multiple sets of eyes here. We will of course still need approval from a **Reviewer**. Something to note is that there is now some wording in the specification of some java.text.Format.* classes that is technically untrue. For example, in `Format.format(Object obj)`, it states the method is equivalent to calling `Format.format(obj, new StringBuffer(), new FieldPosition(0)).toString())`. It is not the biggest deal since it is just minor supplementary info and can probably simply be removed. However, this should be done as a separate issue, since we would like to backport the changes in this PR. - Marked as reviewed by jlu (Committer). PR Review: https://git.openjdk.org/jdk/pull/19513#pullrequestreview-2158180263
Re: RFR: 8333396: Performance regression of DecimalFormat.format [v16]
> ### 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.0"); > } > > @Benchmark > public void testNewAndFormat() throws InterruptedException { > new DecimalFormat("#0.0").format(9524234.1236457); > } > > @Benchmark > public void testNewOnly() throws InterruptedException { > new DecimalFormat("#0.0"); > } > > @Benchmark > public void testFormatOnly() throws InterruptedException { > format.format(9524234.1236457); > } > } > > > ### Test result > Current JDK before optimize > > Benchmark Mode CntScore 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 CntScore Error Units > JmhDecimalFormat.testFormatOnlyavgt 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 CntScore Error Units > JmhDecimalFormat.testFormatOnlyavgt 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: 896: Performance regression of DecimalFormat.format - Changes: - all: https://git.openjdk.org/jdk/pull/19513/files - new: https://git.openjdk.org/jdk/pull/19513/files/da09d85a..7122c2c8 Webrevs: - full: https://webrevs.openjdk.org/?repo=jdk&pr=19513&range=15 - incr: https://webrevs.openjdk.org/?repo=jdk&pr=19513&range=14-15 Stats: 6 lines in 2 files changed: 0 ins; 0 del; 6 mod Patch: https://git.openjdk.org/jdk/pull/19513.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/19513/head:pull/19513 PR: https://git.openjdk.org/jdk/pull/19513