ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/test/loops/CASLoops.java
Revision: 1.1
Committed: Fri Jun 10 15:45:19 2005 UTC (18 years, 11 months ago) by dl
Branch: MAIN
Log Message:
Add CAS timing test; use cheaper RNGs in others

File Contents

# Content
1 /*
2 * Written by Doug Lea and released to the public domain, as explained at
3 * http://creativecommons.org/licenses/publicdomain
4 */
5
6 /*
7 * Estimates the difference in time for compareAndSet and CAS-like
8 * operations versus unsynchronized, non-volatile pseudo-CAS when
9 * updating random numbers. These estimates thus give the cost
10 * of atomicity/barriers/exclusion over and above the time to
11 * just compare and conditionally store (int) values, so are
12 * not intended to measure the "raw" cost of a CAS.
13 *
14 * Outputs, for runs using 1 ... #cpus threads are, in nanoseconds:
15 * "Atomic CAS" AtomicInteger.compareAndSet
16 * "Volatile" pseudo-CAS using volatile store if comparison succeeds
17 * "Mutex" emulated compare and set done under AQS-based mutex lock
18 * "Synchronized" emulated compare and set done under a synchronized block.
19 *
20 * The last two are done only if this program is called with (any) argument
21 */
22
23
24 import java.util.concurrent.atomic.AtomicInteger;
25 import java.util.concurrent.atomic.AtomicLong;
26 import java.util.concurrent.*;
27 import java.util.concurrent.locks.*;
28
29 public class CASLoops {
30
31 static final int TRIALS = 2;
32 static final long BASE_SECS_PER_RUN = 4;
33 static final int NCPUS = Runtime.getRuntime().availableProcessors();
34
35 static boolean includeLocks = false;
36
37 public static void main(String[] args) throws Exception {
38 if (args.length > 0)
39 includeLocks = true;
40
41 System.out.println("Warmup...");
42 for (int j = 0; j < 2; ++j) {
43 for (int i = 1; i <= NCPUS; ++i) {
44 runCalibration(i, 1000);
45 oneRun(i, loopIters[i] / 8, false);
46 }
47 }
48
49 for (int i = 1; i <= NCPUS; ++i)
50 loopIters[i] = 0;
51
52 for (int j = 0; j < TRIALS; ++j) {
53 System.out.println("Trial " + j);
54 for (int i = 1; i <= NCPUS; ++i) {
55 runCalibration(i, BASE_SECS_PER_RUN * 1000L);
56 oneRun(i, loopIters[i], true);
57 }
58 }
59 }
60
61 static final AtomicLong totalIters = new AtomicLong(0);
62 static final AtomicLong successes = new AtomicLong(0);
63 static final AtomicInteger sum = new AtomicInteger(0);
64
65 static final LoopHelpers.MarsagliaRandom rng = new LoopHelpers.MarsagliaRandom();
66
67 static final long[] loopIters = new long[NCPUS+1];
68
69 static final class NonAtomicInteger {
70 volatile int readBarrier;
71 int value;
72
73 NonAtomicInteger() {}
74 int get() {
75 int junk = readBarrier;
76 return value;
77 }
78 boolean compareAndSet(int cmp, int val) {
79 if (value == cmp) {
80 value = val;
81 return true;
82 }
83 return false;
84 }
85 void set(int val) { value = val; }
86 }
87
88 static final class VolatileInteger {
89 volatile int value;
90
91 VolatileInteger() {}
92 int get() {
93 return value;
94 }
95 boolean compareAndSet(int cmp, int val) {
96 if (value == cmp) {
97 value = val;
98 return true;
99 }
100 return false;
101 }
102 void set(int val) { value = val; }
103 }
104
105 static final class SynchedInteger {
106 int value;
107
108 SynchedInteger() {}
109 int get() {
110 return value;
111 }
112 synchronized boolean compareAndSet(int cmp, int val) {
113 if (value == cmp) {
114 value = val;
115 return true;
116 }
117 return false;
118 }
119 synchronized void set(int val) { value = val; }
120 }
121
122
123 static final class LockedInteger extends AbstractQueuedSynchronizer {
124 int value;
125 LockedInteger() {}
126
127 public boolean tryAcquire(int acquires) {
128 return compareAndSetState(0, 1);
129 }
130 public boolean tryRelease(int releases) {
131 setState(0);
132 return true;
133 }
134 void lock() { acquire(1); }
135 void unlock() { release(1); }
136
137 int get() {
138 return value;
139 }
140 boolean compareAndSet(int cmp, int val) {
141 lock();
142 try {
143 if (value == cmp) {
144 value = val;
145 return true;
146 }
147 return false;
148 } finally {
149 unlock();
150 }
151 }
152 void set(int val) {
153 lock();
154 try {
155 value = val;
156 } finally {
157 unlock();
158 }
159 }
160 }
161
162 // All these versions are copy-paste-hacked to avoid
163 // contamination with virtual call resolution etc.
164
165 // Use fixed-length unrollable inner loops to reduce safepoint checks
166 static final int innerPerOuter = 16;
167
168 static final class NonAtomicLoop implements Runnable {
169 final long iters;
170 final NonAtomicInteger obj;
171 final CyclicBarrier barrier;
172 NonAtomicLoop(long iters, NonAtomicInteger obj, CyclicBarrier b) {
173 this.iters = iters;
174 this.obj = obj;
175 this.barrier = b;
176 obj.set(rng.next());
177 }
178
179 public void run() {
180 try {
181 barrier.await();
182 long i = iters;
183 int y = 0;
184 int succ = 0;
185 while (i > 0) {
186 for (int k = 0; k < innerPerOuter; ++k) {
187 int x = obj.get();
188 int z = y + LoopHelpers.compute6(x);
189 if (obj.compareAndSet(x, z))
190 ++succ;
191 y = LoopHelpers.compute7(z);
192 }
193 i -= innerPerOuter;
194 }
195 sum.getAndAdd(obj.get());
196 successes.getAndAdd(succ);
197 barrier.await();
198 }
199 catch (Exception ie) {
200 return;
201 }
202 }
203 }
204
205 static final class AtomicLoop implements Runnable {
206 final long iters;
207 final AtomicInteger obj;
208 final CyclicBarrier barrier;
209 AtomicLoop(long iters, AtomicInteger obj, CyclicBarrier b) {
210 this.iters = iters;
211 this.obj = obj;
212 this.barrier = b;
213 obj.set(rng.next());
214 }
215
216 public void run() {
217 try {
218 barrier.await();
219 long i = iters;
220 int y = 0;
221 int succ = 0;
222 while (i > 0) {
223 for (int k = 0; k < innerPerOuter; ++k) {
224 int x = obj.get();
225 int z = y + LoopHelpers.compute6(x);
226 if (obj.compareAndSet(x, z))
227 ++succ;
228 y = LoopHelpers.compute7(z);
229 }
230 i -= innerPerOuter;
231 }
232 sum.getAndAdd(obj.get());
233 successes.getAndAdd(succ);
234 barrier.await();
235 }
236 catch (Exception ie) {
237 return;
238 }
239 }
240 }
241
242 static final class VolatileLoop implements Runnable {
243 final long iters;
244 final VolatileInteger obj;
245 final CyclicBarrier barrier;
246 VolatileLoop(long iters, VolatileInteger obj, CyclicBarrier b) {
247 this.iters = iters;
248 this.obj = obj;
249 this.barrier = b;
250 obj.set(rng.next());
251 }
252
253 public void run() {
254 try {
255 barrier.await();
256 long i = iters;
257 int y = 0;
258 int succ = 0;
259 while (i > 0) {
260 for (int k = 0; k < innerPerOuter; ++k) {
261 int x = obj.get();
262 int z = y + LoopHelpers.compute6(x);
263 if (obj.compareAndSet(x, z))
264 ++succ;
265 y = LoopHelpers.compute7(z);
266 }
267 i -= innerPerOuter;
268 }
269 sum.getAndAdd(obj.get());
270 successes.getAndAdd(succ);
271 barrier.await();
272 }
273 catch (Exception ie) {
274 return;
275 }
276 }
277 }
278
279 static final class SynchedLoop implements Runnable {
280 final long iters;
281 final SynchedInteger obj;
282 final CyclicBarrier barrier;
283 SynchedLoop(long iters, SynchedInteger obj, CyclicBarrier b) {
284 this.iters = iters;
285 this.obj = obj;
286 this.barrier = b;
287 obj.set(rng.next());
288 }
289
290 public void run() {
291 try {
292 barrier.await();
293 long i = iters;
294 int y = 0;
295 int succ = 0;
296 while (i > 0) {
297 for (int k = 0; k < innerPerOuter; ++k) {
298 int x = obj.get();
299 int z = y + LoopHelpers.compute6(x);
300 if (obj.compareAndSet(x, z))
301 ++succ;
302 y = LoopHelpers.compute7(z);
303 }
304 i -= innerPerOuter;
305 }
306 sum.getAndAdd(obj.get());
307 successes.getAndAdd(succ);
308 barrier.await();
309 }
310 catch (Exception ie) {
311 return;
312 }
313 }
314 }
315
316 static final class LockedLoop implements Runnable {
317 final long iters;
318 final LockedInteger obj;
319 final CyclicBarrier barrier;
320 LockedLoop(long iters, LockedInteger obj, CyclicBarrier b) {
321 this.iters = iters;
322 this.obj = obj;
323 this.barrier = b;
324 obj.set(rng.next());
325 }
326
327 public void run() {
328 try {
329 barrier.await();
330 long i = iters;
331 int y = 0;
332 int succ = 0;
333 while (i > 0) {
334 for (int k = 0; k < innerPerOuter; ++k) {
335 int x = obj.get();
336 int z = y + LoopHelpers.compute6(x);
337 if (obj.compareAndSet(x, z))
338 ++succ;
339 y = LoopHelpers.compute7(z);
340 }
341 i -= innerPerOuter;
342 }
343 sum.getAndAdd(obj.get());
344 successes.getAndAdd(succ);
345 barrier.await();
346 }
347 catch (Exception ie) {
348 return;
349 }
350 }
351 }
352
353 static final int loopsPerTimeCheck = 2048;
354
355 static final class NACalibrationLoop implements Runnable {
356 final long endTime;
357 final NonAtomicInteger obj;
358 final CyclicBarrier barrier;
359 NACalibrationLoop(long endTime, NonAtomicInteger obj, CyclicBarrier b) {
360 this.endTime = endTime;
361 this.obj = obj;
362 this.barrier = b;
363 obj.set(rng.next());
364 }
365
366 public void run() {
367 try {
368 barrier.await();
369 long iters = 0;
370 int y = 0;
371 int succ = 0;
372 do {
373 int i = loopsPerTimeCheck;
374 while (i > 0) {
375 for (int k = 0; k < innerPerOuter; ++k) {
376 int x = obj.get();
377 int z = y + LoopHelpers.compute6(x);
378 if (obj.compareAndSet(x, z))
379 ++succ;
380 y = LoopHelpers.compute7(z);
381 }
382 i -= innerPerOuter;
383 }
384 iters += loopsPerTimeCheck;
385 } while (System.currentTimeMillis() < endTime);
386 totalIters.getAndAdd(iters);
387 sum.getAndAdd(obj.get());
388 successes.getAndAdd(succ);
389 barrier.await();
390 }
391 catch (Exception ie) {
392 return;
393 }
394 }
395 }
396
397 static void runCalibration(int n, long nms) throws Exception {
398 long now = System.currentTimeMillis();
399 long endTime = now + nms;
400 CyclicBarrier b = new CyclicBarrier(n+1);
401 totalIters.set(0);
402 NonAtomicInteger a = new NonAtomicInteger();
403 for (int j = 0; j < n; ++j)
404 new Thread(new NACalibrationLoop(endTime, a, b)).start();
405 b.await();
406 b.await();
407 long ipt = totalIters.get() / n;
408 if (ipt > loopIters[n])
409 loopIters[n] = ipt;
410 if (sum.get() == 0) System.out.print(" ");
411 }
412
413 static long runNonAtomic(int n, long iters) throws Exception {
414 LoopHelpers.BarrierTimer timer = new LoopHelpers.BarrierTimer();
415 CyclicBarrier b = new CyclicBarrier(n+1, timer);
416 NonAtomicInteger a = new NonAtomicInteger();
417 for (int j = 0; j < n; ++j)
418 new Thread(new NonAtomicLoop(iters, a, b)).start();
419 b.await();
420 b.await();
421 if (sum.get() == 0) System.out.print(" ");
422 return timer.getTime();
423 }
424
425 static long runAtomic(int n, long iters) throws Exception {
426 LoopHelpers.BarrierTimer timer = new LoopHelpers.BarrierTimer();
427 CyclicBarrier b = new CyclicBarrier(n+1, timer);
428 AtomicInteger a = new AtomicInteger();
429 for (int j = 0; j < n; ++j)
430 new Thread(new AtomicLoop(iters, a, b)).start();
431 b.await();
432 b.await();
433 if (sum.get() == 0) System.out.print(" ");
434 return timer.getTime();
435 }
436
437 static long runVolatile(int n, long iters) throws Exception {
438 LoopHelpers.BarrierTimer timer = new LoopHelpers.BarrierTimer();
439 CyclicBarrier b = new CyclicBarrier(n+1, timer);
440 VolatileInteger a = new VolatileInteger();
441 for (int j = 0; j < n; ++j)
442 new Thread(new VolatileLoop(iters, a, b)).start();
443 b.await();
444 b.await();
445 if (sum.get() == 0) System.out.print(" ");
446 return timer.getTime();
447 }
448
449
450 static long runSynched(int n, long iters) throws Exception {
451 LoopHelpers.BarrierTimer timer = new LoopHelpers.BarrierTimer();
452 CyclicBarrier b = new CyclicBarrier(n+1, timer);
453 SynchedInteger a = new SynchedInteger();
454 for (int j = 0; j < n; ++j)
455 new Thread(new SynchedLoop(iters, a, b)).start();
456 b.await();
457 b.await();
458 if (sum.get() == 0) System.out.print(" ");
459 return timer.getTime();
460 }
461
462 static long runLocked(int n, long iters) throws Exception {
463 LoopHelpers.BarrierTimer timer = new LoopHelpers.BarrierTimer();
464 CyclicBarrier b = new CyclicBarrier(n+1, timer);
465 LockedInteger a = new LockedInteger();
466 for (int j = 0; j < n; ++j)
467 new Thread(new LockedLoop(iters, a, b)).start();
468 b.await();
469 b.await();
470 if (sum.get() == 0) System.out.print(" ");
471 return timer.getTime();
472 }
473
474 static void report(String tag, long runtime, long basetime, long iters) {
475 System.out.print(tag);
476 System.out.print(LoopHelpers.rightJustify((runtime - basetime) / iters) + " ns");
477 double secs = (double)(runtime) / 1000000000.0;
478 System.out.println("\t " + secs + "s run time");
479 }
480
481
482 static void oneRun(int i, long iters, boolean print) throws Exception {
483 System.out.println("threads : " + i +
484 " base iters per thread per run : " +
485 LoopHelpers.rightJustify(loopIters[i]));
486 long ntime = runNonAtomic(i, iters);
487 if (print)
488 report("Base : ", ntime, ntime, iters);
489 Thread.sleep(100L);
490 long atime = runAtomic(i, iters);
491 if (print)
492 report("Atomic CAS : ", atime, ntime, iters);
493 Thread.sleep(100L);
494 long vtime = runVolatile(i, iters);
495 if (print)
496 report("Volatile : ", vtime, ntime, iters);
497
498 Thread.sleep(100L);
499 if (!includeLocks) return;
500 long mtime = runLocked(i, iters);
501 if (print)
502 report("Mutex : ", mtime, ntime, iters);
503 Thread.sleep(100L);
504 long stime = runSynched(i, iters);
505 if (print)
506 report("Synchronized: ", stime, ntime, iters);
507 Thread.sleep(100L);
508 }
509
510
511 }