Issue #307 datecache contention (#2725)

* use DateTimeFormatter which is threadsafe so no need of synchronized block  #307

Signed-off-by: olivier lamy <oliver.lamy@gmail.com>
This commit is contained in:
Olivier Lamy 2018-07-26 20:46:50 +10:00 committed by GitHub
parent af880f119a
commit 0ba1d9b5a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 887 additions and 30 deletions

16
Jenkinsfile vendored
View File

@ -12,6 +12,20 @@ for (def os in oss) {
parallel builds
// jmh run
/*
stage("jmh-run") {
node( 'jmh-build-node' ) {
timeout( time: 120, unit: 'MINUTES' ) {
withEnv( ["JAVA_HOME=${tool "jdk8"}"] ) {
unstash name: 'perf-tests'
sh "${env.JAVA_HOME}/bin/java -jar jetty-util/target/perf-test.jar -rff jetty-util/target/jmh_result.json -rf json"
jmhReport 'jetty-util/target/jmh_result.json'
}
}
}
}*/
def getFullBuild(jdk, os) {
return {
node(os) {
@ -106,6 +120,7 @@ def getFullBuild(jdk, os) {
[parserName: 'JavaC']];
if (isMainBuild( jdk )) {
// Collect up the jacoco execution results
stash name: 'perf-tests', includes: 'jetty-util/target/perf-test.jar'
def jacocoExcludes =
// build tools
"**/org/eclipse/jetty/ant/**" + ",**/org/eclipse/jetty/maven/**" +
@ -164,6 +179,7 @@ def getFullBuild(jdk, os) {
notifyBuild("Compact3 Failure", jdk)
throw e
}
}
}
}

View File

@ -26,6 +26,7 @@ import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import javax.servlet.MultipartConfigElement;
import javax.servlet.http.Part;
@ -36,18 +37,24 @@ import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;
import org.openjdk.jmh.annotations.Threads;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
@State(Scope.Benchmark)
@Threads(4)
@Warmup(iterations = 7, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 7, time = 500, timeUnit = TimeUnit.MILLISECONDS)
public class MultiPartBenchmark
{

View File

@ -97,6 +97,52 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>testCompile</goal>
</goals>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>${jmh.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<id>make-perf-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptors>
<descriptor>src/main/assembly/perf-tests.xml</descriptor>
</descriptors>
<finalName>perf-test</finalName>
<appendAssemblyId>false</appendAssemblyId>
<attach>false</attach>
<archive>
<manifest>
<mainClass>org.openjdk.jmh.Main</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
<pluginManagement>
<plugins>

View File

@ -0,0 +1,28 @@
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
<id>perf-tests</id>
<formats>
<format>jar</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<dependencySets>
<dependencySet>
<outputDirectory>/</outputDirectory>
<useProjectArtifact>true</useProjectArtifact>
<unpack>true</unpack>
<scope>test</scope>
</dependencySet>
</dependencySets>
<fileSets>
<fileSet>
<directory>${project.build.directory}/test-classes</directory>
<outputDirectory>/</outputDirectory>
<includes>
<include>**/*</include>
</includes>
<useDefaultExcludes>true</useDefaultExcludes>
</fileSet>
</fileSets>
</assembly>

View File

@ -18,12 +18,16 @@
package org.eclipse.jetty.util;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
/** Date Format Cache.
/**
* Date Format Cache.
* Computes String representations of Dates and caches
* the results so that subsequent requests within the same second
* will be fast.
@ -43,8 +47,9 @@ public class DateCache
private final String _formatString;
private final String _tzFormatString;
private final SimpleDateFormat _tzFormat;
private final Locale _locale ;
private final DateTimeFormatter _tzFormat;
private final Locale _locale;
private final ZoneId _zoneId;
private volatile Tick _tick;
@ -138,14 +143,14 @@ public class DateCache
if( _locale != null )
{
_tzFormat=new SimpleDateFormat(_tzFormatString,_locale);
_tzFormat=DateTimeFormatter.ofPattern(_tzFormatString,_locale);
}
else
{
_tzFormat=new SimpleDateFormat(_tzFormatString);
_tzFormat=DateTimeFormatter.ofPattern(_tzFormatString);
}
_tzFormat.setTimeZone(tz);
_zoneId = tz.toZoneId();
_tzFormat.withZone(_zoneId);
_tick=null;
}
@ -153,7 +158,7 @@ public class DateCache
/* ------------------------------------------------------------ */
public TimeZone getTimeZone()
{
return _tzFormat.getTimeZone();
return TimeZone.getTimeZone(_zoneId);
}
@ -171,11 +176,7 @@ public class DateCache
// Is this the cached time
if (tick==null || seconds!=tick._seconds)
{
// It's a cache miss
synchronized (this)
{
return _tzFormat.format(inDate);
}
return ZonedDateTime.ofInstant(inDate.toInstant(),_zoneId).format( _tzFormat );
}
return tick._string;
@ -198,11 +199,7 @@ public class DateCache
if (tick==null || seconds!=tick._seconds)
{
// It's a cache miss
Date d = new Date(inDate);
synchronized (this)
{
return _tzFormat.format(d);
}
return ZonedDateTime.ofInstant(Instant.ofEpochMilli(inDate),_zoneId).format( _tzFormat );
}
return tick._string;
@ -245,17 +242,16 @@ public class DateCache
{
long seconds = now / 1000;
// Synchronize to protect _tzFormat
synchronized (this)
Tick tick=_tick;
// recheck the tick, to save multiple formats
if (tick==null || tick._seconds!=seconds)
{
// recheck the tick, to save multiple formats
if (_tick==null || _tick._seconds!=seconds)
{
String s= _tzFormat.format(new Date(now));
return _tick=new Tick(seconds,s);
}
return _tick;
String s = ZonedDateTime.ofInstant(Instant.now(),_zoneId).format(_tzFormat);
_tick=new Tick(seconds,s);
tick=_tick;
}
return tick;
}
/* ------------------------------------------------------------ */

View File

@ -19,6 +19,8 @@
package org.eclipse.jetty.util;
import java.time.Instant;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
@ -72,4 +74,32 @@ public class DateCacheTest
}
Assert.assertThat(hits,Matchers.greaterThan(misses));
}
@Test
public void test_all_methods() {
// we simply check we do not have any exception
DateCache dateCache = new DateCache();
Assert.assertNotNull(dateCache.formatNow(System.currentTimeMillis()));
Assert.assertNotNull(dateCache.formatNow(new Date().getTime()));
Assert.assertNotNull(dateCache.formatNow(Instant.now().toEpochMilli()));
Assert.assertNotNull(dateCache.format(new Date()));
Assert.assertNotNull(dateCache.format(new Date(System.currentTimeMillis())));
Assert.assertNotNull(dateCache.format(System.currentTimeMillis()));
Assert.assertNotNull(dateCache.format(new Date().getTime()));
Assert.assertNotNull(dateCache.format(Instant.now().toEpochMilli()));
Assert.assertNotNull(dateCache.formatTick(System.currentTimeMillis()));
Assert.assertNotNull(dateCache.formatTick(new Date().getTime()));
Assert.assertNotNull(dateCache.formatTick(Instant.now().toEpochMilli()));
Assert.assertNotNull(dateCache.getFormatString());
Assert.assertNotNull(dateCache.getTimeZone());
Assert.assertNotNull(dateCache.now());
Assert.assertNotNull(dateCache.tick());
}
}

View File

@ -0,0 +1,91 @@
//
// ========================================================================
// Copyright (c) 1995-2018 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.util.jmh;
import org.eclipse.jetty.util.DateCache;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Threads;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import org.openjdk.jmh.runner.options.TimeValue;
import java.time.Instant;
import java.util.Date;
import java.util.concurrent.TimeUnit;
@State(Scope.Benchmark)
@Threads(4)
@Warmup(iterations = 7, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 7, time = 500, timeUnit = TimeUnit.MILLISECONDS)
public class DateCacheBenchmark
{
DateCache dateCache = new DateCache();
long timestamp = Instant.now().toEpochMilli();
@Benchmark
@BenchmarkMode(Mode.Throughput)
public void testDateCacheTimestamp()
{
dateCache.format(timestamp);
}
@Benchmark
@BenchmarkMode(Mode.Throughput)
public void testDateCacheNow()
{
dateCache.format(new Date());
}
@Benchmark
@BenchmarkMode(Mode.Throughput)
public void testDateCacheFormatNow()
{
dateCache.formatNow(System.currentTimeMillis());
}
public static void main(String[] args) throws RunnerException
{
Options opt = new OptionsBuilder()
.include(DateCacheBenchmark.class.getSimpleName())
.warmupIterations(2)
.measurementIterations(3)
.forks(1)
.threads(400)
// .syncIterations(true) // Don't start all threads at same time
.warmupTime(new TimeValue(10000,TimeUnit.MILLISECONDS))
.measurementTime(new TimeValue(10000,TimeUnit.MILLISECONDS))
// .addProfiler(CompilerProfiler.class)
// .addProfiler(LinuxPerfProfiler.class)
// .addProfiler(LinuxPerfNormProfiler.class)
// .addProfiler(LinuxPerfAsmProfiler.class)
// .resultFormat(ResultFormatType.CSV)
.build();
new Runner(opt).run();
}
}

View File

@ -0,0 +1,195 @@
//
// ========================================================================
// Copyright (c) 1995-2018 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.util.jmh;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
/**
* Date Format Cache.
* Computes String representations of Dates and caches
* the results so that subsequent requests within the same second
* will be fast.
*
* Only format strings that contain either "ss". Sub second formatting is
* not handled.
*
* The timezone of the date may be included as an ID with the "zzz"
* format string or as an offset with the "ZZZ" format string.
*
* If consecutive calls are frequently very different, then this
* may be a little slower than a normal DateFormat.
*/
public class DateCacheNoTick
{
public static final String DEFAULT_FORMAT="EEE MMM dd HH:mm:ss zzz yyyy";
private final String _formatString;
private final String _tzFormatString;
private final DateTimeFormatter _tzFormat;
private final Locale _locale;
private final ZoneId _zoneId;
/* ------------------------------------------------------------ */
/** Constructor.
* Make a DateCache that will use a default format. The default format
* generates the same results as Date.toString().
*/
public DateCacheNoTick()
{
this(DEFAULT_FORMAT);
}
/* ------------------------------------------------------------ */
/** Constructor.
* Make a DateCache that will use the given format
* @param format the format to use
*/
public DateCacheNoTick( String format)
{
this(format,null,TimeZone.getDefault());
}
/* ------------------------------------------------------------ */
public DateCacheNoTick( String format, Locale l)
{
this(format,l,TimeZone.getDefault());
}
/* ------------------------------------------------------------ */
public DateCacheNoTick( String format, Locale l, String tz)
{
this(format,l,TimeZone.getTimeZone(tz));
}
/* ------------------------------------------------------------ */
public DateCacheNoTick( String format, Locale l, TimeZone tz)
{
_formatString=format;
_locale = l;
int zIndex = _formatString.indexOf( "ZZZ" );
if( zIndex >= 0 )
{
String ss1 = _formatString.substring( 0, zIndex );
String ss2 = _formatString.substring( zIndex+3 );
int tzOffset = tz.getRawOffset();
StringBuilder sb = new StringBuilder(_formatString.length()+10);
sb.append(ss1);
sb.append("'");
if( tzOffset >= 0 )
sb.append( '+' );
else
{
tzOffset = -tzOffset;
sb.append( '-' );
}
int raw = tzOffset / (1000*60); // Convert to seconds
int hr = raw / 60;
int min = raw % 60;
if( hr < 10 )
sb.append( '0' );
sb.append( hr );
if( min < 10 )
sb.append( '0' );
sb.append( min );
sb.append( '\'' );
sb.append(ss2);
_tzFormatString=sb.toString();
}
else
_tzFormatString=_formatString;
if( _locale != null )
{
_tzFormat=DateTimeFormatter.ofPattern(_tzFormatString,_locale);
}
else
{
_tzFormat=DateTimeFormatter.ofPattern(_tzFormatString);
}
_zoneId = tz.toZoneId();
_tzFormat.withZone(_zoneId);
}
/* ------------------------------------------------------------ */
public TimeZone getTimeZone()
{
return TimeZone.getTimeZone(_zoneId);
}
/* ------------------------------------------------------------ */
/** Format a date according to our stored formatter.
* @param inDate the Date
* @return Formatted date
*/
public String format(Date inDate)
{
return ZonedDateTime.ofInstant(inDate.toInstant(),_zoneId).format( _tzFormat );
}
/* ------------------------------------------------------------ */
/** Format a date according to our stored formatter.
* If it happens to be in the same second as the last formatNow
* call, then the format is reused.
* @param inDate the date in milliseconds since unix epoch
* @return Formatted date
*/
public String format(long inDate)
{
return ZonedDateTime.ofInstant(Instant.ofEpochMilli(inDate),_zoneId).format( _tzFormat );
}
/* ------------------------------------------------------------ */
/** Format a date according to our stored formatter.
* The passed time is expected to be close to the current time, so it is
* compared to the last value passed and if it is within the same second,
* the format is reused. Otherwise a new cached format is created.
* @param now the milliseconds since unix epoch
* @return Formatted date
*/
public String formatNow(long now)
{
return format(now);
}
/* ------------------------------------------------------------ */
public String now()
{
return formatNow(System.currentTimeMillis());
}
/* ------------------------------------------------------------ */
public String getFormatString()
{
return _formatString;
}
}

View File

@ -0,0 +1,90 @@
//
// ========================================================================
// Copyright (c) 1995-2018 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.util.jmh;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Threads;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import org.openjdk.jmh.runner.options.TimeValue;
import java.time.Instant;
import java.util.Date;
import java.util.concurrent.TimeUnit;
@State(Scope.Benchmark)
@Threads(4)
@Warmup(iterations = 7, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 7, time = 500, timeUnit = TimeUnit.MILLISECONDS)
public class DateCacheNoTickBenchmark
{
DateCacheNoTick dateCache = new DateCacheNoTick();
long timestamp = Instant.now().toEpochMilli();
@Benchmark
@BenchmarkMode(Mode.Throughput)
public void testDateCacheTimestamp()
{
dateCache.format(timestamp);
}
@Benchmark
@BenchmarkMode(Mode.Throughput)
public void testDateCacheNow()
{
dateCache.format(new Date());
}
@Benchmark
@BenchmarkMode(Mode.Throughput)
public void testDateCacheFormatNow()
{
dateCache.formatNow(System.currentTimeMillis());
}
public static void main(String[] args) throws RunnerException
{
Options opt = new OptionsBuilder()
.include(DateCacheNoTickBenchmark.class.getSimpleName())
.warmupIterations(2)
.measurementIterations(3)
.forks(1)
.threads(400)
// .syncIterations(true) // Don't start all threads at same time
.warmupTime(new TimeValue(10000,TimeUnit.MILLISECONDS))
.measurementTime(new TimeValue(10000,TimeUnit.MILLISECONDS))
// .addProfiler(CompilerProfiler.class)
// .addProfiler(LinuxPerfProfiler.class)
// .addProfiler(LinuxPerfNormProfiler.class)
// .addProfiler(LinuxPerfAsmProfiler.class)
// .resultFormat(ResultFormatType.CSV)
.build();
new Runner(opt).run();
}
}

View File

@ -0,0 +1,268 @@
//
// ========================================================================
// Copyright (c) 1995-2018 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.util.jmh;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
/** Date Format Cache.
* Computes String representations of Dates and caches
* the results so that subsequent requests within the same second
* will be fast.
*
* Only format strings that contain either "ss". Sub second formatting is
* not handled.
*
* The timezone of the date may be included as an ID with the "zzz"
* format string or as an offset with the "ZZZ" format string.
*
* If consecutive calls are frequently very different, then this
* may be a little slower than a normal DateFormat.
*/
public class DateCacheSimpleDateFormat
{
public static final String DEFAULT_FORMAT="EEE MMM dd HH:mm:ss zzz yyyy";
private final String _formatString;
private final String _tzFormatString;
private final SimpleDateFormat _tzFormat;
private final Locale _locale ;
private volatile Tick _tick;
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
public static class Tick
{
final long _seconds;
final String _string;
public Tick(long seconds, String string)
{
_seconds = seconds;
_string = string;
}
}
/* ------------------------------------------------------------ */
/** Constructor.
* Make a DateCache that will use a default format. The default format
* generates the same results as Date.toString().
*/
public DateCacheSimpleDateFormat()
{
this(DEFAULT_FORMAT);
}
/* ------------------------------------------------------------ */
/** Constructor.
* Make a DateCache that will use the given format
* @param format the format to use
*/
public DateCacheSimpleDateFormat(String format)
{
this( format, null, TimeZone.getDefault());
}
/* ------------------------------------------------------------ */
public DateCacheSimpleDateFormat(String format,Locale l)
{
this(format,l,TimeZone.getDefault());
}
/* ------------------------------------------------------------ */
public DateCacheSimpleDateFormat(String format,Locale l,String tz)
{
this(format,l,TimeZone.getTimeZone(tz));
}
/* ------------------------------------------------------------ */
public DateCacheSimpleDateFormat(String format,Locale l,TimeZone tz)
{
_formatString=format;
_locale = l;
int zIndex = _formatString.indexOf( "ZZZ" );
if( zIndex >= 0 )
{
String ss1 = _formatString.substring( 0, zIndex );
String ss2 = _formatString.substring( zIndex+3 );
int tzOffset = tz.getRawOffset();
StringBuilder sb = new StringBuilder(_formatString.length()+10);
sb.append(ss1);
sb.append("'");
if( tzOffset >= 0 )
sb.append( '+' );
else
{
tzOffset = -tzOffset;
sb.append( '-' );
}
int raw = tzOffset / (1000*60); // Convert to seconds
int hr = raw / 60;
int min = raw % 60;
if( hr < 10 )
sb.append( '0' );
sb.append( hr );
if( min < 10 )
sb.append( '0' );
sb.append( min );
sb.append( '\'' );
sb.append(ss2);
_tzFormatString=sb.toString();
}
else
_tzFormatString=_formatString;
if( _locale != null )
{
_tzFormat=new SimpleDateFormat(_tzFormatString,_locale);
}
else
{
_tzFormat=new SimpleDateFormat(_tzFormatString);
}
_tzFormat.setTimeZone(tz);
_tick=null;
}
/* ------------------------------------------------------------ */
public TimeZone getTimeZone()
{
return _tzFormat.getTimeZone();
}
/* ------------------------------------------------------------ */
/** Format a date according to our stored formatter.
* @param inDate the Date
* @return Formatted date
*/
public String format(Date inDate)
{
long seconds = inDate.getTime() / 1000;
Tick tick=_tick;
// Is this the cached time
if (tick==null || seconds!=tick._seconds)
{
// It's a cache miss
synchronized (this)
{
return _tzFormat.format(inDate);
}
}
return tick._string;
}
/* ------------------------------------------------------------ */
/** Format a date according to our stored formatter.
* If it happens to be in the same second as the last formatNow
* call, then the format is reused.
* @param inDate the date in milliseconds since unix epoch
* @return Formatted date
*/
public String format(long inDate)
{
long seconds = inDate / 1000;
Tick tick=_tick;
// Is this the cached time
if (tick==null || seconds!=tick._seconds)
{
// It's a cache miss
Date d = new Date(inDate);
synchronized (this)
{
return _tzFormat.format(d);
}
}
return tick._string;
}
/* ------------------------------------------------------------ */
/** Format a date according to our stored formatter.
* The passed time is expected to be close to the current time, so it is
* compared to the last value passed and if it is within the same second,
* the format is reused. Otherwise a new cached format is created.
* @param now the milliseconds since unix epoch
* @return Formatted date
*/
public String formatNow(long now)
{
long seconds = now / 1000;
Tick tick=_tick;
// Is this the cached time
if (tick!=null && tick._seconds==seconds)
return tick._string;
return formatTick(now)._string;
}
/* ------------------------------------------------------------ */
public String now()
{
return formatNow(System.currentTimeMillis());
}
/* ------------------------------------------------------------ */
public Tick tick()
{
return formatTick(System.currentTimeMillis());
}
/* ------------------------------------------------------------ */
protected Tick formatTick(long now)
{
long seconds = now / 1000;
// Synchronize to protect _tzFormat
synchronized (this)
{
// recheck the tick, to save multiple formats
if (_tick==null || _tick._seconds!=seconds)
{
String s= _tzFormat.format(new Date(now));
return _tick=new Tick(seconds,s);
}
return _tick;
}
}
/* ------------------------------------------------------------ */
public String getFormatString()
{
return _formatString;
}
}

View File

@ -0,0 +1,90 @@
//
// ========================================================================
// Copyright (c) 1995-2018 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.util.jmh;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Threads;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import org.openjdk.jmh.runner.options.TimeValue;
import java.time.Instant;
import java.util.Date;
import java.util.concurrent.TimeUnit;
@State(Scope.Benchmark)
@Threads(4)
@Warmup(iterations = 7, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 7, time = 500, timeUnit = TimeUnit.MILLISECONDS)
public class DateCacheSimpleDateFormatBenchmark
{
DateCacheSimpleDateFormat dateCache = new DateCacheSimpleDateFormat();
long timestamp = Instant.now().toEpochMilli();
@Benchmark
@BenchmarkMode(Mode.Throughput)
public void testDateCacheTimestamp()
{
dateCache.format(timestamp);
}
@Benchmark
@BenchmarkMode(Mode.Throughput)
public void testDateCacheNow()
{
dateCache.format(new Date());
}
@Benchmark
@BenchmarkMode(Mode.Throughput)
public void testDateCacheFormatNow()
{
dateCache.formatNow(System.currentTimeMillis());
}
public static void main(String[] args) throws RunnerException
{
Options opt = new OptionsBuilder()
.include(DateCacheSimpleDateFormatBenchmark.class.getSimpleName())
.warmupIterations(2)
.measurementIterations(3)
.forks(1)
.threads(400)
// .syncIterations(true) // Don't start all threads at same time
.warmupTime(new TimeValue(10000,TimeUnit.MILLISECONDS))
.measurementTime(new TimeValue(10000,TimeUnit.MILLISECONDS))
// .addProfiler(CompilerProfiler.class)
// .addProfiler(LinuxPerfProfiler.class)
// .addProfiler(LinuxPerfNormProfiler.class)
// .addProfiler(LinuxPerfAsmProfiler.class)
// .resultFormat(ResultFormatType.CSV)
.build();
new Runner(opt).run();
}
}

View File

@ -27,7 +27,7 @@
<alpn.version>undefined</alpn.version>
<conscrypt.version>1.1.4</conscrypt.version>
<asm.version>6.2</asm.version>
<jmh.version>1.20</jmh.version>
<jmh.version>1.21</jmh.version>
<jmhjar.name>benchmarks</jmhjar.name>
<surefireVersion>2.21.0</surefireVersion>
@ -590,7 +590,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.0.0</version>
<version>3.1.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>