8337279: Share StringBuilder to format instant#20353
8337279: Share StringBuilder to format instant#20353wenshao wants to merge 18 commits intoopenjdk:masterfrom
Conversation
|
👋 Welcome back wenshao! A progress list of the required criteria for merging this PR into |
|
@wenshao 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 613 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 |
|
By removing redundant code logic, the codeSize can be reduced from 444 to 240, and the performance is improved. Below are the performance numbers running on MacBook M1 Pro -# master 5ff7c57f9ff5428ef3d2aedd7e860bb1e8ff29ea
-Benchmark Mode Cnt Score Error Units
-ToStringBench.instantToString thrpt 15 4.734 ? 0.547 ops/ms +24.10%
+# current baf0f9ca83c111401d6e034dfba0ee2c71280e62
+Benchmark Mode Cnt Score Error Units
+ToStringBench.instantToString thrpt 15 5.875 ? 0.301 ops/ms |
Webrevs
|
| } | ||
| // add fraction | ||
| if ((fractionalDigits < 0 && inNano > 0) || fractionalDigits > 0) { | ||
| if (fractionalDigits > 0) { |
There was a problem hiding this comment.
This breaks the logic. fractionalDigits can be negative in the block below
There was a problem hiding this comment.
If fractionalDigits < 0, printNano is implemented in LocalDateTime
There was a problem hiding this comment.
fractionalDigits == -2 is used to output 0, 3, 6 or 9 fractional digits as needed. This can be handled by LocalDateTime.
fractionalDigits == -1 is used to output as many fractional digits as needed, from 0 to 9 digits. This is NOT handled by LocalDateTime.
Furthermore, if the -2 branch is handled by LocalDateTime then the logic below for -2 is redundant.
If the tests did not fail as a result of this change, then I imagine the tests need improving.
|
@jodastephen @wenshao Could you please review these proposed changes for instantiation of |
@plokhotnyuk Your contribution cannot be acted upon until the OCA is confirmed. |
@RogerRiggs Thanks for your support! I've submitted a signed OCA more than year ago and nobody answered me yet. |
| } | ||
| // add fraction | ||
| if ((fractionalDigits < 0 && inNano > 0) || fractionalDigits > 0) { | ||
| if (fractionalDigits > 0) { |
There was a problem hiding this comment.
fractionalDigits == -2 is used to output 0, 3, 6 or 9 fractional digits as needed. This can be handled by LocalDateTime.
fractionalDigits == -1 is used to output as many fractional digits as needed, from 0 to 9 digits. This is NOT handled by LocalDateTime.
Furthermore, if the -2 branch is handled by LocalDateTime then the logic below for -2 is redundant.
If the tests did not fail as a result of this change, then I imagine the tests need improving.
| } | ||
| } | ||
| // use LocalDateTime.toString, If fractionalDigits < 0, printNano is implemented in LocalDateTime | ||
| LocalDateTime ldt = LocalDateTime.ofEpochSecond(inSecs, fractionalDigits >= 0 ? 0 : inNano, ZoneOffset.UTC); |
There was a problem hiding this comment.
I believe this whole approach is flawed. The comment above says:
// use INSTANT_SECONDS, thus this code is not bound by Instant.MAX
which indicates that the instant value may not fit in LocalDateTime.
You may be able to special case certain code paths to use LocalDateTime, but that makes things more complex, and not necessarily faster.
2. Split two methods to make codeSize less than 325
|
@jodastephen thank you for your patience in answering my questions. Now I've used a new way to implement it, splitting it into two sub-methods without changing the original logic, which also makes codeSize < 325, and the performance is also faster than before. Here are the performance numbers running on a MacBook M1 Pro: - # master 5ff7c57f9ff5428ef3d2aedd7e860bb1e8ff29ea
-Benchmark Mode Cnt Score Error Units
-ToStringBench.instantToString thrpt 15 4.558 ? 0.561 ops/ms
+ # current 757984258257fd82c389f3ee7bd9be927375e2e0
+Benchmark Mode Cnt Score Error Units
+ToStringBench.instantToString thrpt 15 5.564 ? 0.465 ops/ms +22.07%I also added relevant tests to prevent errors from being discovered in future related changes. I hope to reuse LocalDateTime.toString for the most common paths, so that we can also use LocalDateTime.formatTo method, which can reduce object allocation. I plan to continue to optimize the formatTo method of LocalTime and LocalDate, so that InstantPrinterParser.format can also benefit from it. |
jodastephen
left a comment
There was a problem hiding this comment.
I'm still skeptical of some parts of this PR as it makes the code harder to folow. The new tests are a good addition and should be merged.
Have you tried the performance of simply breaking out the currentEra/beforeCurrentEra methods without making any other changes? Since the logic to produce the nano fraction is going to stay in this method, I don't really see the advantage in trying to get LocalDateTime to do the fraction some of the time.
src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java
Outdated
Show resolved
Hide resolved
…Builder.java Co-authored-by: Stephen Colebourne <scolebourne@joda.org>
|
A more productive direction might be to move |
git remote add wenshao git@github.com:wenshao/jdk.git
git fetch wenshao
# baseline with test
git checkout 85ee5de07dd9f45a4e1659e17f9a52e69fa77df3
make test TEST="micro:java.time.ToStringBench.instantToString"
Benchmark Mode Cnt Score Error Units
ToStringBench.instantToString thrpt 15 4.749 ? 0.624 ops/ms
# current version
git checkout 875666143e6d717634f2a3cc3c397b2ca8b63e63
make test TEST="micro:java.time.ToStringBench.instantToString"
Benchmark Mode Cnt Score Error Units
ToStringBench.instantToString thrpt 15 5.713 ? 0.474 ops/ms +20.29%
# baseline with simply breaking out the currentEra/beforeCurrentEra methods without making any other changes
git fbf66307f738cd40e061c6d26fa05c062ccd048b
make test TEST="micro:java.time.ToStringBench.instantToString"
Benchmark Mode Cnt Score Error Units
ToStringBench.instantToString thrpt 15 4.716 ? 0.569 ops/ms -0.69%In the branch The result is somewhat unexpected. I thought the performance improvement was mainly due to codeSize < 325. The specific reason needs further analysis. |
I prefer to provide JavaTimeAccess in SharedSecrets, which will require less changes. This depends on #20368 |
jodastephen
left a comment
There was a problem hiding this comment.
The change looks OK from my perspective, however I would want someone with experience of SharedSecrets to comment
|
/open |
|
@wenshao This pull request is now open |
| * or visit www.oracle.com if you need additional information or have any | ||
| * questions. | ||
| */ | ||
| package jdk.internal.util; |
There was a problem hiding this comment.
Maybe jdk.internal.time would be appropriate.
There was a problem hiding this comment.
Currently, since we only have the DateTimeHelper class, we can wait until there are two or more classes before creating the jdk.internal.time package.
There was a problem hiding this comment.
This utility class can be final, and have a private default constructor so that no instance can be created.
| import static java.time.LocalTime.NANOS_PER_SECOND; | ||
| import static java.time.LocalTime.SECONDS_PER_DAY; | ||
| import static java.time.temporal.ChronoField.NANO_OF_SECOND; | ||
| import static jdk.internal.util.DateTimeHelper.formatTo; |
There was a problem hiding this comment.
Do not import static across package boundaries. I discourage it even within the same package.
It makes the code hard to read and follow where the function is coming from.
@naotoj |
naotoj
left a comment
There was a problem hiding this comment.
Looks good with some minor nits
| * or visit www.oracle.com if you need additional information or have any | ||
| * questions. | ||
| */ | ||
| package jdk.internal.util; |
There was a problem hiding this comment.
This utility class can be final, and have a private default constructor so that no instance can be created.
| bh.consume(instant.toString()); | ||
| } | ||
| } | ||
| } No newline at end of file |
|
Thank you @naotoj, these have been fixed, please help review again |
naotoj
left a comment
There was a problem hiding this comment.
Looks good. I confirmed the changes passed tier1-3 tests.
|
/reviewers 2 reviewer |
|
@AlanBateman |
|
@wenshao 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 alive. |
liach
left a comment
There was a problem hiding this comment.
Refactor looks harmless. Optimization is splitting methods to help profiling and avoid one string copy in ZonedDateTime.
|
/integrate |
|
Going to push as commit 7442039.
Your commit was automatically rebased without conflicts. |

Progress
Issue
Reviewers
Reviewing
Using
gitCheckout this PR locally:
$ git fetch https://git.openjdk.org/jdk.git pull/20353/head:pull/20353$ git checkout pull/20353Update a local copy of the PR:
$ git checkout pull/20353$ git pull https://git.openjdk.org/jdk.git pull/20353/headUsing Skara CLI tools
Checkout this PR locally:
$ git pr checkout 20353View PR using the GUI difftool:
$ git pr show -t 20353Using diff file
Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/20353.diff
Using Webrev
Link to Webrev Comment