init
This commit is contained in:
508
test/org/apache/catalina/valves/Benchmarks.java
Normal file
508
test/org/apache/catalina/valves/Benchmarks.java
Normal file
@@ -0,0 +1,508 @@
|
||||
/*
|
||||
* 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.catalina.valves;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Some simple micro-benchmarks to help determine best approach for thread
|
||||
* safety in valves, particularly the {@link AccessLogValve}. Implemented as
|
||||
* JUnit tests to make the simple to execute but does not used Test* as the
|
||||
* class name to avoid being included in the automated unit tests.
|
||||
*/
|
||||
public class Benchmarks {
|
||||
@Test
|
||||
public void testAccessLogGetDate() throws Exception {
|
||||
// Is it better to use a sync or a thread local here?
|
||||
BenchmarkTest benchmark = new BenchmarkTest();
|
||||
Runnable[] tests = new Runnable[] { new GetDateBenchmarkTest_Sync(),
|
||||
new GetDateBenchmarkTest_Local(),
|
||||
new GetDateBenchmarkTest_LocalMutableLong(),
|
||||
new GetDateBenchmarkTest_LocalStruct() };
|
||||
benchmark.doTest(5, tests);
|
||||
}
|
||||
|
||||
private static class GetDateBenchmarkTest_Sync implements Runnable {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Syncs";
|
||||
}
|
||||
|
||||
private volatile long currentMillis = 0;
|
||||
private volatile Date currentDate = null;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
getCurrentDate();
|
||||
}
|
||||
|
||||
public Date getCurrentDate() {
|
||||
long systime = System.currentTimeMillis();
|
||||
if ((systime - currentMillis) > 1000) {
|
||||
synchronized (this) {
|
||||
if ((systime - currentMillis) > 1000) {
|
||||
currentDate = new Date(systime);
|
||||
currentMillis = systime;
|
||||
}
|
||||
}
|
||||
}
|
||||
return currentDate;
|
||||
}
|
||||
}
|
||||
|
||||
private static class GetDateBenchmarkTest_Local implements Runnable {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ThreadLocals";
|
||||
}
|
||||
|
||||
private ThreadLocal<Long> currentMillisLocal = new ThreadLocal<Long>() {
|
||||
@Override
|
||||
protected Long initialValue() {
|
||||
return Long.valueOf(0);
|
||||
}
|
||||
};
|
||||
|
||||
private ThreadLocal<Date> currentDateLocal = new ThreadLocal<>();
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
getCurrentDate();
|
||||
}
|
||||
|
||||
public Date getCurrentDate() {
|
||||
long systime = System.currentTimeMillis();
|
||||
if ((systime - currentMillisLocal.get().longValue()) > 1000) {
|
||||
currentDateLocal.set(new Date(systime));
|
||||
currentMillisLocal.set(Long.valueOf(systime));
|
||||
}
|
||||
return currentDateLocal.get();
|
||||
}
|
||||
}
|
||||
|
||||
private static class GetDateBenchmarkTest_LocalMutableLong implements
|
||||
Runnable {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ThreadLocals with a mutable Long";
|
||||
}
|
||||
|
||||
private static class MutableLong {
|
||||
long value = 0;
|
||||
}
|
||||
|
||||
private ThreadLocal<MutableLong> currentMillisLocal = new ThreadLocal<MutableLong>() {
|
||||
@Override
|
||||
protected MutableLong initialValue() {
|
||||
return new MutableLong();
|
||||
}
|
||||
};
|
||||
|
||||
private ThreadLocal<Date> currentDateLocal = new ThreadLocal<>();
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
getCurrentDate();
|
||||
}
|
||||
|
||||
public Date getCurrentDate() {
|
||||
long systime = System.currentTimeMillis();
|
||||
if ((systime - currentMillisLocal.get().value) > 1000) {
|
||||
currentDateLocal.set(new Date(systime));
|
||||
currentMillisLocal.get().value = systime;
|
||||
}
|
||||
return currentDateLocal.get();
|
||||
}
|
||||
}
|
||||
|
||||
private static class GetDateBenchmarkTest_LocalStruct implements Runnable {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "single ThreadLocal";
|
||||
}
|
||||
|
||||
// note, that we can avoid (long -> Long) conversion
|
||||
private static class Struct {
|
||||
public long currentMillis = 0;
|
||||
public Date currentDate;
|
||||
}
|
||||
|
||||
private ThreadLocal<Struct> currentStruct = new ThreadLocal<Struct>() {
|
||||
@Override
|
||||
protected Struct initialValue() {
|
||||
return new Struct();
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
getCurrentDate();
|
||||
}
|
||||
|
||||
public Date getCurrentDate() {
|
||||
Struct struct = currentStruct.get();
|
||||
long systime = System.currentTimeMillis();
|
||||
if ((systime - struct.currentMillis) > 1000) {
|
||||
struct.currentDate = new Date(systime);
|
||||
struct.currentMillis = systime;
|
||||
}
|
||||
return struct.currentDate;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAccessLogTimeDateElement() throws Exception {
|
||||
// Is it better to use a sync or a thread local here?
|
||||
BenchmarkTest benchmark = new BenchmarkTest();
|
||||
Runnable[] tests = new Runnable[] {
|
||||
new TimeDateElementBenchmarkTest_Sync(),
|
||||
new TimeDateElementBenchmarkTest_Local(),
|
||||
new TimeDateElementBenchmarkTest_LocalStruct(),
|
||||
new TimeDateElementBenchmarkTest_LocalStruct_SBuilder() };
|
||||
benchmark.doTest(5, tests);
|
||||
}
|
||||
|
||||
private abstract static class TimeDateElementBenchmarkTestBase {
|
||||
protected static final String months[] = { "Jan", "Feb", "Mar", "Apr",
|
||||
"May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
|
||||
|
||||
protected String lookup(String month) {
|
||||
int index;
|
||||
try {
|
||||
index = Integer.parseInt(month) - 1;
|
||||
} catch (Throwable t) {
|
||||
index = 0; // cannot happen, in theory
|
||||
}
|
||||
return (months[index]);
|
||||
}
|
||||
}
|
||||
|
||||
private static class TimeDateElementBenchmarkTest_Sync extends
|
||||
TimeDateElementBenchmarkTestBase implements Runnable {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Syncs";
|
||||
}
|
||||
|
||||
private volatile Date currentDate = new Date();
|
||||
private volatile String currentDateString = null;
|
||||
private SimpleDateFormat dayFormatter = new SimpleDateFormat("dd");
|
||||
private SimpleDateFormat monthFormatter = new SimpleDateFormat("MM");
|
||||
private SimpleDateFormat yearFormatter = new SimpleDateFormat("yyyy");
|
||||
private SimpleDateFormat timeFormatter = new SimpleDateFormat(
|
||||
"hh:mm:ss");
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
printDate();
|
||||
}
|
||||
|
||||
public String printDate() {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
Date date = getDateSync();
|
||||
if (currentDate != date) {
|
||||
synchronized (this) {
|
||||
if (currentDate != date) {
|
||||
StringBuilder current = new StringBuilder(32);
|
||||
current.append('[');
|
||||
current.append(dayFormatter.format(date)); // Day
|
||||
current.append('/');
|
||||
current.append(lookup(monthFormatter.format(date))); // Month
|
||||
current.append('/');
|
||||
current.append(yearFormatter.format(date)); // Year
|
||||
current.append(':');
|
||||
current.append(timeFormatter.format(date)); // Time
|
||||
current.append(']');
|
||||
currentDateString = current.toString();
|
||||
currentDate = date;
|
||||
}
|
||||
}
|
||||
}
|
||||
buf.append(currentDateString);
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private Date getDateSync() {
|
||||
long systime = System.currentTimeMillis();
|
||||
if ((systime - currentDate.getTime()) > 1000) {
|
||||
synchronized (this) {
|
||||
if ((systime - currentDate.getTime()) > 1000) {
|
||||
currentDate.setTime(systime);
|
||||
}
|
||||
}
|
||||
}
|
||||
return currentDate;
|
||||
}
|
||||
}
|
||||
|
||||
private static class TimeDateElementBenchmarkTest_Local extends
|
||||
TimeDateElementBenchmarkTestBase implements Runnable {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ThreadLocals";
|
||||
}
|
||||
|
||||
private ThreadLocal<String> currentDateStringLocal = new ThreadLocal<>();
|
||||
|
||||
private ThreadLocal<Date> currentDateLocal = new ThreadLocal<Date>() {
|
||||
@Override
|
||||
protected Date initialValue() {
|
||||
return new Date();
|
||||
}
|
||||
};
|
||||
private ThreadLocal<SimpleDateFormat> dayFormatterLocal = new ThreadLocal<SimpleDateFormat>() {
|
||||
@Override
|
||||
protected SimpleDateFormat initialValue() {
|
||||
return new SimpleDateFormat("dd");
|
||||
}
|
||||
};
|
||||
private ThreadLocal<SimpleDateFormat> monthFormatterLocal = new ThreadLocal<SimpleDateFormat>() {
|
||||
@Override
|
||||
protected SimpleDateFormat initialValue() {
|
||||
return new SimpleDateFormat("MM");
|
||||
}
|
||||
};
|
||||
private ThreadLocal<SimpleDateFormat> yearFormatterLocal = new ThreadLocal<SimpleDateFormat>() {
|
||||
@Override
|
||||
protected SimpleDateFormat initialValue() {
|
||||
return new SimpleDateFormat("yyyy");
|
||||
}
|
||||
};
|
||||
private ThreadLocal<SimpleDateFormat> timeFormatterLocal = new ThreadLocal<SimpleDateFormat>() {
|
||||
@Override
|
||||
protected SimpleDateFormat initialValue() {
|
||||
return new SimpleDateFormat("hh:mm:ss");
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
printDate();
|
||||
}
|
||||
|
||||
public String printDate() {
|
||||
getDateLocal();
|
||||
if (currentDateStringLocal.get() == null) {
|
||||
StringBuilder current = new StringBuilder(32);
|
||||
current.append('[');
|
||||
current.append(dayFormatterLocal.get().format(
|
||||
currentDateLocal.get())); // Day
|
||||
current.append('/');
|
||||
current.append(lookup(monthFormatterLocal.get().format(
|
||||
currentDateLocal.get()))); // Month
|
||||
current.append('/');
|
||||
current.append(yearFormatterLocal.get().format(
|
||||
currentDateLocal.get())); // Year
|
||||
current.append(':');
|
||||
current.append(timeFormatterLocal.get().format(
|
||||
currentDateLocal.get())); // Time
|
||||
current.append(']');
|
||||
currentDateStringLocal.set(current.toString());
|
||||
}
|
||||
return currentDateStringLocal.get();
|
||||
}
|
||||
|
||||
private Date getDateLocal() {
|
||||
long systime = System.currentTimeMillis();
|
||||
if ((systime - currentDateLocal.get().getTime()) > 1000) {
|
||||
currentDateLocal.get().setTime(systime);
|
||||
currentDateStringLocal.set(null);
|
||||
}
|
||||
return currentDateLocal.get();
|
||||
}
|
||||
}
|
||||
|
||||
private static class TimeDateElementBenchmarkTest_LocalStruct extends
|
||||
TimeDateElementBenchmarkTestBase implements Runnable {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "single ThreadLocal";
|
||||
}
|
||||
|
||||
private static class Struct {
|
||||
public String currentDateString;
|
||||
public Date currentDate = new Date();
|
||||
public SimpleDateFormat dayFormatter = new SimpleDateFormat("dd");
|
||||
public SimpleDateFormat monthFormatter = new SimpleDateFormat("MM");
|
||||
public SimpleDateFormat yearFormatter = new SimpleDateFormat("yyyy");
|
||||
public SimpleDateFormat timeFormatter = new SimpleDateFormat(
|
||||
"hh:mm:ss");
|
||||
}
|
||||
|
||||
private ThreadLocal<Struct> structLocal = new ThreadLocal<Struct>() {
|
||||
@Override
|
||||
protected Struct initialValue() {
|
||||
return new Struct();
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
printDate();
|
||||
}
|
||||
|
||||
public String printDate() {
|
||||
getDateLocal();
|
||||
Struct struct = structLocal.get();
|
||||
if (struct.currentDateString == null) {
|
||||
StringBuilder current = new StringBuilder(32);
|
||||
current.append('[');
|
||||
current.append(struct.dayFormatter.format(struct.currentDate)); // Day
|
||||
current.append('/');
|
||||
current.append(lookup(struct.monthFormatter
|
||||
.format(struct.currentDate))); // Month
|
||||
current.append('/');
|
||||
current.append(struct.yearFormatter.format(struct.currentDate)); // Year
|
||||
current.append(':');
|
||||
current.append(struct.timeFormatter.format(struct.currentDate)); // Time
|
||||
current.append(']');
|
||||
struct.currentDateString = current.toString();
|
||||
}
|
||||
return struct.currentDateString;
|
||||
}
|
||||
|
||||
private Date getDateLocal() {
|
||||
Struct struct = structLocal.get();
|
||||
long systime = System.currentTimeMillis();
|
||||
if ((systime - struct.currentDate.getTime()) > 1000) {
|
||||
struct.currentDate.setTime(systime);
|
||||
struct.currentDateString = null;
|
||||
}
|
||||
return struct.currentDate;
|
||||
}
|
||||
}
|
||||
|
||||
private static class TimeDateElementBenchmarkTest_LocalStruct_SBuilder extends
|
||||
TimeDateElementBenchmarkTestBase implements Runnable {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "single ThreadLocal, with StringBuilder";
|
||||
}
|
||||
|
||||
private static class Struct {
|
||||
public String currentDateString;
|
||||
public Date currentDate = new Date();
|
||||
public SimpleDateFormat dayFormatter = new SimpleDateFormat("dd");
|
||||
public SimpleDateFormat monthFormatter = new SimpleDateFormat("MM");
|
||||
public SimpleDateFormat yearFormatter = new SimpleDateFormat("yyyy");
|
||||
public SimpleDateFormat timeFormatter = new SimpleDateFormat(
|
||||
"hh:mm:ss");
|
||||
}
|
||||
|
||||
private ThreadLocal<Struct> structLocal = new ThreadLocal<Struct>() {
|
||||
@Override
|
||||
protected Struct initialValue() {
|
||||
return new Struct();
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
printDate();
|
||||
}
|
||||
|
||||
public String printDate() {
|
||||
getDateLocal();
|
||||
Struct struct = structLocal.get();
|
||||
if (struct.currentDateString == null) {
|
||||
StringBuilder current = new StringBuilder(32);
|
||||
current.append('[');
|
||||
current.append(struct.dayFormatter.format(struct.currentDate)); // Day
|
||||
current.append('/');
|
||||
current.append(lookup(struct.monthFormatter
|
||||
.format(struct.currentDate))); // Month
|
||||
current.append('/');
|
||||
current.append(struct.yearFormatter.format(struct.currentDate)); // Year
|
||||
current.append(':');
|
||||
current.append(struct.timeFormatter.format(struct.currentDate)); // Time
|
||||
current.append(']');
|
||||
struct.currentDateString = current.toString();
|
||||
}
|
||||
return struct.currentDateString;
|
||||
}
|
||||
|
||||
private Date getDateLocal() {
|
||||
Struct struct = structLocal.get();
|
||||
long systime = System.currentTimeMillis();
|
||||
if ((systime - struct.currentDate.getTime()) > 1000) {
|
||||
struct.currentDate.setTime(systime);
|
||||
struct.currentDateString = null;
|
||||
}
|
||||
return struct.currentDate;
|
||||
}
|
||||
}
|
||||
|
||||
private static class BenchmarkTest {
|
||||
public void doTest(int threadCount, Runnable[] tests) throws Exception {
|
||||
for (int iterations = 1000000; iterations < 10000001; iterations += 1000000) {
|
||||
for (int i = 0; i < tests.length; i++) {
|
||||
doTestInternal(threadCount, iterations, tests[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void doTestInternal(int threadCount, int iterations,
|
||||
Runnable test) throws Exception {
|
||||
long start = System.currentTimeMillis();
|
||||
Thread[] threads = new Thread[threadCount];
|
||||
for (int i = 0; i < threadCount; i++) {
|
||||
threads[i] = new Thread(new TestThread(iterations, test));
|
||||
}
|
||||
for (int i = 0; i < threadCount; i++) {
|
||||
threads[i].start();
|
||||
}
|
||||
for (int i = 0; i < threadCount; i++) {
|
||||
threads[i].join();
|
||||
}
|
||||
long end = System.currentTimeMillis();
|
||||
|
||||
System.out.println(test.getClass().getSimpleName() + ": "
|
||||
+ threadCount + " threads and " + iterations
|
||||
+ " iterations using " + test + " took " + (end - start)
|
||||
+ "ms");
|
||||
}
|
||||
}
|
||||
|
||||
private static class TestThread implements Runnable {
|
||||
private int count;
|
||||
private Runnable test;
|
||||
|
||||
public TestThread(int count, Runnable test) {
|
||||
this.count = count;
|
||||
this.test = test;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
for (int i = 0; i < count; i++) {
|
||||
test.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user