|
| 1 | +//: concurrency/SynchronizationComparisons.java |
| 2 | +// Comparing the performance of explicit Locks |
| 3 | +// and Atomics versus the synchronized keyword. |
| 4 | +import java.util.concurrent.*; |
| 5 | +import java.util.concurrent.atomic.*; |
| 6 | +import java.util.concurrent.locks.*; |
| 7 | +import java.util.*; |
| 8 | +import static net.mindview.util.Print.*; |
| 9 | + |
| 10 | +abstract class Accumulator { |
| 11 | + public static long cycles = 50000L; |
| 12 | + // Number of Modifiers and Readers during each test: |
| 13 | + private static final int N = 4; |
| 14 | + public static ExecutorService exec = |
| 15 | + Executors.newFixedThreadPool(N*2); |
| 16 | + private static CyclicBarrier barrier = |
| 17 | + new CyclicBarrier(N*2 + 1); |
| 18 | + protected volatile int index = 0; |
| 19 | + protected volatile long value = 0; |
| 20 | + protected long duration = 0; |
| 21 | + protected String id = "error"; |
| 22 | + protected final static int SIZE = 100000; |
| 23 | + protected static int[] preLoaded = new int[SIZE]; |
| 24 | + static { |
| 25 | + // Load the array of random numbers: |
| 26 | + Random rand = new Random(47); |
| 27 | + for(int i = 0; i < SIZE; i++) |
| 28 | + preLoaded[i] = rand.nextInt(); |
| 29 | + } |
| 30 | + public abstract void accumulate(); |
| 31 | + public abstract long read(); |
| 32 | + private class Modifier implements Runnable { |
| 33 | + public void run() { |
| 34 | + for(long i = 0; i < cycles; i++) |
| 35 | + accumulate(); |
| 36 | + try { |
| 37 | + barrier.await(); |
| 38 | + } catch(Exception e) { |
| 39 | + throw new RuntimeException(e); |
| 40 | + } |
| 41 | + } |
| 42 | + } |
| 43 | + private class Reader implements Runnable { |
| 44 | + private volatile long value; |
| 45 | + public void run() { |
| 46 | + for(long i = 0; i < cycles; i++) |
| 47 | + value = read(); |
| 48 | + try { |
| 49 | + barrier.await(); |
| 50 | + } catch(Exception e) { |
| 51 | + throw new RuntimeException(e); |
| 52 | + } |
| 53 | + } |
| 54 | + } |
| 55 | + public void timedTest() { |
| 56 | + long start = System.nanoTime(); |
| 57 | + for(int i = 0; i < N; i++) { |
| 58 | + exec.execute(new Modifier()); |
| 59 | + exec.execute(new Reader()); |
| 60 | + } |
| 61 | + try { |
| 62 | + barrier.await(); |
| 63 | + } catch(Exception e) { |
| 64 | + throw new RuntimeException(e); |
| 65 | + } |
| 66 | + duration = System.nanoTime() - start; |
| 67 | + printf("%-13s: %13d\n", id, duration); |
| 68 | + } |
| 69 | + public static void |
| 70 | + report(Accumulator acc1, Accumulator acc2) { |
| 71 | + printf("%-22s: %.2f\n", acc1.id + "/" + acc2.id, |
| 72 | + (double)acc1.duration/(double)acc2.duration); |
| 73 | + } |
| 74 | +} |
| 75 | + |
| 76 | +class BaseLine extends Accumulator { |
| 77 | + { id = "BaseLine"; } |
| 78 | + public void accumulate() { |
| 79 | + if(index >= SIZE - 1) index = 0; |
| 80 | + value += preLoaded[index++]; |
| 81 | + } |
| 82 | + public long read() { return value; } |
| 83 | +} |
| 84 | + |
| 85 | +class SynchronizedTest extends Accumulator { |
| 86 | + { id = "synchronized"; } |
| 87 | + public synchronized void accumulate() { |
| 88 | + if(index >= SIZE - 1) index = 0; |
| 89 | + value += preLoaded[index++]; |
| 90 | + } |
| 91 | + public synchronized long read() { |
| 92 | + return value; |
| 93 | + } |
| 94 | +} |
| 95 | + |
| 96 | +class LockTest extends Accumulator { |
| 97 | + { id = "Lock"; } |
| 98 | + private Lock lock = new ReentrantLock(); |
| 99 | + public void accumulate() { |
| 100 | + lock.lock(); |
| 101 | + try { |
| 102 | + if(index >= SIZE - 1) index = 0; |
| 103 | + value += preLoaded[index++]; |
| 104 | + } finally { |
| 105 | + lock.unlock(); |
| 106 | + } |
| 107 | + } |
| 108 | + public long read() { |
| 109 | + lock.lock(); |
| 110 | + try { |
| 111 | + return value; |
| 112 | + } finally { |
| 113 | + lock.unlock(); |
| 114 | + } |
| 115 | + } |
| 116 | +} |
| 117 | + |
| 118 | +class AtomicTest extends Accumulator { |
| 119 | + { id = "Atomic"; } |
| 120 | + private AtomicInteger index = new AtomicInteger(0); |
| 121 | + private AtomicLong value = new AtomicLong(0); |
| 122 | + public void accumulate() { |
| 123 | + // Oops! Relying on more than one Atomic at |
| 124 | + // a time doesn't work. But it still gives us |
| 125 | + // a performance indicator: |
| 126 | + int i = index.getAndIncrement(); |
| 127 | + if(++i >= SIZE - 1) { |
| 128 | + i = 0; |
| 129 | + index.set(i); |
| 130 | + } |
| 131 | + value.getAndAdd(preLoaded[i]); |
| 132 | + } |
| 133 | + public long read() { return value.get(); } |
| 134 | +} |
| 135 | + |
| 136 | +public class SynchronizationComparisons { |
| 137 | + static BaseLine baseLine = new BaseLine(); |
| 138 | + static SynchronizedTest synch = new SynchronizedTest(); |
| 139 | + static LockTest lock = new LockTest(); |
| 140 | + static AtomicTest atomic = new AtomicTest(); |
| 141 | + static void test() { |
| 142 | + print("============================"); |
| 143 | + printf("%-12s : %13d\n", "Cycles", Accumulator.cycles); |
| 144 | + baseLine.timedTest(); |
| 145 | + synch.timedTest(); |
| 146 | + lock.timedTest(); |
| 147 | + atomic.timedTest(); |
| 148 | + Accumulator.report(synch, baseLine); |
| 149 | + Accumulator.report(lock, baseLine); |
| 150 | + Accumulator.report(atomic, baseLine); |
| 151 | + Accumulator.report(synch, lock); |
| 152 | + Accumulator.report(synch, atomic); |
| 153 | + Accumulator.report(lock, atomic); |
| 154 | + } |
| 155 | + public static void main(String[] args) { |
| 156 | + int iterations = 5; // Default |
| 157 | + if(args.length > 0) // Optionally change iterations |
| 158 | + iterations = new Integer(args[0]); |
| 159 | + // The first time fills the thread pool: |
| 160 | + print("Warmup"); |
| 161 | + baseLine.timedTest(); |
| 162 | + // Now the initial test doesn't include the cost |
| 163 | + // of starting the threads for the first time. |
| 164 | + // Produce multiple data points: |
| 165 | + for(int i = 0; i < iterations; i++) { |
| 166 | + test(); |
| 167 | + Accumulator.cycles *= 2; |
| 168 | + } |
| 169 | + Accumulator.exec.shutdown(); |
| 170 | + } |
| 171 | +} /* Output: (Sample) |
| 172 | +Warmup |
| 173 | +BaseLine : 34237033 |
| 174 | +============================ |
| 175 | +Cycles : 50000 |
| 176 | +BaseLine : 20966632 |
| 177 | +synchronized : 24326555 |
| 178 | +Lock : 53669950 |
| 179 | +Atomic : 30552487 |
| 180 | +synchronized/BaseLine : 1.16 |
| 181 | +Lock/BaseLine : 2.56 |
| 182 | +Atomic/BaseLine : 1.46 |
| 183 | +synchronized/Lock : 0.45 |
| 184 | +synchronized/Atomic : 0.79 |
| 185 | +Lock/Atomic : 1.76 |
| 186 | +============================ |
| 187 | +Cycles : 100000 |
| 188 | +BaseLine : 41512818 |
| 189 | +synchronized : 43843003 |
| 190 | +Lock : 87430386 |
| 191 | +Atomic : 51892350 |
| 192 | +synchronized/BaseLine : 1.06 |
| 193 | +Lock/BaseLine : 2.11 |
| 194 | +Atomic/BaseLine : 1.25 |
| 195 | +synchronized/Lock : 0.50 |
| 196 | +synchronized/Atomic : 0.84 |
| 197 | +Lock/Atomic : 1.68 |
| 198 | +============================ |
| 199 | +Cycles : 200000 |
| 200 | +BaseLine : 80176670 |
| 201 | +synchronized : 5455046661 |
| 202 | +Lock : 177686829 |
| 203 | +Atomic : 101789194 |
| 204 | +synchronized/BaseLine : 68.04 |
| 205 | +Lock/BaseLine : 2.22 |
| 206 | +Atomic/BaseLine : 1.27 |
| 207 | +synchronized/Lock : 30.70 |
| 208 | +synchronized/Atomic : 53.59 |
| 209 | +Lock/Atomic : 1.75 |
| 210 | +============================ |
| 211 | +Cycles : 400000 |
| 212 | +BaseLine : 160383513 |
| 213 | +synchronized : 780052493 |
| 214 | +Lock : 362187652 |
| 215 | +Atomic : 202030984 |
| 216 | +synchronized/BaseLine : 4.86 |
| 217 | +Lock/BaseLine : 2.26 |
| 218 | +Atomic/BaseLine : 1.26 |
| 219 | +synchronized/Lock : 2.15 |
| 220 | +synchronized/Atomic : 3.86 |
| 221 | +Lock/Atomic : 1.79 |
| 222 | +============================ |
| 223 | +Cycles : 800000 |
| 224 | +BaseLine : 322064955 |
| 225 | +synchronized : 336155014 |
| 226 | +Lock : 704615531 |
| 227 | +Atomic : 393231542 |
| 228 | +synchronized/BaseLine : 1.04 |
| 229 | +Lock/BaseLine : 2.19 |
| 230 | +Atomic/BaseLine : 1.22 |
| 231 | +synchronized/Lock : 0.47 |
| 232 | +synchronized/Atomic : 0.85 |
| 233 | +Lock/Atomic : 1.79 |
| 234 | +============================ |
| 235 | +Cycles : 1600000 |
| 236 | +BaseLine : 650004120 |
| 237 | +synchronized : 52235762925 |
| 238 | +Lock : 1419602771 |
| 239 | +Atomic : 796950171 |
| 240 | +synchronized/BaseLine : 80.36 |
| 241 | +Lock/BaseLine : 2.18 |
| 242 | +Atomic/BaseLine : 1.23 |
| 243 | +synchronized/Lock : 36.80 |
| 244 | +synchronized/Atomic : 65.54 |
| 245 | +Lock/Atomic : 1.78 |
| 246 | +============================ |
| 247 | +Cycles : 3200000 |
| 248 | +BaseLine : 1285664519 |
| 249 | +synchronized : 96336767661 |
| 250 | +Lock : 2846988654 |
| 251 | +Atomic : 1590545726 |
| 252 | +synchronized/BaseLine : 74.93 |
| 253 | +Lock/BaseLine : 2.21 |
| 254 | +Atomic/BaseLine : 1.24 |
| 255 | +synchronized/Lock : 33.84 |
| 256 | +synchronized/Atomic : 60.57 |
| 257 | +Lock/Atomic : 1.79 |
| 258 | +*///:~ |
0 commit comments