Skip to content

Commit

Permalink
added first draft of a single-threaded throughput benchmark.
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisvest committed Jun 13, 2012
1 parent 5d8104f commit 9ac14cd
Show file tree
Hide file tree
Showing 7 changed files with 304 additions and 0 deletions.
8 changes: 8 additions & 0 deletions stormpot-benchmark/pom.xml
Expand Up @@ -14,5 +14,13 @@
<artifactId>stormpot-parent</artifactId>
<version>2.1-SNAPSHOT</version>
</parent>

<dependencies>
<dependency>
<groupId>${groupId}</groupId>
<artifactId>stormpot</artifactId>
<version>${version}</version>
</dependency>
</dependencies>
</project>

51 changes: 51 additions & 0 deletions stormpot-benchmark/src/main/java/stormpot/benchmark/Bench.java
@@ -0,0 +1,51 @@
package stormpot.benchmark;

public abstract class Bench {
public abstract void primeWithSize(int size) throws Exception;
public abstract Object claim() throws Exception;
public abstract void release(Object object) throws Exception;
public abstract void claimAndRelease() throws Exception;

private int trials;
private long timeSum;
private long timeMin = Long.MAX_VALUE;
private long timeMax = Long.MIN_VALUE;
private long period;

public final void recordTime(long time) {
trials++;
timeSum += time;
timeMin = Math.min(timeMin, time);
timeMax = Math.max(timeMax, time);
}

public final void recordPeriod(long period) {
this.period = period;
}

public final void reset() {
trials = 0;
timeSum = 0;
timeMin = Long.MAX_VALUE;
timeMax = Long.MIN_VALUE;
period = 0;
}

public final void report() {
String name = getName();
double cyclesPerSec = (1000.0 / period) * trials;
double timeMean = ((double) timeSum) / trials;

String str = "Benchmark: %s\t" +
"%s trials\t" +
"%.1f claim+release/sec\t" +
"latency(max, mean, min) = " +
"(% 3d, %.6f, %s) in millis.\n";
System.out.printf(
str, name, trials, cyclesPerSec, timeMax, timeMean, timeMin);
}

public final String getName() {
return getClass().getSimpleName();
}
}
95 changes: 95 additions & 0 deletions stormpot-benchmark/src/main/java/stormpot/benchmark/Clock.java
@@ -0,0 +1,95 @@
package stormpot.benchmark;

import java.util.concurrent.locks.LockSupport;

/**
* The Clock improves upon {@link System#currentTimeMillis()} in that it can be
* stopped and manually ticked forward. This is useful for testability.
* @author cvh
*
*/
public final class Clock {

private static volatile long now;

private static final Ticker ticker = new Ticker();

private Clock() { /* static utility class */ }

/**
* Advance the clock to the current time.
* @return The new current time, in milliseconds since the epoch.
*/
public static long tick() {
return now = System.currentTimeMillis();
}

/**
* Advance the clock by exactly one millisecond, regardless of what the
* actual time may be.
* Note that this method is racy, and is technically only safe to call by
* a single thread at a time, and when the automatic ticking of the clock
* has been turned off. This method is only really useful for unit testing of
* time dependent code.
*/
public static void inc() {
now++;
}

/**
* Get the value of the last tick.
* @return The "current" time recorded by the last tick.
*/
public static long currentTimeMillis() {
return now;
}

/**
* Start automatic ticking. This will tick the clock, and update its current
* time value, about every 10 milliseconds. Note that this does not guarantee
* that the clock will get a distinctively new time value every 10
* milliseconds. The clock can be stopped again at any time with the
* {@link #stop()} method. Starting an already started clock has no effect.
*/
public static void start() {
tick();
ticker.stopFlag = false;
if (ticker.getState() == Thread.State.NEW) {
ticker.start();
} else {
LockSupport.unpark(ticker);
}
}

/**
* Stop automatic ticking. It can be resumed again with the {@link #start()}
* method. Stopping an already stopped clock has no effect.
*/
public static void stop() {
ticker.stopFlag = true;
}

private static final class Ticker extends Thread {
public volatile boolean stopFlag;

public Ticker() {
super("Clock-Thread");
setDaemon(true);
}

@Override
public void run() {
for (;;) {
if (stopFlag) {
LockSupport.park();
}
tick();
// LockSupport.parkNanos(1000); // 1 millis
try {
Thread.sleep(1);
} catch (InterruptedException e) {
}
}
}
}
}
@@ -0,0 +1,37 @@
package stormpot.benchmark;

import java.util.concurrent.TimeUnit;

import stormpot.Config;
import stormpot.Timeout;
import stormpot.qpool.QueuePool;

public class QueuePoolBench extends Bench {
private static final Timeout timeout =
new Timeout(1000, TimeUnit.MILLISECONDS);

private QueuePool<StormpotPoolable> pool;

@Override
public void primeWithSize(int size) {
Config<StormpotPoolable> config = new Config<StormpotPoolable>();
config.setAllocator(new StormpotPoolableAllocator());
config.setSize(size);
pool = new QueuePool<StormpotPoolable>(config);
}

@Override
public Object claim() throws Exception {
return pool.claim(timeout);
}

@Override
public void release(Object object) throws Exception {
((StormpotPoolable) object).release();
}

@Override
public void claimAndRelease() throws Exception {
release(claim());
}
}
@@ -0,0 +1,17 @@
package stormpot.benchmark;

import stormpot.Poolable;
import stormpot.Slot;

public class StormpotPoolable implements Poolable {
private Slot slot;

public StormpotPoolable(Slot slot) {
this.slot = slot;
}

@Override
public void release() {
slot.release(this);
}
}
@@ -0,0 +1,16 @@
package stormpot.benchmark;

import stormpot.Allocator;
import stormpot.Poolable;
import stormpot.Slot;

public class StormpotPoolableAllocator implements Allocator<Poolable> {
@Override
public Poolable allocate(Slot slot) throws Exception {
return new StormpotPoolable(slot);
}

@Override
public void deallocate(Poolable poolable) throws Exception {
}
}
@@ -0,0 +1,80 @@
package stormpot.benchmark;

/**
* After warm-up, how many times can we claim and release non-expiring objects
* in a given timeframe?
* @author cvh
*/
public class Throughput {
private static final int SIZE = 10;
private static final long TRIAL_TIME_MILLIS = 500L;

public static void main(String[] args) throws Exception {
Clock.start();
System.out.println("Stormpot Single-Threaded Throughput Benchmark");
try {
runBenchmark();
} finally {
System.exit(0);
}
}

private static void runBenchmark() throws Exception {
Bench queuePool = new QueuePoolBench();
queuePool.primeWithSize(SIZE);

warmup(queuePool);
trial(queuePool);
trial(queuePool);
trial(queuePool);
trial(queuePool);
trial(queuePool);
trial(queuePool);
trial(queuePool);
trial(queuePool);
trial(queuePool);
trial(queuePool);
}

private static void warmup(Bench bench) throws Exception {
System.out.println("Warming up " + bench.getName());
int steps = 20;
for (int i = 0; i < steps; i++) {
for (int j = 0; j < 1000; j++) {
benchmark(bench, 1);
}
System.out.printf("%02d/%s.", i, steps);
}
System.out.println("\nWarmup done.");
}

private static void trial(Bench bench) throws Exception {
Thread.sleep(10);
benchmark(bench, TRIAL_TIME_MILLIS);
bench.report();
}

private static void benchmark(Bench bench, long trialTimeMillis) throws Exception {
bench.reset();

long start = Clock.currentTimeMillis();
long deadline = start + trialTimeMillis;
long end = 0L;
do {
end = runCycles(bench, 100);
} while (end < deadline);
bench.recordPeriod(end - start);
}

private static long runCycles(Bench bench, int cycles) throws Exception {
long start;
long end = 0;
for (int i = 0; i < cycles; i++) {
start = Clock.currentTimeMillis();
bench.claimAndRelease();
end = Clock.currentTimeMillis();
bench.recordTime(end - start);
}
return end;
}
}

0 comments on commit 9ac14cd

Please sign in to comment.