-
Notifications
You must be signed in to change notification settings - Fork 6.2k
8205592: BigDecimal.doubleValue() is depressingly slow #9410
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
Conversation
|
👋 Welcome back rgiulietti! A progress list of the required criteria for merging this PR into |
|
@rgiulietti The following label will be automatically applied to this pull request:
When this pull request is ready to be reviewed, an "RFR" email will be sent to the corresponding mailing list. If you would like to change these labels, use the /label pull request command. |
|
These are the improvements over the current implementation:
JMH benchmarks show that, on the fast path, the new implementation is on par or way better (>200x for the cases not currently covered). |
Webrevs
|
|
Impressive performance improvements, I also like the PR title :P |
|
@TheShermanTanker |
|
@rgiulietti This pull request has been inactive for more than 4 weeks and will be automatically closed if another 4 weeks passes without any activity. To avoid this, simply add a new comment to the pull request. Feel free to ask for assistance if you need help with progressing this pull request towards integration! |
|
keep open |
|
@rgiulietti This pull request has been inactive for more than 4 weeks and will be automatically closed if another 4 weeks passes without any activity. To avoid this, simply add a new comment to the pull request. Feel free to ask for assistance if you need help with progressing this pull request towards integration! |
|
keep open, waiting for review |
|
@rgiulietti This pull request has been inactive for more than 4 weeks and will be automatically closed if another 4 weeks passes without any activity. To avoid this, simply add a new comment to the pull request. Feel free to ask for assistance if you need help with progressing this pull request towards integration! |
|
waiting for review |
|
@rgiulietti This pull request has been inactive for more than 4 weeks and will be automatically closed if another 4 weeks passes without any activity. To avoid this, simply add a new comment to the pull request. Feel free to ask for assistance if you need help with progressing this pull request towards integration! |
|
waiting for review |
|
@rgiulietti This pull request has been inactive for more than 4 weeks and will be automatically closed if another 4 weeks passes without any activity. To avoid this, simply add a new comment to the pull request. Feel free to ask for assistance if you need help with progressing this pull request towards integration! |
|
This PR is waiting for a review |
|
@rgiulietti Thanks for keeping making JDK faster! I have a couple of review comments: 1) For the line https://github.com/openjdk/jdk/pull/9410/files#diff-94d400b99466045dd76001c37eada6b24d086d8d115b49c439752bbceb233772L3746It seems that removing this check for 22-bit number introduces an extra rounding error. Please consider just increasing the constant up to Bellow are REPL expressions that illustrate my thoughts (sorry for using Scala): 2) For the line: https://github.com/openjdk/jdk/pull/9410/files#diff-94d400b99466045dd76001c37eada6b24d086d8d115b49c439752bbceb233772L3791It seems that removing this check for 52-bit number introduces an extra rounding error: 3) For the line: https://github.com/openjdk/jdk/pull/9410/files#diff-94d400b99466045dd76001c37eada6b24d086d8d115b49c439752bbceb233772L3800Here before falling back to the costly fallback with big number operations we can get a number of digits in intCompact and use a trick with two multiplications |
|
The expression We have: L.3838 sets On the other hand, the expression A similar analysis holds for the As for the last paragraph, I'll have to have a deeper look. Stay tuned ;-) |
|
Sorry, I overlooked those checks two times :) How about adding a moderate path like this? I think it worth do be reused for regular parsing of |
|
@plokhotnyuk The main goal of this PR is to avoid generating a string and parse it, as it happens in the current implementation. The fact that it results in being faster is only a welcome byproduct. The proposed patch for Adding the "moderate path" to this patch would increase the code size considerably. Moreover, I would have to invest time to understand the dense, uncommented code and convince myself and the reviewers that it is correct. I would also have to setup benchmarks to measure the overall benefits of adding it to the proposed patch. And add specific tests to cover the path. Before that, I would prefer for this patch to be first reviewed as it is (with possible corrections). I hope to have time to invest into your proposals once this PR is integrated into mainline. Thanks for your patience. |
|
@rgiulietti Thanks for the explanation! I wish faster reviews of all your PRs! I bet that investigation of the moderate path will pay itself when that path will be reused for improving of You can roughly estimate the moderate path speed up comparing the throughput of BTW, the P.S. Here is the setup method of the corresponding benchmark. |
|
@rgiulietti This pull request has been inactive for more than 4 weeks and will be automatically closed if another 4 weeks passes without any activity. To avoid this, simply add a new comment to the pull request. Feel free to ask for assistance if you need help with progressing this pull request towards integration! |
|
Keep it open |
| public class BigDecimal extends Number implements Comparable<BigDecimal> { | ||
| /* | ||
| * Let l = log_2(10). | ||
| * Then, L < l < L + ulp(L) / 2, that is, L = roundTiesToEven(l). |
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.
It doesn't matter in terms of the code, but shouldn't this be something like:
L - (ulp(L)) < l < L ulp(L)
In other words, without further checking, it isn't clear that L is the lower-bound of the two double value bracketing l.
(If the ulp function being discussed were the real-valued version than L +/- ulp(l) would also be a reasonable formulation.)
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.
While it isn't obvious from the definition, high precision computations reveal that the inequalities L < l < L + ulp(L) / 2 do indeed hold.
But as noted, this is not really relevant for the rest of the analysis.
| if (intCompact == 0) { | ||
| return 0.0f; | ||
| } | ||
| BigInteger d = unscaledValue().abs(); |
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'd prefer a name other than "d" be used for the BigInteger significand's magnitude.
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 have no objections as long as it is a 1 letter name that does not conflict with others.
Have you a preferred one?
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 have no objections as long as it is a 1 letter name that does not conflict with others. Have you a preferred one?
I usually use "bd" or "bi" for such BigDecima/BigInteger variables myself and use "d" for double; "v" or "w" have integer-ish feel to me so perhaps one of those?
| @Override | ||
| public float floatValue(){ | ||
| if(intCompact != INFLATED) { | ||
| public float floatValue() { |
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.
If the total size of code were a concern, I wouldn't be adverse to floatValue() being implemented as
return (float)doubleValue();
Given the 2p+2 property between the float and double formats, BigDecimal -> double -> float and BigDecimal -> float round the same way.
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.
No, that would not be correct. It would be subject to double rounding, against the spec.
For example, BigDecimal 1.000000059604644775390626 should round to float 1.0000001f.
When going to the closest double and then to the closest float, however, it first rounds to double 1.0000000596046448 and the to float 1.0f.
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.
No, that would not be correct. It would be subject to double rounding, against the spec.
For example,
BigDecimal1.000000059604644775390626 should round tofloat1.0000001f. When going to the closestdoubleand then to the closestfloat, however, it first rounds todouble1.0000000596046448and the tofloat1.0f.
Ah right; thanks for the correction -- since the set of possible inputs in this case isn't constrained by operations on float values, the 2p+2 property that hold for {+, -, *, /, sqrt} doesn't hold here.
|
@rgiulietti This change now passes all automated pre-integration checks. ℹ️ This project also has non-automated pre-integration requirements. Please see the file CONTRIBUTING.md for details. After integration, the commit message for the final commit will be: You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed. At the time when this comment was updated there had been 3887 new commits pushed to the
As there are no conflicts, your changes will automatically be rebased on top of these commits when integrating. If you prefer to avoid this automatic rebasing, please check the documentation for the /integrate command for further details. ➡️ To integrate this PR with the above commit message to the |
|
/integrate |
|
Going to push as commit eb35861.
Your commit was automatically rebased without conflicts. |
|
@rgiulietti Pushed as commit eb35861. 💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored. |

A reimplementation of
BigDecimal.[double|float]Value()to enhance performance, avoiding an intermediate string and its subsequent parsing on the slow path.Progress
Issue
Reviewers
Reviewing
Using
gitCheckout this PR locally:
$ git fetch https://git.openjdk.org/jdk.git pull/9410/head:pull/9410$ git checkout pull/9410Update a local copy of the PR:
$ git checkout pull/9410$ git pull https://git.openjdk.org/jdk.git pull/9410/headUsing Skara CLI tools
Checkout this PR locally:
$ git pr checkout 9410View PR using the GUI difftool:
$ git pr show -t 9410Using diff file
Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/9410.diff
Webrev
Link to Webrev Comment