001: // Copyright 2006 The Apache Software Foundation
002: //
003: // Licensed under the Apache License, Version 2.0 (the "License");
004: // you may not use this file except in compliance with the License.
005: // You may obtain a copy of the License at
006: //
007: // http://www.apache.org/licenses/LICENSE-2.0
008: //
009: // Unless required by applicable law or agreed to in writing, software
010: // distributed under the License is distributed on an "AS IS" BASIS,
011: // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: // See the License for the specific language governing permissions and
013: // limitations under the License.
014:
015: package org.apache.tapestry.services;
016:
017: import static java.lang.String.format;
018: import static java.lang.System.out;
019:
020: import java.util.concurrent.locks.ReadWriteLock;
021: import java.util.concurrent.locks.ReentrantReadWriteLock;
022:
023: import org.apache.tapestry.ioc.internal.util.ConcurrentBarrier;
024:
025: /**
026: * Tests single-thread synchronization overhead using different techniques. Note that we're fudging
027: * things a bit by getting a read lock for a write operation .... it's just that I'm more concerned
028: * about read locks (which will be very common) than about write locks (very rare). Another concern
029: * is that hotspot is going to mess up our synchronization when it see we're not really doing
030: * anything multi-threaded.
031: * <p>
032: * The results show that using the {@link org.apache.tapestry.internal.annotations.Concurrent}
033: * aspect (which used a {@link java.util.concurrent.locks.ReentrantReadWriteLock} under the covers)
034: * is about 4x as expensive as just using the synchronized keyword. There are some anomolous results
035: * ... for example, ReadWriteLockRunner is consistently slower than ReadWriteLockAspectRunner (one
036: * would expect it to be the other way around ... must be something about how AspectJ weaves the
037: * code ... and it's use of static methods in many cases).
038: * <p>
039: * Well, the Concurrent aspect is gone, replaced with the {@link ConcurrentBarrier} utility.
040: */
041: public class SyncCostBench {
042: /** Calculates a fibunacci series. */
043: static class Worker implements Runnable {
044: private long[] _series = { 1, 1 };
045:
046: public void run() {
047: long value = _series[0] + _series[1];
048:
049: // Now shift the values down to prepare for the next iteration.
050:
051: _series[0] = _series[1];
052: _series[1] = value;
053: }
054: }
055:
056: static class SimpleRunner implements Runnable {
057: private final Runnable _delegate;
058:
059: public SimpleRunner(Runnable delegate) {
060: _delegate = delegate;
061: }
062:
063: public void run() {
064: _delegate.run();
065: }
066: }
067:
068: static class SynchronizedRunner implements Runnable {
069: private final Runnable _delegate;
070:
071: public SynchronizedRunner(Runnable delegate) {
072: _delegate = delegate;
073: }
074:
075: public synchronized void run() {
076: _delegate.run();
077: }
078: }
079:
080: static class ReadWriteLockAspectRunner implements Runnable {
081: private final ConcurrentBarrier _barrier = new ConcurrentBarrier();
082:
083: private final Runnable _delegate;
084:
085: public ReadWriteLockAspectRunner(Runnable delegate) {
086: _delegate = delegate;
087: }
088:
089: public void run() {
090: _barrier.withRead(_delegate);
091: }
092: }
093:
094: static class ReadWriteLockRunner implements Runnable {
095: private final Runnable _delegate;
096:
097: private final ReadWriteLock _lock = new ReentrantReadWriteLock();
098:
099: public ReadWriteLockRunner(Runnable delegate) {
100: _delegate = delegate;
101: }
102:
103: public void run() {
104:
105: try {
106: _lock.readLock().lock();
107:
108: _delegate.run();
109: } finally {
110: _lock.readLock().unlock();
111: }
112:
113: }
114: }
115:
116: private static final int WARMUP_BLOCK_SIZE = 1000;
117:
118: private static final int BLOCK_SIZE = 5 * 1000 * 1000;
119:
120: static class BlockRunner implements Runnable {
121: private final Runnable _delegate;
122:
123: private final int _blockSize;
124:
125: public BlockRunner(int blockSize, Runnable delegate) {
126: _blockSize = blockSize;
127: _delegate = delegate;
128: }
129:
130: public void run() {
131: for (int i = 0; i < _blockSize; i++)
132: _delegate.run();
133: }
134: }
135:
136: public static void main(String[] args) throws Exception {
137: Runnable simple = new SimpleRunner(new Worker());
138: Runnable synched = new SynchronizedRunner(new Worker());
139: Runnable rw = new ReadWriteLockRunner(new Worker());
140: Runnable aspect = new ReadWriteLockAspectRunner(new Worker());
141:
142: out.println(format("%40s %9s %9s %9s", ",simple", ",synched",
143: ",rw", ",aspect"));
144:
145: stage("warmup");
146:
147: go(WARMUP_BLOCK_SIZE, simple);
148: go(WARMUP_BLOCK_SIZE, synched);
149: go(WARMUP_BLOCK_SIZE, rw);
150: go(WARMUP_BLOCK_SIZE, aspect);
151:
152: out.println();
153:
154: for (int i = 0; i < 10; i++) {
155: Thread.sleep(5 * 1000);
156: System.gc();
157:
158: stage(format("stage #%d", i + 1));
159: go(BLOCK_SIZE, simple);
160: go(BLOCK_SIZE, synched);
161: go(BLOCK_SIZE, rw);
162: go(BLOCK_SIZE, aspect);
163:
164: out.println();
165: }
166: }
167:
168: private static void stage(String name) {
169: out.print(format("%30s", name));
170: }
171:
172: private static void go(int blockSize, Runnable runner)
173: throws InterruptedException {
174:
175: Thread t = new Thread(new BlockRunner(blockSize, runner));
176:
177: long tick = System.nanoTime();
178:
179: t.start();
180:
181: // Now wait for it to finish.
182:
183: t.join();
184:
185: long tock = System.nanoTime();
186:
187: out.print(format(",%9d", tock - tick));
188: }
189: }
|