2015-04-20 15:36:01 -07:00
|
|
|
//: concurrency/SynchronizationComparisons.java
|
|
|
|
// Comparing the performance of explicit Locks
|
|
|
|
// and Atomics versus the synchronized keyword.
|
|
|
|
import java.util.concurrent.*;
|
|
|
|
import java.util.concurrent.atomic.*;
|
|
|
|
import java.util.concurrent.locks.*;
|
|
|
|
import java.util.*;
|
|
|
|
import static net.mindview.util.Print.*;
|
|
|
|
|
|
|
|
abstract class Accumulator {
|
|
|
|
public static long cycles = 50000L;
|
|
|
|
// Number of Modifiers and Readers during each test:
|
|
|
|
private static final int N = 4;
|
|
|
|
public static ExecutorService exec =
|
|
|
|
Executors.newFixedThreadPool(N*2);
|
|
|
|
private static CyclicBarrier barrier =
|
|
|
|
new CyclicBarrier(N*2 + 1);
|
|
|
|
protected volatile int index = 0;
|
|
|
|
protected volatile long value = 0;
|
|
|
|
protected long duration = 0;
|
|
|
|
protected String id = "error";
|
|
|
|
protected final static int SIZE = 100000;
|
|
|
|
protected static int[] preLoaded = new int[SIZE];
|
2015-04-22 09:30:08 -07:00
|
|
|
static { // Load the array of random numbers:
|
2015-04-20 15:36:01 -07:00
|
|
|
Random rand = new Random(47);
|
|
|
|
for(int i = 0; i < SIZE; i++)
|
|
|
|
preLoaded[i] = rand.nextInt();
|
|
|
|
}
|
|
|
|
public abstract void accumulate();
|
|
|
|
public abstract long read();
|
|
|
|
private class Modifier implements Runnable {
|
2015-05-05 11:20:13 -07:00
|
|
|
@Override
|
2015-04-20 15:36:01 -07:00
|
|
|
public void run() {
|
|
|
|
for(long i = 0; i < cycles; i++)
|
|
|
|
accumulate();
|
|
|
|
try {
|
|
|
|
barrier.await();
|
2015-05-06 12:09:38 -07:00
|
|
|
} catch(InterruptedException |
|
|
|
|
BrokenBarrierException e) {
|
2015-04-20 15:36:01 -07:00
|
|
|
throw new RuntimeException(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
private class Reader implements Runnable {
|
|
|
|
private volatile long value;
|
2015-05-05 11:20:13 -07:00
|
|
|
@Override
|
2015-04-20 15:36:01 -07:00
|
|
|
public void run() {
|
|
|
|
for(long i = 0; i < cycles; i++)
|
|
|
|
value = read();
|
|
|
|
try {
|
|
|
|
barrier.await();
|
2015-05-06 12:09:38 -07:00
|
|
|
} catch(InterruptedException |
|
|
|
|
BrokenBarrierException e) {
|
2015-04-20 15:36:01 -07:00
|
|
|
throw new RuntimeException(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
public void timedTest() {
|
|
|
|
long start = System.nanoTime();
|
|
|
|
for(int i = 0; i < N; i++) {
|
|
|
|
exec.execute(new Modifier());
|
|
|
|
exec.execute(new Reader());
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
barrier.await();
|
2015-05-06 12:09:38 -07:00
|
|
|
} catch(InterruptedException |
|
|
|
|
BrokenBarrierException e) {
|
2015-04-20 15:36:01 -07:00
|
|
|
throw new RuntimeException(e);
|
|
|
|
}
|
|
|
|
duration = System.nanoTime() - start;
|
|
|
|
printf("%-13s: %13d\n", id, duration);
|
|
|
|
}
|
2015-04-22 09:30:08 -07:00
|
|
|
public void report(Accumulator acc2) {
|
|
|
|
printf("%-22s: %.2f\n", this.id + "/" + acc2.id,
|
|
|
|
(double)this.duration/(double)acc2.duration);
|
2015-04-20 15:36:01 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class SynchronizedTest extends Accumulator {
|
2015-04-22 09:30:08 -07:00
|
|
|
{ id = "synch"; }
|
2015-05-05 11:20:13 -07:00
|
|
|
@Override
|
2015-04-20 15:36:01 -07:00
|
|
|
public synchronized void accumulate() {
|
|
|
|
value += preLoaded[index++];
|
|
|
|
if(index >= SIZE) index = 0;
|
|
|
|
}
|
2015-05-05 11:20:13 -07:00
|
|
|
@Override
|
2015-04-20 15:36:01 -07:00
|
|
|
public synchronized long read() {
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class LockTest extends Accumulator {
|
|
|
|
{ id = "Lock"; }
|
|
|
|
private Lock lock = new ReentrantLock();
|
2015-05-05 11:20:13 -07:00
|
|
|
@Override
|
2015-04-20 15:36:01 -07:00
|
|
|
public void accumulate() {
|
|
|
|
lock.lock();
|
|
|
|
try {
|
|
|
|
value += preLoaded[index++];
|
|
|
|
if(index >= SIZE) index = 0;
|
|
|
|
} finally {
|
|
|
|
lock.unlock();
|
|
|
|
}
|
|
|
|
}
|
2015-05-05 11:20:13 -07:00
|
|
|
@Override
|
2015-04-20 15:36:01 -07:00
|
|
|
public long read() {
|
|
|
|
lock.lock();
|
|
|
|
try {
|
|
|
|
return value;
|
|
|
|
} finally {
|
|
|
|
lock.unlock();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class AtomicTest extends Accumulator {
|
|
|
|
{ id = "Atomic"; }
|
|
|
|
private AtomicInteger index = new AtomicInteger(0);
|
|
|
|
private AtomicLong value = new AtomicLong(0);
|
2015-04-22 09:30:08 -07:00
|
|
|
// Relying on more than one Atomic at a time doesn't
|
|
|
|
// work, so we still have to synchronize. But it gives
|
|
|
|
// a performance indicator:
|
2015-05-05 11:20:13 -07:00
|
|
|
@Override
|
2015-04-22 09:30:08 -07:00
|
|
|
public synchronized void accumulate() {
|
|
|
|
int i;
|
|
|
|
i = index.getAndIncrement();
|
2015-04-20 15:36:01 -07:00
|
|
|
value.getAndAdd(preLoaded[i]);
|
|
|
|
if(++i >= SIZE)
|
|
|
|
index.set(0);
|
|
|
|
}
|
2015-05-05 11:20:13 -07:00
|
|
|
@Override
|
2015-04-22 09:30:08 -07:00
|
|
|
public synchronized long read() { return value.get(); }
|
2015-05-05 11:20:13 -07:00
|
|
|
@Override
|
2015-04-22 09:30:08 -07:00
|
|
|
public void report(Accumulator acc2) {
|
|
|
|
printf("%-22s: %.2f\n", "synch/(Atomic-synch)",
|
|
|
|
(double)acc2.duration/
|
|
|
|
((double)this.duration - (double)acc2.duration));
|
|
|
|
}
|
2015-04-20 15:36:01 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
public class SynchronizationComparisons {
|
|
|
|
static SynchronizedTest synch = new SynchronizedTest();
|
|
|
|
static LockTest lock = new LockTest();
|
|
|
|
static AtomicTest atomic = new AtomicTest();
|
|
|
|
static void test() {
|
|
|
|
print("============================");
|
|
|
|
printf("%-12s : %13d\n", "Cycles", Accumulator.cycles);
|
|
|
|
synch.timedTest();
|
|
|
|
lock.timedTest();
|
|
|
|
atomic.timedTest();
|
2015-04-22 09:30:08 -07:00
|
|
|
synch.report(lock);
|
|
|
|
atomic.report(synch);
|
2015-04-20 15:36:01 -07:00
|
|
|
}
|
|
|
|
public static void main(String[] args) {
|
|
|
|
int iterations = 5; // Default
|
|
|
|
if(args.length > 0) // Optionally change iterations
|
|
|
|
iterations = new Integer(args[0]);
|
|
|
|
// The first time fills the thread pool:
|
|
|
|
print("Warmup");
|
2015-04-22 09:30:08 -07:00
|
|
|
synch.timedTest();
|
2015-04-20 15:36:01 -07:00
|
|
|
// Now the initial test doesn't include the cost
|
|
|
|
// of starting the threads for the first time.
|
|
|
|
// Produce multiple data points:
|
|
|
|
for(int i = 0; i < iterations; i++) {
|
|
|
|
test();
|
|
|
|
Accumulator.cycles *= 2;
|
|
|
|
}
|
|
|
|
Accumulator.exec.shutdown();
|
|
|
|
}
|
2015-05-18 23:05:20 -07:00
|
|
|
} /* Output: (Sample)
|
2015-04-20 15:36:01 -07:00
|
|
|
Warmup
|
2015-05-18 23:05:20 -07:00
|
|
|
synch : 94513807
|
2015-04-20 15:36:01 -07:00
|
|
|
============================
|
|
|
|
Cycles : 50000
|
2015-05-18 23:05:20 -07:00
|
|
|
synch : 94514234
|
|
|
|
Lock : 24976352
|
|
|
|
Atomic : 98296651
|
|
|
|
synch/Lock : 3.78
|
|
|
|
synch/(Atomic-synch) : 24.99
|
2015-04-20 15:36:01 -07:00
|
|
|
============================
|
|
|
|
Cycles : 100000
|
2015-05-18 23:05:20 -07:00
|
|
|
synch : 178531353
|
|
|
|
Lock : 46007787
|
|
|
|
Atomic : 192372561
|
|
|
|
synch/Lock : 3.88
|
|
|
|
synch/(Atomic-synch) : 12.90
|
2015-04-20 15:36:01 -07:00
|
|
|
============================
|
|
|
|
Cycles : 200000
|
2015-05-18 23:05:20 -07:00
|
|
|
synch : 377107591
|
|
|
|
Lock : 91051260
|
|
|
|
Atomic : 394509274
|
|
|
|
synch/Lock : 4.14
|
|
|
|
synch/(Atomic-synch) : 21.67
|
2015-04-20 15:36:01 -07:00
|
|
|
============================
|
|
|
|
Cycles : 400000
|
2015-05-18 23:05:20 -07:00
|
|
|
synch : 722152518
|
|
|
|
Lock : 184968090
|
|
|
|
Atomic : 746950974
|
|
|
|
synch/Lock : 3.90
|
|
|
|
synch/(Atomic-synch) : 29.12
|
2015-04-20 15:36:01 -07:00
|
|
|
============================
|
|
|
|
Cycles : 800000
|
2015-05-18 23:05:20 -07:00
|
|
|
synch : 1478348925
|
|
|
|
Lock : 416729956
|
|
|
|
Atomic : 1559738238
|
|
|
|
synch/Lock : 3.55
|
|
|
|
synch/(Atomic-synch) : 18.16
|
2015-04-20 15:36:01 -07:00
|
|
|
*///:~
|