Final form of the lock strategy benchmark class
package so.dahlgren; | |
import org.apache.commons.cli.*; | |
import java.util.concurrent.atomic.AtomicInteger; | |
import java.util.concurrent.CountDownLatch; | |
import java.util.concurrent.locks.ReentrantReadWriteLock; | |
import java.util.ArrayList; | |
import java.util.List; | |
/** | |
* SyncVsGranularLocks - Verify the performance characteristics of synchronizing access | |
* to an object, versus using granular read-write locks. | |
* @author rdahlgren | |
* @created 19Apr2013 13:46:47 | |
*/ | |
public class SyncVsGranularLocks { | |
private static final int dataSize = 1024 * 10; | |
private static final int mask = dataSize - 1; // See previous mod vs AND test | |
private static final long[] dataBlock = new long[dataSize]; | |
private static final Object syncObject = new Object(); | |
private static final ReentrantReadWriteLock locks = new ReentrantReadWriteLock(true); | |
// For great convenience! | |
private static class AppData { | |
public boolean useLocks = false; | |
public int readerThreads = 0; | |
public int writerThreads = 0; | |
public long millisToRun = 0l; | |
public int readerSleep = 0; | |
public int writerSleep = 0; | |
} | |
private static class ReaderLogic implements Runnable { | |
private static final AtomicInteger offset = new AtomicInteger(0); | |
private final long millisToRun; | |
private final CountDownLatch start; | |
private final CountDownLatch stop; | |
private final boolean useLocks; | |
private final int sleepTime; | |
public long readsPerformed = 0l; | |
public ReaderLogic( | |
long millisToRun, CountDownLatch start, | |
CountDownLatch stop, boolean useLocks, | |
int sleepTime | |
) { | |
this.millisToRun = millisToRun; | |
this.start = start; | |
this.stop = stop; | |
this.useLocks = useLocks; | |
this.sleepTime = sleepTime; | |
} | |
public void run() { | |
// So every reader isn't trying to read the same index, just to make the test more interesting | |
int counter = offset.getAndIncrement() * 4; | |
try { start.await(); } | |
catch (InterruptedException ex) { // Die | |
return; | |
} | |
long startTime = System.currentTimeMillis(); | |
long stopTime = startTime + millisToRun; | |
while (System.currentTimeMillis() < stopTime) { | |
if (useLocks) lockLogic(counter); | |
else syncLogic(counter); | |
readsPerformed++; | |
} | |
stop.countDown(); | |
} | |
private long syncLogic(int counter) { | |
synchronized(syncObject) { | |
try { Thread.sleep(sleepTime); } catch (Exception e) {} | |
long dataVal = SyncVsGranularLocks.dataBlock[ | |
counter++ & SyncVsGranularLocks.mask | |
]; | |
return dataVal; | |
} | |
} | |
private long lockLogic(int counter) { | |
locks.readLock().lock(); | |
try { | |
try { Thread.sleep(sleepTime); } catch (Exception e) {} | |
long dataVal = SyncVsGranularLocks.dataBlock[ | |
counter++ & SyncVsGranularLocks.mask | |
]; | |
return dataVal; | |
} finally { | |
locks.readLock().unlock(); | |
} | |
} | |
} | |
private static class WriterLogic implements Runnable { | |
private static final AtomicInteger offset = new AtomicInteger(2); | |
private final long millisToRun; | |
private final CountDownLatch start; | |
private final CountDownLatch stop; | |
private final boolean useLocks; | |
private final int sleepTime; | |
public long writesPerformed = 0l; | |
public WriterLogic( | |
long millisToRun, CountDownLatch start, | |
CountDownLatch stop, boolean useLocks, | |
int sleepTime | |
) { | |
this.millisToRun = millisToRun; | |
this.start = start; | |
this.stop = stop; | |
this.useLocks = useLocks; | |
this.sleepTime = sleepTime; | |
} | |
public void run() { | |
int counter = offset.getAndIncrement() * 4; | |
try { start.await(); } | |
catch (InterruptedException ex) { // Just die :-/ | |
return; | |
} | |
long startTime = System.currentTimeMillis(); | |
long stopTime = startTime + millisToRun; | |
while (System.currentTimeMillis() < stopTime) { | |
if (useLocks) lockLogic(counter); | |
else syncLogic(counter); | |
writesPerformed++; | |
} | |
stop.countDown(); | |
} | |
private void syncLogic(int counter) { | |
synchronized(syncObject) { | |
try { Thread.sleep(sleepTime); } catch (Exception e) {} | |
SyncVsGranularLocks.dataBlock[counter++ & SyncVsGranularLocks.mask] = counter; | |
} | |
} | |
private void lockLogic(int counter) { | |
locks.writeLock().lock(); | |
try { | |
try { Thread.sleep(sleepTime); } catch (Exception e) {} | |
SyncVsGranularLocks.dataBlock[counter++ & SyncVsGranularLocks.mask] = counter; | |
} finally { | |
locks.writeLock().unlock(); | |
} | |
} | |
} | |
/** | |
* Kick off the test. | |
*/ | |
public static void main(String[] args) throws InterruptedException { | |
AppData data = parseOptions(args); | |
CountDownLatch start = new CountDownLatch(1); | |
CountDownLatch stop = new CountDownLatch(data.readerThreads + data.writerThreads); | |
List<ReaderLogic> readers = new ArrayList<ReaderLogic>(data.readerThreads); | |
List<WriterLogic> writers = new ArrayList<WriterLogic>(data.writerThreads); | |
// Readers | |
for (int i = 0; i < data.readerThreads; i++) { | |
ReaderLogic reader = new ReaderLogic( | |
data.millisToRun, start, stop, data.useLocks, data.readerSleep | |
); | |
readers.add(reader); | |
new Thread(reader).start(); | |
} | |
// Writers | |
for (int i = 0; i < data.writerThreads; i++) { | |
WriterLogic writer = new WriterLogic( | |
data.millisToRun, start, stop, data.useLocks, data.writerSleep | |
); | |
writers.add(writer); | |
new Thread(writer).start(); | |
} | |
System.out.println("Beginning " + (data.millisToRun / 1000.0) + " second run."); | |
start.countDown(); | |
stop.await(); | |
System.out.println("Run complete."); | |
for (ReaderLogic reader : readers) { | |
System.out.println("Reader performed " + reader.readsPerformed + " read ops."); | |
} | |
for (WriterLogic writer : writers) { | |
System.out.println("Writer performed " + writer.writesPerformed + " write ops."); | |
} | |
} | |
private static AppData parseOptions(String[] args) { | |
Option accessMethod = OptionBuilder.withArgName("method") | |
.hasArg() | |
.withDescription("Specify sync | locks") | |
.isRequired() | |
.create("accessmethod"); | |
Option readerThreads = OptionBuilder.withArgName("number") | |
.hasArg() | |
.withDescription("Number of reader threads") | |
.isRequired() | |
.create("readerthreads"); | |
Option writerThreads = OptionBuilder.withArgName("number") | |
.hasArg() | |
.withDescription("Number of writer threads") | |
.isRequired() | |
.create("writerthreads"); | |
Option testDuration = OptionBuilder.withArgName("duration") | |
.hasArg() | |
.withDescription("Test duration, specified in seconds") | |
.isRequired() | |
.create("duration"); | |
Option writerSleep = OptionBuilder.withArgName("writersleep") | |
.hasArg() | |
.withDescription("Milliseconds to sleep in the write op") | |
.isRequired() | |
.create("writersleep"); | |
Option readerSleep = OptionBuilder.withArgName("readersleep") | |
.hasArg() | |
.withDescription("Milliseconds to sleep in the read op") | |
.isRequired() | |
.create("readersleep"); | |
Options options = new Options(); | |
options.addOption(accessMethod); | |
options.addOption(readerThreads); | |
options.addOption(writerThreads); | |
options.addOption(writerSleep); | |
options.addOption(readerSleep); | |
options.addOption(testDuration); | |
CommandLineParser parser = new BasicParser(); | |
AppData retVal = new AppData(); | |
try { | |
CommandLine line = parser.parse(options, args); | |
retVal.useLocks = line.getOptionValue("accessmethod").equals("locks"); | |
retVal.readerThreads = Integer.parseInt(line.getOptionValue("readerthreads")); | |
retVal.writerThreads = Integer.parseInt(line.getOptionValue("writerthreads")); | |
retVal.writerSleep = Integer.parseInt(line.getOptionValue("writersleep")); | |
retVal.readerSleep = Integer.parseInt(line.getOptionValue("readersleep")); | |
retVal.millisToRun = Long.parseLong(line.getOptionValue("duration")) * 1000l; | |
} catch (MissingOptionException ex) { | |
HelpFormatter formatter = new HelpFormatter(); | |
formatter.printHelp("App", options); | |
System.exit(-1); | |
} catch (ParseException ex) { | |
// Can't really recover from this anyway | |
throw new IllegalStateException("Problem parsing arguments", ex); | |
} catch (NumberFormatException ex) { | |
// Same as above | |
throw new IllegalArgumentException("Number format was incorrect", ex); | |
} | |
return retVal; | |
} | |
/* vim: set ts=2 sw=2 et: */ | |
} /// end of SyncVsGranularLocks |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment