Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
added first draft of a single-threaded throughput benchmark.
- Loading branch information
Showing
7 changed files
with
304 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
51 changes: 51 additions & 0 deletions
51
stormpot-benchmark/src/main/java/stormpot/benchmark/Bench.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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
95
stormpot-benchmark/src/main/java/stormpot/benchmark/Clock.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) { | ||
} | ||
} | ||
} | ||
} | ||
} |
37 changes: 37 additions & 0 deletions
37
stormpot-benchmark/src/main/java/stormpot/benchmark/QueuePoolBench.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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()); | ||
} | ||
} |
17 changes: 17 additions & 0 deletions
17
stormpot-benchmark/src/main/java/stormpot/benchmark/StormpotPoolable.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
stormpot-benchmark/src/main/java/stormpot/benchmark/StormpotPoolableAllocator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 { | ||
} | ||
} |
80 changes: 80 additions & 0 deletions
80
stormpot-benchmark/src/main/java/stormpot/benchmark/Throughput.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} |