Skip to content

Commit

Permalink
import high scale project
Browse files Browse the repository at this point in the history
  • Loading branch information
Cliff Moon committed Aug 31, 2010
0 parents commit 5180b95
Show file tree
Hide file tree
Showing 70 changed files with 20,756 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
@@ -0,0 +1,5 @@
.classpath
.cvsignore
.project
bin/*
lib/*
57 changes: 57 additions & 0 deletions README
@@ -0,0 +1,57 @@

IF YOU ARE LOOKING for the drop-in replacement for java.util.Hashtable, it's
in the lib directory, lib/java_util_hashtable.jar. It needs to be in your
bootclasspath. Example:

java -Xbootclasspath/p:lib/java_util_hashtable.jar my_java_app_goes_here


---

A collection of Concurrent and Highly Scalable Utilities. These are intended
as direct replacements for the java.util.* or java.util.concurrent.*
collections but with better performance when many CPUs are using the
collection concurrently. Single-threaded performance may be slightly lower.

The direct replacements match the API - but not all behaviors are covered by
the API, and so they may not work for your program. In particular, the
replacement for java.util.Hashtable is NOT synchronized (that is the point!),
although it is multi-threaded safe. If you rely on the undocumented
synchronization behavior of the JDK Hashtable, your program may not work.
Similarly, the iteration order is different between this version and the JDK
version (this exact issue broke the SpecJBB benchmark when the iteration order
was changed slightly (via using a slightly different hash function) between
JDK rev's).

If you want to drop-in the non-blocking versions of Hashtable, HashMap or
ConcurrentHashMap, you'll need to alter your bootclasspath - these classes
come directly from your JDK and so are found via the System loader before any
class-path hacks can be done.

To replace the JDK implementation of Hashtable with a non-blocking version of
Hashtable, add java_util_hashtable.jar to your java launch line:

java -Xbootclasspath/p:lib/java_util_hashtable.jar my_app_goes_here

Similarly for ConcurrentHashMap, add java_util_concurrent_chm.jar:

java -Xbootclasspath/p:lib/java_util_concurrent_chm.jar my_app_goes_here


The other utilities do not have direct JDK replacements; you need to call them
out directly and place high_scale_lib.jar in your classpath:

- NonBlockingHashMap - Fast, concurrent, lock-free HashMap. Linear scaling to 768 CPUs.
- NonBlockingHashMapLong - Same as above, but using primitive 'long' keys
- NonBlockingHashSet - A Set version of NBHM
- NonBlockingSetInt - A fast fully concurrent BitVector
- Counter - A simple counter that scales linearly even when extremely hot.
Most simple counters are either unsynchronized (hence drop counts, generally
really badly beyond 2 cpus), or are normally lock'd (hence bottleneck in the
5-10 cpu range), or might use Atomic's (hence bottleneck in the 25-50 cpu
range). This version scales linearly to 768 CPUs.



Cliff Click

8 changes: 8 additions & 0 deletions Testing/CAT_Tester/AtomicCounter.java
@@ -0,0 +1,8 @@

import java.util.concurrent.atomic.*;
public final class AtomicCounter extends Counter {
public String name() { return "Atomic"; }
private final AtomicLong _cnt = new AtomicLong();
public long get(){ return _cnt.get(); }
public void add( long x ) { _cnt.getAndAdd(x); }
}
5 changes: 5 additions & 0 deletions Testing/CAT_Tester/BUILD.sh
@@ -0,0 +1,5 @@
# Simple build line
#
set JAVA_HOME=/usr/local/j2sdk1.5.0_06
javac -classpath $JAVA_HOME/jre/lib/rt.jar:. harness.java org/cliffc/high_scale_lib/*.java ../org/cliffc/high_scale_lib/*.java

10 changes: 10 additions & 0 deletions Testing/CAT_Tester/CATCounter.java
@@ -0,0 +1,10 @@

import org.cliffc.high_scale_lib.*;
public final class CATCounter extends Counter {
public String name() { return "CAT"; }
private final ConcurrentAutoTable _tab = new ConcurrentAutoTable();
public long get(){ return _tab.get(); }
public void add( long x ) { _tab.add(x); }
public void print() { _tab.print(); }
public int internal_size() { return _tab.internal_size(); }
}
11 changes: 11 additions & 0 deletions Testing/CAT_Tester/Counter.java
@@ -0,0 +1,11 @@
//package org.cliffc.high_scale_lib;
public abstract class Counter {
public abstract String name();
public abstract long get();
public abstract void add( long x );
public long pre_add ( long x ) { long l = get(); add(x); return l; }
public long post_add( long x ) { add(x); long l = get(); return l; }
public long post_inc() { return post_add( 1); }
public long pre_dec() { return pre_add(-1); }
}

212 changes: 212 additions & 0 deletions Testing/CAT_Tester/Harness.java
@@ -0,0 +1,212 @@
/*
* 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;
}

}
16 changes: 16 additions & 0 deletions Testing/CAT_Tester/LockCounter.java
@@ -0,0 +1,16 @@
import java.util.concurrent.locks.*;

public final class LockCounter extends Counter {
public String name() { return "Lock"; }
private final ReentrantLock _lock = new ReentrantLock();
private long _cnt;
public long get(){ return _cnt; }
public void add( long x ) {
try {
_lock.lock();
_cnt += x;
} finally {
_lock.unlock();
}
}
}
12 changes: 12 additions & 0 deletions Testing/CAT_Tester/README.txt
@@ -0,0 +1,12 @@

A testing harness for scalable counters.

There are some counter implementations in the local java/util which
must be in the bootclasspath (they use Unsafe).

The main counter is ConcurrentAutoTable.java, which is kept in the
top-level java/util directory, so I end up with 2 java/util
directories: one for the testing harness & strawman counter
implementations, one for the main implementation.


6 changes: 6 additions & 0 deletions Testing/CAT_Tester/RaceyCounter.java
@@ -0,0 +1,6 @@
public final class RaceyCounter extends Counter {
private long _cnt;
public long get(){ return _cnt; }
public void add( long x ) { _cnt += x; }
public String name() { return "Racey"; }
}
33 changes: 33 additions & 0 deletions Testing/CAT_Tester/StripeLockCounter.java
@@ -0,0 +1,33 @@

import java.util.concurrent.locks.*;

public final class StripeLockCounter extends Counter {
private final int _stripes;
private final ReentrantLock[] _locks;
private final long _cnts[];
StripeLockCounter(int stripes) {
_stripes = stripes;
_locks = new ReentrantLock[stripes];
_cnts = new long[stripes];
for( int i=0; i<stripes; i++ )
_locks[i] = new ReentrantLock();
}
public String name() { return "Locks"+_stripes; }
public long get() {
long sum = 0;
for( int i=0; i<_cnts.length; i++ )
sum += _cnts[i];
return sum;
}
public void add( long x ) {
int hash = System.identityHashCode( Thread.currentThread());
int idx = hash & (_locks.length-1);
final Lock l = _locks[idx];
try {
l.lock();
_cnts [idx] += x;
} finally {
l.unlock();
}
}
}

0 comments on commit 5180b95

Please sign in to comment.