You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by rp...@apache.org on 2015/08/21 19:58:54 UTC
logging-log4j2 git commit: LOG4J2-1097 bugfixes, added unit tests,
updated benchmark
Repository: logging-log4j2
Updated Branches:
refs/heads/master 2ee96915f -> 24e89e7cd
LOG4J2-1097 bugfixes, added unit tests, updated benchmark
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/24e89e7c
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/24e89e7c
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/24e89e7c
Branch: refs/heads/master
Commit: 24e89e7cd1c6909d3871b2a7518a83ae05bb91f7
Parents: 2ee9691
Author: rpopma <rp...@apache.org>
Authored: Sat Aug 22 02:59:00 2015 +0900
Committer: rpopma <rp...@apache.org>
Committed: Sat Aug 22 02:59:00 2015 +0900
----------------------------------------------------------------------
.../core/util/datetime/CustomTimeFormat.java | 91 ++++++-----
.../util/datetime/CustomTimeFormatTest.java | 154 +++++++++++++++++++
.../log4j/perf/jmh/TimeFormatBenchmark.java | 147 +++++++-----------
3 files changed, 265 insertions(+), 127 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/24e89e7c/log4j-core/src/main/java/org/apache/logging/log4j/core/util/datetime/CustomTimeFormat.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/datetime/CustomTimeFormat.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/datetime/CustomTimeFormat.java
index 1388a90..07b730f 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/datetime/CustomTimeFormat.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/datetime/CustomTimeFormat.java
@@ -26,68 +26,72 @@ import java.util.Objects;
public class CustomTimeFormat {
/**
* Enumeration over the supported date/time format patterns.
+ * <p>
+ * Package protected for unit tests.
*/
- public static enum FixedFormat {
+ static enum FixedFormat {
/**
* ABSOLUTE time format: {@code "HH:mm:ss,SSS"}.
*/
- ABSOLUTE("HH:mm:ss,SSS", null, ':', 1, ',', 1),
+ ABSOLUTE("HH:mm:ss,SSS", null, 0, ':', 1, ',', 1),
/**
* ABSOLUTE time format variation with period separator: {@code "HH:mm:ss.SSS"}.
*/
- ABSOLUTE2("HH:mm:ss.SSS", null, ':', 1, '0', 1),
+ ABSOLUTE2("HH:mm:ss.SSS", null, 0, ':', 1, '.', 1),
/**
* COMPACT time format: {@code "yyyyMMddHHmmssSSS"}.
*/
- COMPACT("yyyyMMddHHmmssSSS", "yyyyMMdd", ' ', 0, ' ', 0),
+ COMPACT("yyyyMMddHHmmssSSS", "yyyyMMdd", 0, ' ', 0, ' ', 0),
/**
* DATE time format: {@code "dd MMM yyyy HH:mm:ss,SSS"}.
*/
- DATE("dd MMM yyyy HH:mm:ss,SSS", "dd MMM yyyy ", ':', 1, ',', 1),
+ DATE("dd MMM yyyy HH:mm:ss,SSS", "dd MMM yyyy ", 0, ':', 1, ',', 1),
/**
* DATE time format variation with period separator: {@code "dd MMM yyyy HH:mm:ss.SSS"}.
*/
- DATE2("dd MMM yyyy HH:mm:ss.SSS", "dd MMM yyyy ", ':', 1, '.', 1),
+ DATE2("dd MMM yyyy HH:mm:ss.SSS", "dd MMM yyyy ", 0, ':', 1, '.', 1),
/**
* DEFAULT time format: {@code "yyyy-MM-dd HH:mm:ss,SSS"}.
*/
- DEFAULT("yyyy-MM-dd HH:mm:ss,SSS", "yyyy-MM-dd ", ':', 1, ',', 1),
+ DEFAULT("yyyy-MM-dd HH:mm:ss,SSS", "yyyy-MM-dd ", 0, ':', 1, ',', 1),
/**
* DEFAULT time format variation with period separator: {@code "yyyy-MM-dd HH:mm:ss.SSS"}.
*/
- DEFAULT2("yyyy-MM-dd HH:mm:ss.SSS", "yyyy-MM-dd ", ':', 1, '.', 1),
+ DEFAULT2("yyyy-MM-dd HH:mm:ss.SSS", "yyyy-MM-dd ", 0, ':', 1, '.', 1),
/**
* ISO8601_BASIC time format: {@code "yyyyMMdd'T'HHmmss,SSS"}.
*/
- ISO8601_BASIC("yyyyMMdd'T'HHmmss,SSS", "yyyyMMdd'T'", ' ', 0, ',', 1),
+ ISO8601_BASIC("yyyyMMdd'T'HHmmss,SSS", "yyyyMMdd'T'", 2, ' ', 0, ',', 1),
/**
* ISO8601 time format: {@code "yyyy-MM-dd'T'HH:mm:ss,SSS"}.
*/
- ISO8601("yyyy-MM-dd'T'HH:mm:ss,SSS", "yyyy-MM-dd'T'", ':', 1, ',', 1), ;
+ ISO8601("yyyy-MM-dd'T'HH:mm:ss,SSS", "yyyy-MM-dd'T'", 2, ':', 1, ',', 1), ;
private final String pattern;
private final String datePattern;
+ private final int escapeCount;
private final char timeSeparatorChar;
private final int timeSeparatorLength;
private final char millisSeparatorChar;
private final int millisSeparatorLength;
- private FixedFormat(final String pattern, final String datePattern, char timeSeparator, int timeSepLength,
- char millisSeparator, int millisSepLength) {
+ private FixedFormat(final String pattern, final String datePattern, final int escapeCount,
+ final char timeSeparator, final int timeSepLength, final char millisSeparator, final int millisSepLength) {
this.timeSeparatorChar = timeSeparator;
this.timeSeparatorLength = timeSepLength;
this.millisSeparatorChar = millisSeparator;
this.millisSeparatorLength = millisSepLength;
this.pattern = Objects.requireNonNull(pattern);
this.datePattern = datePattern; // may be null
+ this.escapeCount = escapeCount;
}
public String getPattern() {
@@ -101,8 +105,8 @@ public class CustomTimeFormat {
/**
* Returns the FixedFormat with the name or pattern matching the specified string or {@code null} if not found.
*/
- public static FixedFormat lookup(String nameOrPattern) {
- for (FixedFormat type : FixedFormat.values()) {
+ public static FixedFormat lookup(final String nameOrPattern) {
+ for (final FixedFormat type : FixedFormat.values()) {
if (type.name().equals(nameOrPattern) || type.getPattern().equals(nameOrPattern)) {
return type;
}
@@ -110,8 +114,12 @@ public class CustomTimeFormat {
return null;
}
+ public int getLength() {
+ return pattern.length() - escapeCount;
+ }
+
public int getDatePatternLength() {
- return getDatePattern() == null ? 0 : getDatePattern().length();
+ return getDatePattern() == null ? 0 : getDatePattern().length() - escapeCount;
}
public FastDateFormat getFastDateFormat() {
@@ -123,7 +131,7 @@ public class CustomTimeFormat {
if (options == null || options.length == 0 || options.length > 1) {
return null; // time zone not supported
}
- FixedFormat type = FixedFormat.lookup(options[0]);
+ final FixedFormat type = FixedFormat.lookup(options[0]);
return type == null ? null : new CustomTimeFormat(type);
}
@@ -138,6 +146,12 @@ public class CustomTimeFormat {
private volatile long midnightToday = 0;
private volatile long midnightTomorrow = 0;
+ // cachedDate does not need to be volatile because
+ // there is a write to a volatile field *after* cachedDate is modified,
+ // and there is a read from a volatile field *before* cachedDate is read.
+ // The Java memory model guarantees that because of the above,
+ // changes to cachedDate in one thread are visible to other threads.
+ // See http://g.oswego.edu/dl/jmm/cookbook.html
private char[] cachedDate; // may be null
/**
@@ -147,13 +161,13 @@ public class CustomTimeFormat {
*
* @param type the fixed format
*/
- CustomTimeFormat(FixedFormat type) {
+ CustomTimeFormat(final FixedFormat type) {
this.type = Objects.requireNonNull(type);
this.timeSeparatorChar = type.timeSeparatorChar;
this.timeSeparatorLength = type.timeSeparatorLength;
this.millisSeparatorChar = type.millisSeparatorChar;
this.millisSeparatorLength = type.millisSeparatorLength;
- this.length = type.getPattern().length();
+ this.length = type.getLength();
this.dateLength = type.getDatePatternLength();
this.fastDateFormat = type.getFastDateFormat();
}
@@ -162,7 +176,8 @@ public class CustomTimeFormat {
return type.getPattern();
}
- // 21 bytes (allows immediate JVM inlining: < 35 bytes)
+ // Profiling showed this method is important to log4j performance. Modify with care!
+ // 21 bytes (allows immediate JVM inlining: <= -XX:MaxInlineSize=35 bytes)
private long millisSinceMidnight(final long now) {
if (now >= midnightTomorrow) {
updateMidnightMillis(now);
@@ -172,19 +187,15 @@ public class CustomTimeFormat {
private void updateMidnightMillis(final long now) {
- // cachedDate does not need to be volatile because
- // there is a write to a volatile field *after* cachedDate is modified,
- // and there is a read from a volatile field *before* cachedDate is read.
- // The Java memory model guarantees that because of the above,
- // changes to cachedDate in one thread are visible to other threads.
updateCachedDate(now);
- midnightToday = calcMidnightMillis(0);
- midnightTomorrow = calcMidnightMillis(1);
+ midnightToday = calcMidnightMillis(now, 0);
+ midnightTomorrow = calcMidnightMillis(now, 1);
}
- private long calcMidnightMillis(final int addDays) {
+ static long calcMidnightMillis(final long time, final int addDays) {
final Calendar cal = Calendar.getInstance();
+ cal.setTimeInMillis(time);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
@@ -193,41 +204,45 @@ public class CustomTimeFormat {
return cal.getTimeInMillis();
}
- private void updateCachedDate(long now) {
+ private void updateCachedDate(final long now) {
if (fastDateFormat != null) {
- StringBuilder result = fastDateFormat.format(now, new StringBuilder());
+ final StringBuilder result = fastDateFormat.format(now, new StringBuilder());
cachedDate = result.toString().toCharArray();
}
}
- // 28 bytes (allows immediate JVM inlining: < 35 bytes)
+ // Profiling showed this method is important to log4j performance. Modify with care!
+ // 28 bytes (allows immediate JVM inlining: <= -XX:MaxInlineSize=35 bytes)
public String format(final long time) {
final char[] result = new char[length];
int written = format(time, result, 0);
return new String(result, 0, written);
}
- // 31 bytes (allows immediate JVM inlining: < 35 bytes)
- public int format(final long time, final char[] buffer, int startPos) {
+ // Profiling showed this method is important to log4j performance. Modify with care!
+ // 31 bytes (allows immediate JVM inlining: <= -XX:MaxInlineSize=35 bytes)
+ public int format(final long time, final char[] buffer, final int startPos) {
// Calculate values by getting the ms values first and do then
// calculate the hour minute and second values divisions.
// Get daytime in ms: this does fit into an int
// int ms = (int) (time % 86400000);
final int ms = (int) (millisSinceMidnight(time));
- writeDate(buffer);
+ writeDate(buffer, startPos);
return writeTime(ms, buffer, startPos + dateLength) - startPos;
}
- // 22 bytes (allows immediate JVM inlining: < 35 bytes)
- private void writeDate(char[] buffer) {
+ // Profiling showed this method is important to log4j performance. Modify with care!
+ // 22 bytes (allows immediate JVM inlining: <= -XX:MaxInlineSize=35 bytes)
+ private void writeDate(final char[] buffer, final int startPos) {
if (cachedDate != null) {
- System.arraycopy(cachedDate, 0, buffer, 0, dateLength);
+ System.arraycopy(cachedDate, 0, buffer, startPos, dateLength);
}
}
- // 262 bytes (will be inlined when hot enough: < 325 bytes)
- private int writeTime(int ms, char[] buffer, int pos) {
+ // Profiling showed this method is important to log4j performance. Modify with care!
+ // 262 bytes (will be inlined when hot enough: <= -XX:FreqInlineSize=325 bytes on Linux)
+ private int writeTime(int ms, final char[] buffer, int pos) {
final int hours = ms / 3600000;
ms -= 3600000 * hours;
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/24e89e7c/log4j-core/src/test/java/org/apache/logging/log4j/core/util/datetime/CustomTimeFormatTest.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/util/datetime/CustomTimeFormatTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/util/datetime/CustomTimeFormatTest.java
new file mode 100644
index 0000000..c277c5b
--- /dev/null
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/util/datetime/CustomTimeFormatTest.java
@@ -0,0 +1,154 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.core.util.datetime;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.logging.log4j.core.util.datetime.CustomTimeFormat.FixedFormat;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Tests the CustomTimeFormat class.
+ */
+public class CustomTimeFormatTest {
+
+ @Test
+ public void testFixedFormat_getDatePatternNullIfNoDateInPattern() {
+ assertNull(FixedFormat.ABSOLUTE.getDatePattern());
+ assertNull(FixedFormat.ABSOLUTE2.getDatePattern());
+ }
+
+ @Test
+ public void testFixedFormat_getDatePatternLengthZeroIfNoDateInPattern() {
+ assertEquals(0, FixedFormat.ABSOLUTE.getDatePatternLength());
+ assertEquals(0, FixedFormat.ABSOLUTE2.getDatePatternLength());
+ }
+
+ @Test
+ public void testFixedFormat_getFastDateFormatNullIfNoDateInPattern() {
+ assertNull(FixedFormat.ABSOLUTE.getFastDateFormat());
+ assertNull(FixedFormat.ABSOLUTE2.getFastDateFormat());
+ }
+
+ @Test
+ public void testFixedFormat_getDatePatternReturnsDatePatternIfExists() {
+ assertEquals("yyyyMMdd", FixedFormat.COMPACT.getDatePattern());
+ assertEquals("yyyy-MM-dd ", FixedFormat.DEFAULT.getDatePattern());
+ }
+
+ @Test
+ public void testFixedFormat_getDatePatternLengthReturnsDatePatternLength() {
+ assertEquals("yyyyMMdd".length(), FixedFormat.COMPACT.getDatePatternLength());
+ assertEquals("yyyy-MM-dd ".length(), FixedFormat.DEFAULT.getDatePatternLength());
+ }
+
+ @Test
+ public void testFixedFormat_getFastDateFormatNonNullIfDateInPattern() {
+ assertNotNull(FixedFormat.COMPACT.getFastDateFormat());
+ assertNotNull(FixedFormat.DEFAULT.getFastDateFormat());
+ assertEquals("yyyyMMdd", FixedFormat.COMPACT.getFastDateFormat().getPattern());
+ assertEquals("yyyy-MM-dd ", FixedFormat.DEFAULT.getFastDateFormat().getPattern());
+ }
+
+ @Test
+ public void testCreateIfSupported_nonNullIfNameMatches() {
+ for (final CustomTimeFormat.FixedFormat format : CustomTimeFormat.FixedFormat.values()) {
+ final String[] options = {format.name()};
+ assertNotNull(format.name(), CustomTimeFormat.createIfSupported(options));
+ }
+ }
+
+ @Test
+ public void testCreateIfSupported_nonNullIfPatternMatches() {
+ for (final CustomTimeFormat.FixedFormat format : CustomTimeFormat.FixedFormat.values()) {
+ final String[] options = {format.getPattern()};
+ assertNotNull(format.name(), CustomTimeFormat.createIfSupported(options));
+ }
+ }
+
+ @Test
+ public void testCreateIfSupported_nullIfNameDoesNotMatch() {
+ final String[] options = {"DEFAULT3"};
+ assertNull("DEFAULT3", CustomTimeFormat.createIfSupported(options));
+ }
+
+ @Test
+ public void testCreateIfSupported_nullIfPatternDoesNotMatch() {
+ final String[] options = {"y M d H m s"};
+ assertNull("y M d H m s", CustomTimeFormat.createIfSupported(options));
+ }
+
+ @Test
+ public void testCreateIfSupported_nullIfOptionsArrayNull() {
+ assertNull("null", CustomTimeFormat.createIfSupported(null));
+ }
+
+ @Test
+ public void testCreateIfSupported_nullIfOptionsArrayHasTwoElements() {
+ final String[] options = {CustomTimeFormat.FixedFormat.ABSOLUTE.getPattern(), "+08:00"};
+ assertNull("timezone", CustomTimeFormat.createIfSupported(options));
+ }
+
+ @Test(expected=NullPointerException.class)
+ public void testConstructorDisallowsNull() {
+ new CustomTimeFormat(null);
+ }
+
+ @Test
+ public void testGetFormatReturnsConstructorFixedFormatPattern() {
+ final CustomTimeFormat format = new CustomTimeFormat(CustomTimeFormat.FixedFormat.ABSOLUTE);
+ assertSame(CustomTimeFormat.FixedFormat.ABSOLUTE.getPattern(), format.getFormat());
+ }
+
+ @Test
+ public void testFormatLong() {
+ final long start = System.currentTimeMillis();
+ final long end = start + TimeUnit.HOURS.toMillis(25);
+ for (final FixedFormat format : FixedFormat.values()) {
+ final SimpleDateFormat simpleDF = new SimpleDateFormat(format.getPattern());
+ final CustomTimeFormat customTF = new CustomTimeFormat(format);
+ for (long time = start; time < end; time += 12345) {
+ final String actual = customTF.format(time);
+ final String expected = simpleDF.format(new Date(time));
+ assertEquals(format + "/" + time, expected, actual);
+ }
+ }
+ }
+
+ @Test
+ public void testFormatLongCharArrayInt() {
+ final long start = System.currentTimeMillis();
+ final long end = start + TimeUnit.HOURS.toMillis(25);
+ final char[] buffer = new char[128];
+ for (final FixedFormat format : FixedFormat.values()) {
+ final SimpleDateFormat simpleDF = new SimpleDateFormat(format.getPattern());
+ final CustomTimeFormat customTF = new CustomTimeFormat(format);
+ for (long time = start; time < end; time += 12345) {
+ final int length = customTF.format(time, buffer, 23);
+ final String actual = new String(buffer, 23, length);
+ final String expected = simpleDF.format(new Date(time));
+ assertEquals(format + "/" + time, expected, actual);
+ }
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/24e89e7c/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/TimeFormatBenchmark.java
----------------------------------------------------------------------
diff --git a/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/TimeFormatBenchmark.java b/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/TimeFormatBenchmark.java
index 6813ca1..1d6f4ff 100644
--- a/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/TimeFormatBenchmark.java
+++ b/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/TimeFormatBenchmark.java
@@ -18,12 +18,12 @@
package org.apache.logging.log4j.perf.jmh;
import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.concurrent.TimeUnit;
+import org.apache.logging.log4j.core.util.datetime.CustomTimeFormat;
import org.apache.logging.log4j.core.util.datetime.FastDateFormat;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
@@ -38,7 +38,7 @@ import org.openjdk.jmh.annotations.State;
// ============================== HOW TO RUN THIS TEST: ====================================
//
// single thread:
-// java -jar log4j-perf/target/benchmarks.jar ".*TimeFormat.*" -f 1 -wi 5 -i 5
+// java -jar log4j-perf/target/benchmarks.jar ".*TimeFormat.*" -f 1 -wi 5 -i 10
//
// multiple threads (for example, 4 threads):
// java -jar log4j-perf/target/benchmarks.jar ".*TimeFormat.*" -f 1 -wi 5 -i 5 -t 4 -si true
@@ -55,28 +55,29 @@ public class TimeFormatBenchmark {
return new SimpleDateFormat("HH:mm:ss.SSS");
}
};
- // SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm:ss.SSS");
FastDateFormat fastDateFormat = FastDateFormat.getInstance("HH:mm:ss.SSS");
+ CustomTimeFormat customTimeFormat = CustomTimeFormat.createIfSupported(new String[]{"ABSOLUTE"});
volatile long midnightToday = 0;
volatile long midnightTomorrow = 0;
@State(Scope.Thread)
public static class BufferState {
- ByteBuffer buffer = ByteBuffer.allocate(12);
- StringBuilder stringBuilder = new StringBuilder(12);
+ final ByteBuffer buffer = ByteBuffer.allocate(12);
+ final StringBuilder stringBuilder = new StringBuilder(12);
+ final char[] charArray = new char[12];
}
private long millisSinceMidnight(final long now) {
if (now >= midnightTomorrow) {
- midnightToday = calcMidnightMillis(0);
- midnightTomorrow = calcMidnightMillis(1);
+ midnightToday = calcMidnightMillis(now, 0);
+ midnightTomorrow = calcMidnightMillis(now, 1);
}
return now - midnightToday;
}
- private long calcMidnightMillis(final int addDays) {
- // Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UCT"));
+ private long calcMidnightMillis(final long time, final int addDays) {
final Calendar cal = Calendar.getInstance();
+ cal.setTimeInMillis(time);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
@@ -86,98 +87,66 @@ public class TimeFormatBenchmark {
}
public static void main(final String[] args) {
- System.out.println(new TimeFormatBenchmark().customBitFiddlingFormatString(new BufferState()));
- System.out.println(new TimeFormatBenchmark().customFormatString(new BufferState()));
+ System.out.println(new TimeFormatBenchmark().customBitFiddlingReuseCharArray(new BufferState()));
+ System.out.println(new TimeFormatBenchmark().customFormatReuseStringBuilder(new BufferState()));
}
@Benchmark
@BenchmarkMode(Mode.SampleTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
- public void baseline() {
- }
-
- @Benchmark
- @BenchmarkMode(Mode.SampleTime)
- @OutputTimeUnit(TimeUnit.NANOSECONDS)
- public String simpleDateFormatString() {
+ public String simpleDateFormat() {
return threadLocalSimpleDateFormat.get().format(new Date());
}
@Benchmark
@BenchmarkMode(Mode.SampleTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
- public int simpleDateFormatBytes(final BufferState state) {
- final String str = threadLocalSimpleDateFormat.get().format(new Date());
- final byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
- state.buffer.clear();
- state.buffer.put(bytes);
- return state.buffer.position();
- }
-
- @Benchmark
- @BenchmarkMode(Mode.SampleTime)
- @OutputTimeUnit(TimeUnit.NANOSECONDS)
- public String fastDateFormatString() {
+ public String fastDateFormatCreateNewStringBuilder() {
return fastDateFormat.format(new Date());
}
@Benchmark
@BenchmarkMode(Mode.SampleTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
- public int fastDateFormatBytes(final BufferState state) {
- final String str = fastDateFormat.format(new Date());
- final byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
- state.buffer.clear();
- state.buffer.put(bytes);
- return state.buffer.position();
+ public String fastDateFormatReuseStringBuilder(final BufferState state) {
+ state.stringBuilder.setLength(0);
+ fastDateFormat.format(new Date(), state.stringBuilder);
+ return new String(state.stringBuilder);
}
@Benchmark
@BenchmarkMode(Mode.SampleTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
- public String customBitFiddlingFormatString(final BufferState state) {
- state.buffer.clear();
- fastFormat(System.currentTimeMillis(), state.buffer);
- return new String(state.buffer.array(), 0, state.buffer.position(), StandardCharsets.UTF_8);
+ public String customBitFiddlingReuseCharArray(final BufferState state) {
+ final int len = formatCharArrayBitFiddling(System.currentTimeMillis(), state.charArray, 0);
+ return new String(state.charArray, 0, len);
}
@Benchmark
@BenchmarkMode(Mode.SampleTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
- public int customBitFiddlingFormatBytes(final BufferState state) {
- state.buffer.clear();
- fastFormat(System.currentTimeMillis(), state.buffer);
- return state.buffer.position();
+ public String customTimeFormatCreateNewCharArray(final BufferState state) {
+ return customTimeFormat.format(System.currentTimeMillis());
}
@Benchmark
@BenchmarkMode(Mode.SampleTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
- public String customFormatString(final BufferState state) {
- state.stringBuilder.setLength(0);
- formatText(System.currentTimeMillis(), state.stringBuilder);
- return new String(state.stringBuilder);
+ public String customTimeFormatReuseCharArray(final BufferState state) {
+ final int len = customTimeFormat.format(System.currentTimeMillis(), state.charArray, 0);
+ return new String(state.charArray, 0, len);
}
@Benchmark
@BenchmarkMode(Mode.SampleTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
- public int customFormatStringBuilder(final BufferState state) {
+ public String customFormatReuseStringBuilder(final BufferState state) {
state.stringBuilder.setLength(0);
- formatText(System.currentTimeMillis(), state.stringBuilder);
- return state.stringBuilder.length();
- }
-
- @Benchmark
- @BenchmarkMode(Mode.SampleTime)
- @OutputTimeUnit(TimeUnit.NANOSECONDS)
- public int customFormatBytes(final BufferState state) {
- state.buffer.clear();
- format(System.currentTimeMillis(), state.buffer);
- return state.buffer.position();
+ formatStringBuilder(System.currentTimeMillis(), state.stringBuilder);
+ return new String(state.stringBuilder);
}
- public ByteBuffer fastFormat(final long time, final ByteBuffer buffer) {
+ int formatCharArrayBitFiddling(final long time, final char[] buffer, int pos) {
// Calculate values by getting the ms values first and do then
// shave off the hour minute and second values with multiplications
// and bit shifts instead of simple but expensive divisions.
@@ -199,43 +168,43 @@ public class TimeFormatBenchmark {
// Hour
// 13/128 is nearly the same as /10 for values up to 65
int temp = (hour * 13) >> 7;
- buffer.put((byte) (temp + '0'));
+ buffer[pos++] = ((char) (temp + '0'));
// Do subtract to get remainder instead of doing % 10
- buffer.put((byte) (hour - 10 * temp + '0'));
- buffer.put((byte) ':');
+ buffer[pos++] = ((char) (hour - 10 * temp + '0'));
+ buffer[pos++] = ((char) ':');
// Minute
// 13/128 is nearly the same as /10 for values up to 65
temp = (minute * 13) >> 7;
- buffer.put((byte) (temp + '0'));
+ buffer[pos++] = ((char) (temp + '0'));
// Do subtract to get remainder instead of doing % 10
- buffer.put((byte) (minute - 10 * temp + '0'));
- buffer.put((byte) ':');
+ buffer[pos++] = ((char) (minute - 10 * temp + '0'));
+ buffer[pos++] = ((char) ':');
// Second
// 13/128 is nearly the same as /10 for values up to 65
temp = (second * 13) >> 7;
- buffer.put((byte) (temp + '0'));
- buffer.put((byte) (second - 10 * temp + '0'));
- buffer.put((byte) '.');
+ buffer[pos++] = ((char) (temp + '0'));
+ buffer[pos++] = ((char) (second - 10 * temp + '0'));
+ buffer[pos++] = ((char) '.');
// Millisecond
// 41/4096 is nearly the same as /100
temp = (ms * 41) >> 12;
- buffer.put((byte) (temp + '0'));
+ buffer[pos++] = ((char) (temp + '0'));
ms -= 100 * temp;
temp = (ms * 205) >> 11; // 205/2048 is nearly the same as /10
- buffer.put((byte) (temp + '0'));
+ buffer[pos++] = ((char) (temp + '0'));
ms -= 10 * temp;
- buffer.put((byte) (ms + '0'));
- return buffer;
+ buffer[pos++] = ((char) (ms + '0'));
+ return pos;
}
- public StringBuilder formatText(final long time, final StringBuilder buffer) {
+ StringBuilder formatStringBuilder(final long time, final StringBuilder buffer) {
// Calculate values by getting the ms values first and do then
// calculate the hour minute and second values divisions.
@@ -287,7 +256,7 @@ public class TimeFormatBenchmark {
return buffer;
}
- public ByteBuffer format(final long time, final ByteBuffer buffer) {
+ int formatCharArray(final long time, final char[] buffer, int pos) {
// Calculate values by getting the ms values first and do then
// calculate the hour minute and second values divisions.
@@ -306,36 +275,36 @@ public class TimeFormatBenchmark {
// Hour
int temp = hours / 10;
- buffer.put((byte) (temp + '0'));
+ buffer[pos++] = ((char) (temp + '0'));
// Do subtract to get remainder instead of doing % 10
- buffer.put((byte) (hours - 10 * temp + '0'));
- buffer.put((byte) ':');
+ buffer[pos++] = ((char) (hours - 10 * temp + '0'));
+ buffer[pos++] = ((char) ':');
// Minute
temp = minutes / 10;
- buffer.put((byte) (temp + '0'));
+ buffer[pos++] = ((char) (temp + '0'));
// Do subtract to get remainder instead of doing % 10
- buffer.put((byte) (minutes - 10 * temp + '0'));
- buffer.put((byte) ':');
+ buffer[pos++] = ((char) (minutes - 10 * temp + '0'));
+ buffer[pos++] = ((char) ':');
// Second
temp = seconds / 10;
- buffer.put((byte) (temp + '0'));
- buffer.put((byte) (seconds - 10 * temp + '0'));
- buffer.put((byte) '.');
+ buffer[pos++] = ((char) (temp + '0'));
+ buffer[pos++] = ((char) (seconds - 10 * temp + '0'));
+ buffer[pos++] = ((char) '.');
// Millisecond
temp = ms / 100;
- buffer.put((byte) (temp + '0'));
+ buffer[pos++] = ((char) (temp + '0'));
ms -= 100 * temp;
temp = ms / 10;
- buffer.put((byte) (temp + '0'));
+ buffer[pos++] = ((char) (temp + '0'));
ms -= 10 * temp;
- buffer.put((byte) (ms + '0'));
- return buffer;
+ buffer[pos++] = ((char) (ms + '0'));
+ return pos;
}
}