Skip to content
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

FastISOTimestampFormatter as a fast alternative to Java's DateTimeFormatter for standard ISO formats #711

Merged
merged 7 commits into from
Dec 20, 2021

Conversation

brenuart
Copy link
Collaborator

@brenuart brenuart commented Dec 8, 2021

The FastISOTimestampFormatter is a fast alternative to Java's DateTimeFormatter for the most commonly used ISO formats, i.e.:

  • ISO_OFFSET_DATE_TIME
  • ISO_ZONED_DATE_TIME
  • ISO_LOCAL_DATE_TIME
  • ISO_DATE_TIME
  • ISO_INSTANT

Instead of computing every date/time fields every time it is invoked like the standard DateTimeFormatter does, the fast formatter remembers the previous formatted value and uses it to replace only the second and millis parts if the next timestamp to format is within the same minute.

This new formatter is currently used only when the FormattedTimestampJsonProvider is configured with one of default format listed above (expressed between [] in the configuration). A standard DateTimeFormatter is used for the other standard formats or if a custom pattern is configured (even if the pattern matches one of the supported formats). We can extend support for other formats if needed...

To highlight the benefits I ran a quick benchmark whose only job is to format System.currentTimeMillis() as fast as possible. This test is of course not representative of any real world scenario but it gives a fair overview of the differences between the two approaches.

                           Thrpt (ops/s)      Mem alloc (B/op)
--------------------------------------------------------------
FastISOTimestampFormatter       14212725                    87
DateTimeFormatter                2021980                   973

As we can see, the "fast" formatter is 7x faster and produces 10x less garbage.

I then ran the "LogstashEncoder" benchmark to see how it performs with the new fast formatter. This benchmark invokes the LogstashEncoder as fast as it can during 3 rounds of 10 seconds each. I also ran it against LLE 7.01 and 6.6:

                                          Thrpt (ops/s)    Mem alloc (B/op)
---------------------------------------------------------------------------
7.1-SNAPSHOT (FastISOTimestampFormatter)        1185035                 846
7.0.1        (DateTimeFormatter)                 751877                1755
6.6          (DateTimeFormatter)                 764514                2752

We can see that 7.1-SNAPSHOT is nearly 50% faster than 7.0.1 and produces 50% less garbage.
7.0.1 and 6.6 have roughly the same throughput but 7.0.1 produces less memory allocation. This is due to the reuse of the Jackson JsonFactory introduced in 7.0.


Pending things todo:

  • update README.md
  • update release note

@brenuart brenuart requested a review from philsttr December 8, 2021 18:10
@brenuart brenuart added this to the 7.1 milestone Dec 8, 2021
…eTimeFormatter for standard ISO formats

The FastISOTimestampFormatter is a fast alternative to Java's DateTimeFormatter for the most commonly used ISO formats, i.e.:
- ISO_OFFSET_DATE_TIME
- ISO_ZONED_DATE_TIME
- ISO_LOCAL_DATE_TIME
- ISO_DATE_TIME
- ISO_INSTANT

Instead of computing every date/time fields everytime it is invoked like the standard DateTimeFormatter does, the fast formatter remembers the previous formatted value and uses it to replace only the second and millis parts if the next timestamp to format is within the same minute.
@codecov
Copy link

codecov bot commented Dec 8, 2021

Codecov Report

Merging #711 (c65e2fc) into main (de6021e) will increase coverage by 0.54%.
The diff coverage is 87.09%.

Impacted file tree graph

@@             Coverage Diff              @@
##               main     #711      +/-   ##
============================================
+ Coverage     68.87%   69.42%   +0.54%     
- Complexity     1190     1206      +16     
============================================
  Files           160      161       +1     
  Lines          4623     4719      +96     
  Branches        458      481      +23     
============================================
+ Hits           3184     3276      +92     
+ Misses         1192     1190       -2     
- Partials        247      253       +6     
Impacted Files Coverage Δ
...posite/AbstractFormattedTimestampJsonProvider.java 71.64% <62.50%> (-8.36%) ⬇️
...h/logback/composite/FastISOTimestampFormatter.java 98.80% <98.80%> (ø)
...ck/appender/AbstractLogstashTcpSocketAppender.java 69.23% <0.00%> (-0.52%) ⬇️
...stash/logback/appender/AsyncDisruptorAppender.java 78.21% <0.00%> (+1.98%) ⬆️
.../logback/composite/AbstractNestedJsonProvider.java 86.95% <0.00%> (+13.04%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 0c8ebe9...c65e2fc. Read the comment docs.

…TimestampFormatter

Use a format not supported by the FastISOTimestampFormatter to force the test to go through the reflection code.
@brenuart brenuart requested a review from philsttr December 11, 2021 21:48
Copy link
Collaborator

@philsttr philsttr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for making the suggested changes.

Approved as-is. Just had some comments on minor stuff that I'm not terribly concerned about.

@brenuart
Copy link
Collaborator Author

@philsttr Code always looks obvious to the writer after writing... but 6 months later is another story ;-) So your comments are definitely worth it!

I have another question tho. The "fast" formatter is used only when the FormattedTimestampJsonProvider is explicitly configured with one of the supported [ISO_*] formats (between brackets) - but not when a pattern is used even if this pattern is equivalent to one of the ISO_* formats.

Isn't this confusing for the user? When a pattern is used instead of a format name, shouldn't we try to detect it matches one of the supported formats? This may not be easy tho: what would be the pattern for ISO_INSTANT?

The "fast" formatter can support any format as long as the prefix and suffix parts are stable during one minute, i.e. they are not affected by the second and millis time field. Do you think we should make it more general so it can support arbitrary patterns as long as they match this rule?

@philsttr
Copy link
Collaborator

Do you think we should make it more general so it can support arbitrary patterns as long as they match this rule?

Your choice. I imagine most people are probably using one of the standards already, so I'm not too concerned about supporting arbitrary formats. But it's your call.

@brenuart brenuart merged commit 1341206 into main Dec 20, 2021
@brenuart brenuart deleted the fastdatetimeformat branch December 20, 2021 15:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants