Skip to content

Commit 3eed46b

Browse files
author
Nicolai Parlog
committed
[06] Create benchmark for OffestDateTime::parse
1 parent 7ebc620 commit 3eed46b

1 file changed

Lines changed: 178 additions & 0 deletions

File tree

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
package org.codefx.demo.effective_java._06_unnecessary_objects;
2+
3+
import java.time.LocalDate;
4+
import java.time.LocalTime;
5+
import java.time.OffsetDateTime;
6+
import java.time.OffsetTime;
7+
import java.time.ZoneOffset;
8+
9+
import org.openjdk.jmh.annotations.Benchmark;
10+
import org.openjdk.jmh.annotations.Fork;
11+
import org.openjdk.jmh.annotations.Measurement;
12+
import org.openjdk.jmh.annotations.Warmup;
13+
14+
//@Fork(value = 2, jvmArgsAppend = "-Djmh.stack.lines=3")
15+
//@Warmup(iterations = 5, time = 2)
16+
//@Measurement(iterations = 7, time = 2)
17+
public class OffsetDateTimeParse extends ObjectCreationBenchmarks {
18+
19+
@Benchmark
20+
public Object parse() {
21+
// NOTE unnecessary object:
22+
// The `OffsetDateTime` parser uses a map to store parsed values,
23+
// which produces a measurable amount of garbage.
24+
// See https://bugs.openjdk.java.net/browse/JDK-8213243 for details.
25+
return OffsetDateTime.parse("2001-02-03T04:05:06.789+01:00");
26+
}
27+
28+
@Benchmark
29+
public Object handwrittenParser() {
30+
return OffsetDateTimeParser.offsetDateTime("2001-02-03T04:05:06.789+01:00");
31+
}
32+
33+
/**
34+
* This class was written by Lukas Eder and attached to JDK-8213243
35+
* (https://bugs.openjdk.java.net/browse/JDK-8213243)
36+
* to demonstrate a faster parser than {@link OffsetDateTime#parse(CharSequence)}.
37+
*/
38+
static final class OffsetDateTimeParser {
39+
40+
static final OffsetTime offsetTime(String string) {
41+
if (string == null)
42+
return null;
43+
44+
// Out parameter emulation
45+
int[] position = { 0 };
46+
return OffsetTime.of(parseLocalTime(string, position), parseOffset(string, position));
47+
}
48+
49+
static final OffsetDateTime offsetDateTime(String string) {
50+
if (string == null)
51+
return null;
52+
53+
// Out parameter emulation
54+
int[] position = { 0 };
55+
56+
LocalDate d = parseLocalDate(string, position);
57+
58+
// [#4338] SQL supports the alternative ISO 8601 date format, where a
59+
// whitespace character separates date and time. java.time does not
60+
parseAnyChar(string, position, " T");
61+
LocalTime t = parseLocalTime(string, position);
62+
63+
return OffsetDateTime.of(d, t, parseOffset(string, position));
64+
}
65+
66+
static final LocalDate parseLocalDate(String string, int[] position) {
67+
int year = parseInt(string, position, 4);
68+
69+
parseChar(string, position, '-');
70+
int month = parseInt(string, position, 2);
71+
72+
parseChar(string, position, '-');
73+
int day = parseInt(string, position, 2);
74+
75+
return LocalDate.of(year, month, day);
76+
}
77+
78+
static final LocalTime parseLocalTime(String string, int[] position) {
79+
int hour = parseInt(string, position, 2);
80+
81+
// [#5895] HSQLDB seems to confuse 00:00:00+02:00 with 24:00:00+02:00
82+
// https://sourceforge.net/p/hsqldb/bugs/1523/
83+
if (hour == 24)
84+
hour = hour % 24;
85+
86+
parseChar(string, position, ':');
87+
int minute = parseInt(string, position, 2);
88+
int second = 0;
89+
int nano = 0;
90+
91+
if (parseCharIf(string, position, ':')) {
92+
second = parseInt(string, position, 2);
93+
94+
if (parseCharIf(string, position, '.')) {
95+
nano = 1000000 * parseInt(string, position, 3);
96+
97+
if (Character.isDigit(string.charAt(position[0]))) {
98+
nano = nano + 1000 * parseInt(string, position, 3);
99+
100+
if (Character.isDigit(string.charAt(position[0]))) {
101+
nano = nano + parseInt(string, position, 3);
102+
}
103+
}
104+
}
105+
}
106+
107+
return LocalTime.of(hour, minute, second, nano);
108+
}
109+
110+
private static final ZoneOffset parseOffset(String string, int[] position) {
111+
int offsetHours = 0;
112+
int offsetMinutes = 0;
113+
114+
if (!parseCharIf(string, position, 'Z')) {
115+
116+
// [#4965] Oracle might return some spare space
117+
while (parseCharIf(string, position, ' '))
118+
;
119+
120+
boolean minus = parseCharIf(string, position, '-');
121+
boolean plus = !minus && parseCharIf(string, position, '+');
122+
123+
if (minus || plus) {
124+
offsetHours = parseInt(string, position, 1);
125+
126+
// [#4965] Oracle might return a single-digit hour offset
127+
if (Character.isDigit(string.charAt(position[0])))
128+
offsetHours = offsetHours * 10 + parseInt(string, position, 1);
129+
130+
// [#4338] [#5180] [#5776] PostgreSQL is more lenient regarding the offset format
131+
if (parseCharIf(string, position, ':'))
132+
offsetMinutes = parseInt(string, position, 2);
133+
}
134+
}
135+
136+
return ZoneOffset.ofHoursMinutes(offsetHours, offsetMinutes);
137+
}
138+
139+
private static final void parseAnyChar(String string, int[] position, String expected) {
140+
for (int i = 0; i < expected.length(); i++) {
141+
if (string.charAt(position[0]) == expected.charAt(i)) {
142+
position[0] = position[0] + 1;
143+
return;
144+
}
145+
}
146+
147+
throw new IllegalArgumentException("Expected any of \"" + expected + "\" at position " + position[0] + " in " + string);
148+
}
149+
150+
private static final boolean parseCharIf(String string, int[] position, char expected) {
151+
boolean result = string.length() > position[0] && string.charAt(position[0]) == expected;
152+
if (result)
153+
position[0] = position[0] + 1;
154+
return result;
155+
}
156+
157+
private static final void parseChar(String string, int[] position, char expected) {
158+
if (!parseCharIf(string, position, expected))
159+
throw new IllegalArgumentException("Expected '" + expected + "' at position " + position[0] + " in " + string);
160+
}
161+
162+
private static final int parseInt(String string, int[] position, int length) {
163+
int result = 0;
164+
165+
for (int i = position[0] + length - 1, dec = 1; i >= position[0]; i--, dec = dec * 10) {
166+
int digit = string.charAt(i) - '0';
167+
168+
if (digit >= 0 && digit < 10)
169+
result = result + dec * digit;
170+
else
171+
throw new NumberFormatException("Not a number: " + string);
172+
}
173+
174+
position[0] = position[0] + length;
175+
return result;
176+
}
177+
}
178+
}

0 commit comments

Comments
 (0)