Skip to content

Instantly share code, notes, and snippets.

@odrotbohm
Last active November 28, 2016 12:38
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save odrotbohm/487d58bd146a47574fb5a8316f950b04 to your computer and use it in GitHub Desktop.
Save odrotbohm/487d58bd146a47574fb5a8316f950b04 to your computer and use it in GitHub Desktop.
Benchmark showing differences between formatting JDK 8 dates and legacy Date instances
package org.sample;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.TimeZone;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
public class MyBenchmark {
@State(Scope.Benchmark)
public static class LegacyDateState {
private final DateFormat format;
private final Date date;
public LegacyDateState() {
this.format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
this.format.setTimeZone(TimeZone.getTimeZone("UTC"));
this.date = new Date();
}
}
@State(Scope.Benchmark)
public static class Jdk8DateState {
volatile LocalDateTime now = LocalDateTime.now();
volatile DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
}
@State(Scope.Benchmark)
public static class Jdk8DateStateWithConstant {
volatile LocalDateTime now = LocalDateTime.now();
volatile DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME;
}
@Benchmark
public void formatLegacyDate(LegacyDateState state) {
state.format.format(state.date);
}
@Benchmark
public void formatJdk8Date(Jdk8DateState state) {
state.formatter.format(state.now);
}
@Benchmark
public void formatJdk8DateWithConstant(Jdk8DateStateWithConstant state) {
state.formatter.format(state.now);
}
}
$ java -jar target/benchmarks.jar -wi 10 -i 10 -f 3
# JMH 1.16 (released 17 days ago)
# VM version: JDK 1.8.0_112, VM 25.112-b16
# VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0_112.jdk/Contents/Home/jre/bin/java
# VM options: <none>
# Warmup: 10 iterations, 1 s each
# Measurement: 10 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: org.sample.MyBenchmark.formatJdk8Date
# Run progress: 0,00% complete, ETA 00:03:00
# Fork: 1 of 3
# Warmup Iteration 1: 1684192,789 ops/s
# Warmup Iteration 2: 1686202,091 ops/s
# Warmup Iteration 3: 1464344,370 ops/s
# Warmup Iteration 4: 1759962,170 ops/s
# Warmup Iteration 5: 1994955,274 ops/s
# Warmup Iteration 6: 2102568,510 ops/s
# Warmup Iteration 7: 2095488,529 ops/s
# Warmup Iteration 8: 2108256,666 ops/s
# Warmup Iteration 9: 2111502,802 ops/s
# Warmup Iteration 10: 1418172,189 ops/s
Iteration 1: 1997149,166 ops/s
Iteration 2: 2111866,469 ops/s
Iteration 3: 2140621,185 ops/s
Iteration 4: 2100821,614 ops/s
Iteration 5: 2121535,606 ops/s
Iteration 6: 2028756,643 ops/s
Iteration 7: 1701779,072 ops/s
Iteration 8: 2014830,453 ops/s
Iteration 9: 2061489,624 ops/s
Iteration 10: 1954533,519 ops/s
# Run progress: 11,11% complete, ETA 00:02:45
# Fork: 2 of 3
# Warmup Iteration 1: 1416393,987 ops/s
# Warmup Iteration 2: 1733252,638 ops/s
# Warmup Iteration 3: 1680686,365 ops/s
# Warmup Iteration 4: 1978360,537 ops/s
# Warmup Iteration 5: 2013851,481 ops/s
# Warmup Iteration 6: 2034086,186 ops/s
# Warmup Iteration 7: 1985682,880 ops/s
# Warmup Iteration 8: 1736872,111 ops/s
# Warmup Iteration 9: 2026089,664 ops/s
# Warmup Iteration 10: 1531258,871 ops/s
Iteration 1: 1558250,045 ops/s
Iteration 2: 1869779,437 ops/s
Iteration 3: 2022251,593 ops/s
Iteration 4: 2017906,258 ops/s
Iteration 5: 2025278,466 ops/s
Iteration 6: 1985557,002 ops/s
Iteration 7: 1362722,742 ops/s
Iteration 8: 1251507,524 ops/s
Iteration 9: 1655907,024 ops/s
Iteration 10: 1932596,109 ops/s
# Run progress: 22,22% complete, ETA 00:02:24
# Fork: 3 of 3
# Warmup Iteration 1: 1188103,490 ops/s
# Warmup Iteration 2: 1761795,965 ops/s
# Warmup Iteration 3: 1588427,057 ops/s
# Warmup Iteration 4: 1630942,379 ops/s
# Warmup Iteration 5: 1990693,450 ops/s
# Warmup Iteration 6: 1977035,232 ops/s
# Warmup Iteration 7: 1974555,591 ops/s
# Warmup Iteration 8: 2062885,267 ops/s
# Warmup Iteration 9: 1956363,451 ops/s
# Warmup Iteration 10: 1665759,371 ops/s
Iteration 1: 1444716,229 ops/s
Iteration 2: 1894498,774 ops/s
Iteration 3: 2053270,523 ops/s
Iteration 4: 2056347,853 ops/s
Iteration 5: 2008202,462 ops/s
Iteration 6: 2053475,444 ops/s
Iteration 7: 1401129,059 ops/s
Iteration 8: 1773503,351 ops/s
Iteration 9: 2017938,564 ops/s
Iteration 10: 1972053,954 ops/s
Result "formatJdk8Date":
1886342,526 ±(99.9%) 166823,201 ops/s [Average]
(min, avg, max) = (1251507,524, 1886342,526, 2140621,185), stdev = 249693,132
CI (99.9%): [1719519,325, 2053165,726] (assumes normal distribution)
# JMH 1.16 (released 17 days ago)
# VM version: JDK 1.8.0_112, VM 25.112-b16
# VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0_112.jdk/Contents/Home/jre/bin/java
# VM options: <none>
# Warmup: 10 iterations, 1 s each
# Measurement: 10 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: org.sample.MyBenchmark.formatJdk8DateWithConstant
# Run progress: 33,33% complete, ETA 00:02:03
# Fork: 1 of 3
# Warmup Iteration 1: 259264,260 ops/s
# Warmup Iteration 2: 309792,258 ops/s
# Warmup Iteration 3: 299717,069 ops/s
# Warmup Iteration 4: 335683,471 ops/s
# Warmup Iteration 5: 356124,256 ops/s
# Warmup Iteration 6: 364178,229 ops/s
# Warmup Iteration 7: 368815,564 ops/s
# Warmup Iteration 8: 372435,154 ops/s
# Warmup Iteration 9: 349236,714 ops/s
# Warmup Iteration 10: 289359,626 ops/s
Iteration 1: 328938,994 ops/s
Iteration 2: 286539,935 ops/s
Iteration 3: 368033,224 ops/s
Iteration 4: 345386,619 ops/s
Iteration 5: 365218,450 ops/s
Iteration 6: 352205,647 ops/s
Iteration 7: 314011,655 ops/s
Iteration 8: 367909,510 ops/s
Iteration 9: 386295,095 ops/s
Iteration 10: 382388,971 ops/s
# Run progress: 44,44% complete, ETA 00:01:43
# Fork: 2 of 3
# Warmup Iteration 1: 274785,956 ops/s
# Warmup Iteration 2: 261040,612 ops/s
# Warmup Iteration 3: 278998,742 ops/s
# Warmup Iteration 4: 356453,013 ops/s
# Warmup Iteration 5: 374929,084 ops/s
# Warmup Iteration 6: 370160,348 ops/s
# Warmup Iteration 7: 374745,342 ops/s
# Warmup Iteration 8: 381081,191 ops/s
# Warmup Iteration 9: 370130,363 ops/s
# Warmup Iteration 10: 329296,221 ops/s
Iteration 1: 367806,437 ops/s
Iteration 2: 373774,662 ops/s
Iteration 3: 371254,588 ops/s
Iteration 4: 320872,273 ops/s
Iteration 5: 353666,236 ops/s
Iteration 6: 306398,556 ops/s
Iteration 7: 357973,373 ops/s
Iteration 8: 406654,190 ops/s
Iteration 9: 403319,415 ops/s
Iteration 10: 409396,011 ops/s
# Run progress: 55,56% complete, ETA 00:01:22
# Fork: 3 of 3
# Warmup Iteration 1: 265749,479 ops/s
# Warmup Iteration 2: 255471,703 ops/s
# Warmup Iteration 3: 214959,264 ops/s
# Warmup Iteration 4: 293742,177 ops/s
# Warmup Iteration 5: 306066,527 ops/s
# Warmup Iteration 6: 292562,147 ops/s
# Warmup Iteration 7: 296701,359 ops/s
# Warmup Iteration 8: 294786,427 ops/s
# Warmup Iteration 9: 294343,792 ops/s
# Warmup Iteration 10: 270318,195 ops/s
Iteration 1: 253292,748 ops/s
Iteration 2: 307244,102 ops/s
Iteration 3: 295886,576 ops/s
Iteration 4: 305620,518 ops/s
Iteration 5: 292690,097 ops/s
Iteration 6: 278443,058 ops/s
Iteration 7: 244879,827 ops/s
Iteration 8: 294855,005 ops/s
Iteration 9: 312867,369 ops/s
Iteration 10: 312401,455 ops/s
Result "formatJdk8DateWithConstant":
335540,820 ±(99.9%) 29845,868 ops/s [Average]
(min, avg, max) = (244879,827, 335540,820, 409396,011), stdev = 44671,893
CI (99.9%): [305694,952, 365386,687] (assumes normal distribution)
# JMH 1.16 (released 17 days ago)
# VM version: JDK 1.8.0_112, VM 25.112-b16
# VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0_112.jdk/Contents/Home/jre/bin/java
# VM options: <none>
# Warmup: 10 iterations, 1 s each
# Measurement: 10 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: org.sample.MyBenchmark.formatLegacyDate
# Run progress: 66,67% complete, ETA 00:01:01
# Fork: 1 of 3
# Warmup Iteration 1: 558908,887 ops/s
# Warmup Iteration 2: 733246,803 ops/s
# Warmup Iteration 3: 654824,263 ops/s
# Warmup Iteration 4: 970764,418 ops/s
# Warmup Iteration 5: 1876089,024 ops/s
# Warmup Iteration 6: 1882206,197 ops/s
# Warmup Iteration 7: 1877363,617 ops/s
# Warmup Iteration 8: 1858094,838 ops/s
# Warmup Iteration 9: 1878924,724 ops/s
# Warmup Iteration 10: 1789864,746 ops/s
Iteration 1: 1668166,021 ops/s
Iteration 2: 1803687,883 ops/s
Iteration 3: 1873987,624 ops/s
Iteration 4: 1487915,597 ops/s
Iteration 5: 1752674,668 ops/s
Iteration 6: 1786828,395 ops/s
Iteration 7: 1565108,237 ops/s
Iteration 8: 1787501,500 ops/s
Iteration 9: 1940318,219 ops/s
Iteration 10: 1912371,359 ops/s
# Run progress: 77,78% complete, ETA 00:00:41
# Fork: 2 of 3
# Warmup Iteration 1: 599469,734 ops/s
# Warmup Iteration 2: 749705,562 ops/s
# Warmup Iteration 3: 742123,698 ops/s
# Warmup Iteration 4: 694818,481 ops/s
# Warmup Iteration 5: 1716543,122 ops/s
# Warmup Iteration 6: 1920223,205 ops/s
# Warmup Iteration 7: 1888521,835 ops/s
# Warmup Iteration 8: 1943699,556 ops/s
# Warmup Iteration 9: 1899301,137 ops/s
# Warmup Iteration 10: 1715226,998 ops/s
Iteration 1: 1659789,851 ops/s
Iteration 2: 1617006,970 ops/s
Iteration 3: 1892000,346 ops/s
Iteration 4: 1881957,686 ops/s
Iteration 5: 1730584,294 ops/s
Iteration 6: 1910522,703 ops/s
Iteration 7: 1567944,026 ops/s
Iteration 8: 1669108,620 ops/s
Iteration 9: 1756863,430 ops/s
Iteration 10: 1579308,134 ops/s
# Run progress: 88,89% complete, ETA 00:00:20
# Fork: 3 of 3
# Warmup Iteration 1: 535643,751 ops/s
# Warmup Iteration 2: 645546,644 ops/s
# Warmup Iteration 3: 663993,474 ops/s
# Warmup Iteration 4: 823116,005 ops/s
# Warmup Iteration 5: 1274272,854 ops/s
# Warmup Iteration 6: 1783957,934 ops/s
# Warmup Iteration 7: 1877934,541 ops/s
# Warmup Iteration 8: 1916655,841 ops/s
# Warmup Iteration 9: 1938060,232 ops/s
# Warmup Iteration 10: 1788800,558 ops/s
Iteration 1: 1693860,214 ops/s
Iteration 2: 1834989,508 ops/s
Iteration 3: 1812320,658 ops/s
Iteration 4: 1838640,784 ops/s
Iteration 5: 1634193,379 ops/s
Iteration 6: 1758847,144 ops/s
Iteration 7: 1738058,690 ops/s
Iteration 8: 1666883,116 ops/s
Iteration 9: 1827605,405 ops/s
Iteration 10: 1878504,576 ops/s
Result "formatLegacyDate":
1750918,301 ±(99.9%) 80093,337 ops/s [Average]
(min, avg, max) = (1487915,597, 1750918,301, 1940318,219), stdev = 119879,946
CI (99.9%): [1670824,964, 1831011,639] (assumes normal distribution)
# Run complete. Total time: 00:03:05
Benchmark Mode Cnt Score Error Units
MyBenchmark.formatJdk8Date thrpt 30 1886342,526 ± 166823,201 ops/s
MyBenchmark.formatJdk8DateWithConstant thrpt 30 335540,820 ± 29845,868 ops/s
MyBenchmark.formatLegacyDate thrpt 30 1750918,301 ± 80093,337 ops/s
@odrotbohm
Copy link
Author

It looks like changing the setup of the JDK 8 DateTimeFormatter to rather use DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm'Z'") significantly improves the numbers ever to the benefit of JDK 8:

# Run complete. Total time: 00:02:03

Benchmark                      Mode  Cnt        Score        Error  Units
MyBenchmark.formatJdk8Date    thrpt   30  3780721,391 ± 159550,447  ops/s
MyBenchmark.formatLegacyDate  thrpt   30  2675913,927 ± 230778,273  ops/s

@terribleherbst
Copy link

Any idea why?

@odrotbohm
Copy link
Author

bildschirmfoto 2016-11-26 um 14 23 21

Looks like for the usage of the constant, a lot of time is spent in adapting the length of the StringBuilder.

@sgybas
Copy link

sgybas commented Nov 26, 2016

DateTimeFormatter.ISO_LOCAL_DATE_TIME is a lot faster that DateTimeFormatter.ISO_DATE_TIME:

Benchmark                                     Mode  Cnt        Score        Error  Units
MyBenchmark.formatJdk8Date                   thrpt   30  2212442,038 ±  44622,371  ops/s
MyBenchmark.formatJdk8DateWithConstant       thrpt   30   238294,704 ±  13061,266  ops/s
MyBenchmark.formatJdk8DateWithConstantLocal  thrpt   30  2019639,192 ± 168394,350  ops/s
MyBenchmark.formatLegacyDate                 thrpt   30  1581412,174 ±  80028,387  ops/s

It looks like the offset and zone handling in DateTimeFormatter.ISO_DATE_TIME causes the slowdown.

@sgybas
Copy link

sgybas commented Nov 26, 2016

Actually it's the combination of LocalDateTimeand DateTimeFormatter.ISO_DATE_TIME. If you use ZonedDateTime instead, it gets a lot faster. So if you use LocalDateTime, you should also use DateTimeFormatter.ISO_LOCAL_DATE_TIME.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment