* 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:
parent
af880f119a
commit
0ba1d9b5a5
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
4
pom.xml
4
pom.xml
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue