-
Notifications
You must be signed in to change notification settings - Fork 6.1k
8358880: Performance of parsing with DecimalFormat can be improved #25644
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
j3graham
wants to merge
11
commits into
openjdk:master
Choose a base branch
from
j3graham:digitlist-getdouble-get-long
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+137
−59
Open
Changes from 8 commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
8bf1f61
improve getDouble, getLong
j3graham 9644568
copyright dates
j3graham bcac896
update comment
j3graham e13dea8
simplify comparison
j3graham a6978bd
simplify getBigDecimal
j3graham a85ddd8
Merge branch 'openjdk:master' into digitlist-getdouble-get-long
j3graham da9e4ae
catch ArithmeticException
j3graham 6953dcf
Address review comments
j3graham 6a07287
fix overflow check
j3graham c87a3de
Revert "fix overflow check"
j3graham b7faa3b
add comments
j3graham File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
89 changes: 89 additions & 0 deletions
89
test/micro/org/openjdk/bench/java/text/DecimalFormatParseBench.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
/* | ||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. | ||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
* | ||
* This code is free software; you can redistribute it and/or modify it | ||
* under the terms of the GNU General Public License version 2 only, as | ||
* published by the Free Software Foundation. | ||
* | ||
* This code is distributed in the hope that it will be useful, but WITHOUT | ||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
* version 2 for more details (a copy is included in the LICENSE file that | ||
* accompanied this code). | ||
* | ||
* You should have received a copy of the GNU General Public License version | ||
* 2 along with this work; if not, write to the Free Software Foundation, | ||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
* | ||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
* or visit www.oracle.com if you need additional information or have any | ||
* questions. | ||
*/ | ||
package org.openjdk.bench.java.text; | ||
|
||
import java.text.DecimalFormat; | ||
import java.text.ParseException; | ||
import java.util.concurrent.TimeUnit; | ||
|
||
import org.openjdk.jmh.annotations.Benchmark; | ||
import org.openjdk.jmh.annotations.BenchmarkMode; | ||
import org.openjdk.jmh.annotations.Fork; | ||
import org.openjdk.jmh.annotations.Measurement; | ||
import org.openjdk.jmh.annotations.Mode; | ||
import org.openjdk.jmh.annotations.OperationsPerInvocation; | ||
import org.openjdk.jmh.annotations.OutputTimeUnit; | ||
import org.openjdk.jmh.annotations.Scope; | ||
import org.openjdk.jmh.annotations.Setup; | ||
import org.openjdk.jmh.annotations.State; | ||
import org.openjdk.jmh.annotations.Warmup; | ||
import org.openjdk.jmh.infra.Blackhole; | ||
import org.openjdk.jmh.runner.Runner; | ||
import org.openjdk.jmh.runner.options.Options; | ||
import org.openjdk.jmh.runner.options.OptionsBuilder; | ||
|
||
@BenchmarkMode(Mode.Throughput) | ||
@OutputTimeUnit(TimeUnit.MILLISECONDS) | ||
@Warmup(iterations = 5, time = 1) | ||
@Measurement(iterations = 5, time = 1) | ||
@Fork(3) | ||
@State(Scope.Benchmark) | ||
public class DecimalFormatParseBench { | ||
|
||
public String[] valuesLong; | ||
public String[] valuesDouble; | ||
|
||
@Setup | ||
public void setup() { | ||
valuesLong = new String[]{ | ||
"123", "149", "180", "170000000000000000", "0", "-149", "-15000", "99999123", "1494", "1495", "1030", "25996", "-25996" | ||
}; | ||
|
||
valuesDouble = new String[]{ | ||
"1.23", "1.49", "1.80", "17000000000000000.1", "0.01", "-1.49", "-1.50", "9999.9123", "1.494", "1.495", "1.03", "25.996", "-25.996" | ||
}; | ||
} | ||
|
||
private DecimalFormat dnf = new DecimalFormat(); | ||
|
||
@Benchmark | ||
@OperationsPerInvocation(13) | ||
public void testParseLongs(final Blackhole blackhole) throws ParseException { | ||
for (String value : valuesLong) { | ||
blackhole.consume(this.dnf.parse(value)); | ||
} | ||
} | ||
|
||
@Benchmark | ||
@OperationsPerInvocation(13) | ||
public void testParseDoubles(final Blackhole blackhole) throws ParseException { | ||
for (String value : valuesDouble) { | ||
blackhole.consume(this.dnf.parse(value)); | ||
} | ||
} | ||
public static void main(String... args) throws Exception { | ||
Options opts = new OptionsBuilder().include(DefFormatterBench.class.getSimpleName()).shouldDoGC(true).build(); | ||
new Runner(opts).run(); | ||
} | ||
|
||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this check needed? I think
ASCIIToBinaryConverter
will return the proper zero value whendoubleValue()
is invoked.And if this explicit check is a shortcut, I don't think we would need one for an edge case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately some check is required (a test fails), but I now realize what I had was wrong. The issue is that on line 1084 (https://github.com/openjdk/jdk/pull/25644/files#diff-79e6fd549b5ec5e7f49658581beddcb07fcbb0c09ae8e1117c385b66514da6d2R1084)) exp can overflow and become positive again. I've updated the check to avoid the overflow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah got it, I see your point. We would have goten underflow in
ASCIIToBinaryConverter.doubleValue()
for some extreme cases without a check.Is there a specific example you have that requires the switch to the newer check? Adding a comment along those lines might be helpful. Actually, I thought DigitList caps
decimalAt
to Integer.MIN/MAX, so then the first check you had would have been fine. (Maybe I am missing something?)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have a specific example, so I've reverted to my original check. I'm a bit unsettled by the check for an extreme value later in
doubleValue()
comparing againstMIN_DECIMAL_EXPONENT - 1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO, the original check you had is easier to understand what is happening without further context, so I prefer your switch back.
I think we are fine from (negative) "extreme values" in
doubleValue()
because of the check you have implemented in the first place. i.e. we avoid any potential underflow fromint exp = decExponent - kDigits;
. I think we do need a comment to accompany the check. (Why do we check? why not check the max exponent value?)Also, should the check be against
MIN_DECIMAL_EXPONENT - 1
for consistency withdoubleValue()
? (Functionally, I don't think it matters.)