/*
* Written by Cliff Click and released to the public domain, as explained at
* http://creativecommons.org/licenses/publicdomain
*/
public class Harness extends Thread {
static int _thread_min, _thread_max, _thread_incr;
static int _ctr_impl;
static Counter make_ctr( final int impl ) {
switch( impl ) {
case 1: return new RaceyCounter();
case 2: return new SyncCounter();
case 3: return new LockCounter();
case 4: return new AtomicCounter();
case 5: return new UnsafeCounter();
case 6: return new StripeLockCounter( 8);
case 7: return new StripeUnsafeCounter( 8);
case 8: return new StripeLockCounter( 64);
case 9: return new StripeUnsafeCounter( 64);
case 10: return new StripeLockCounter(256);
case 11: return new StripeUnsafeCounter(256);
case 12: return new CATCounter();
default:
throw new Error("Bad imple");
}
}
static volatile boolean _start;
static volatile boolean _stop;
static final int NUM_CPUS = Runtime.getRuntime().availableProcessors();
static int check( String arg, String msg, int lower, int upper ) {
return check( Integer.parseInt(arg), msg, lower, upper );
}
static int check( int x, String msg, int lower, int upper ) {
if( x < lower || x > upper )
throw new Error(msg+" must be from "+lower+" to "+upper);
return x;
}
public static void main( String args[] ) {
// Parse args
try {
_thread_min = check( args[0], "thread_min", 1, 100000 );
_thread_max = check( args[1], "thread_max", 1, 100000 );
_thread_incr = check( args[2], "thread_incr", 1, 100000 );
_ctr_impl = check( args[3], "implementation", -1, 13 );
int trips = (_thread_max - _thread_min)/_thread_incr;
_thread_max = trips*_thread_incr + _thread_min;
} catch( Error e ) {
System.out.println("Usage: harness thread-min thread-max thread-incr impl[All=0]");
throw e;
}
String name = _ctr_impl == 0 ? "ALL" : (_ctr_impl==-1 ? "Best" : make_ctr(_ctr_impl).name());
System.out.println("===== "+name+" =====");
System.out.println("Threads from "+_thread_min+" to "+_thread_max+" by "+_thread_incr);
// Do some warmup
System.out.println("==== Warmup -variance: ");
run_till_stable(Math.min(_thread_min,2),1);
// Now do the real thing
int num_trials = 7; // Number of Trials
System.out.print("==== Counter Threads Trial:");
for( int i=0; i<num_trials; i++ )
System.out.printf(" %3d ",i);
System.out.println(" Average");
for( int i=_thread_min; i<=_thread_max; i += _thread_incr )
run_till_stable( i, num_trials );
}
static void run_till_stable( int num_threads, int num_trials ) {
if( _ctr_impl > 0 ) {
run_till_stable(num_threads,num_trials,_ctr_impl);
} else if( _ctr_impl == 0 ) {
for( int impl=1;impl<13; impl++ )
run_till_stable(num_threads,num_trials,impl);
System.out.println();
} else {
run_till_stable(num_threads,num_trials,11); // big stripage Unsafe
run_till_stable(num_threads,num_trials,12); // CAT
}
}
static void run_till_stable( int num_threads, int num_trials, int impl ) {
Counter C = make_ctr(impl);
System.out.printf("=== %10.10s %3d cnts/sec=",C.name(),num_threads);
long[] trials = new long[num_trials]; // Number of trials
long total_ops = 0; // Total ops altogether
long total_ops_sec = 0; // Sum of ops/sec for each run
// Run some trials
for( int j=0; j<trials.length; j++ ) {
long[] ops = new long[num_threads];
long millis = run_once(num_threads,C,ops);
long sum = 0;
for( int i=0; i<num_threads; i++ )
sum += ops[i];
total_ops += sum;
sum = sum*1000L/millis;
trials[j] = sum;
total_ops_sec += sum;
System.out.printf(" %10d",sum);
}
// Compute nice trial results
if( trials.length > 2 ) {
// Toss out low & high
int lo=0;
int hi=0;
for( int j=1; j<trials.length; j++ ) {
if( trials[lo] < trials[j] ) lo=j;
if( trials[hi] > trials[j] ) hi=j;
}
long total2 = total_ops_sec - (trials[lo]+trials[hi]);
trials[lo] = trials[trials.length-1];
trials[hi] = trials[trials.length-2];
// Print avg,stddev
long avg = total2/(trials.length-2);
long stddev = compute_stddev(trials,trials.length-2);
long p = stddev*100/avg; // std-dev as a percent
System.out.printf(" %10d",avg);
System.out.printf(" (+/-%2d%%)",p);
}
long loss = total_ops - C.get();
if( loss != 0 ) {
System.out.print(" Lossage=");
int loss_per = (int)(loss*100/total_ops);
System.out.print(loss_per == 0 ? (""+loss) : (""+loss_per+"%"));
}
if( C instanceof CATCounter ) {
CATCounter cat = (CATCounter)C;
System.out.print(" autotable="+ cat.internal_size());
if( loss != 0 ) cat.print();
}
System.out.println();
}
static long compute_stddev(long[] trials, int len) {
double sum = 0;
double squ = 0.0;
for( int i=0; i<len; i++ ) {
double d = (double)trials[i];
sum += d;
squ += d*d;
}
double x = squ - sum*sum/len;
double stddev = Math.sqrt(x/(len-1));
return (long)stddev;
}
final int _tnum;
final Counter _C;
final long[] _ops;
Harness( int tnum, Counter C, long[] ops ) { _tnum = tnum; _C = C; _ops = ops; }
static long run_once( int num_threads, Counter C, long[] ops ) {
_start = false;
_stop = false;
// Launch threads
Harness thrs[] = new Harness[num_threads];
for( int i=0; i<num_threads; i++ ) {
thrs[i] = new Harness(i, C, ops);
//int h1 = System.identityHashCode(thrs[i]);
//int h2 = h1;
//h2 ^= (h2>>>20) ^ (h2>>>12);
//h2 ^= (h2>>> 7) ^ (h2>>> 4);
//System.out.printf("%x ",h1&0xfff);
}
//System.out.println("");
for( int i=0; i<num_threads; i++ )
thrs[i].start();
// Run threads
long start = System.currentTimeMillis();
_start = true;
try { Thread.sleep(2000); } catch( InterruptedException e ){}
_stop = true;
long stop = System.currentTimeMillis();
long millis = stop-start;
for( int i=0; i<num_threads; i++ ) {
try {
thrs[i].join();
} catch( InterruptedException e ) { }
}
return millis;
}
// What a worker thread does
public void run() {
while( !_start ) // Spin till Time To Go
try { Thread.sleep(1); } catch( InterruptedException e ){}
int ops = 0;
while( !_stop ) {
ops++;
_C.add(1);
}
// We stopped; report results into shared result structure
_ops[_tnum] = ops;
}
}