package org.multiverse.stms.gamma.benchmarks; import org.benchy.BenchmarkDriver; import org.benchy.TestCaseResult; import org.multiverse.TestThread; import org.multiverse.api.Txn; import org.multiverse.api.TxnExecutor; import org.multiverse.api.LockMode; import org.multiverse.api.callables.TxnBooleanCallable; import org.multiverse.api.callables.TxnVoidCallable; import org.multiverse.api.references.TxnInteger; import org.multiverse.api.references.TxnRef; import org.multiverse.stms.gamma.GammaStm; import static org.benchy.BenchyUtils.format; import static org.multiverse.TestUtils.joinAll; import static org.multiverse.TestUtils.startAll; public class SimpleStackDriver extends BenchmarkDriver { private int pushThreadCount = 1; private int popThreadCount = 1; private int capacity = Integer.MAX_VALUE; private boolean poolCallables = false; private LockMode readLockMode = LockMode.None; private LockMode writeLockMode = LockMode.None; private boolean dirtyCheck = false; private GammaStm stm; private PopThread[] popThreads; private PushThread[] pushThreads; private Stack<String> stack; @Override public void setUp() { System.out.printf("Multiverse > Pop threadcount %s\n", pushThreadCount); System.out.printf("Multiverse > Push threadcount %s\n", popThreadCount); if (capacity == Integer.MAX_VALUE) { System.out.printf("Multiverse > Capacity unbound\n"); } else { System.out.printf("Multiverse > Capacity %s\n", capacity); } System.out.printf("Multiverse > Pool Callables %s\n", poolCallables); System.out.printf("Multiverse > LockLevel %s\n", readLockMode); System.out.printf("Multiverse > DirtyCheck %s\n", dirtyCheck); stm = new GammaStm(); stack = new Stack<String>(); pushThreads = new PushThread[pushThreadCount]; for (int k = 0; k < pushThreadCount; k++) { pushThreads[k] = new PushThread(k, stack); } popThreads = new PopThread[popThreadCount]; for (int k = 0; k < popThreadCount; k++) { popThreads[k] = new PopThread(k, stack); } } @Override public void run(TestCaseResult testCaseResult) { startAll(pushThreads); startAll(popThreads); joinAll(pushThreads); joinAll(popThreads); } @Override public void processResults(TestCaseResult testCaseResult) { long pushCount = 0; long totalDurationMs = 0; for (PushThread t : pushThreads) { pushCount += t.count; totalDurationMs += t.getDurationMs(); } long popCount = 0; for (PopThread t : popThreads) { popCount += t.count; totalDurationMs += t.getDurationMs(); } int threadCount = pushThreadCount + popThreadCount; long count = pushCount + popCount; System.out.printf("Multiverse > Total number of transactions %s\n", count); double transactionsPerSecond = (count * 1000.0d) / totalDurationMs; System.out.printf("Multiverse > Performance %s transactions/second with %s threads\n", format(transactionsPerSecond), threadCount); testCaseResult.put("transactionsPerSecond", transactionsPerSecond); } class PushThread extends TestThread { private final Stack<String> stack; private long count; private final TxnExecutor pushBlock = stm.newTxnFactoryBuilder() .setDirtyCheckEnabled(dirtyCheck) .setReadLockMode(readLockMode) .setWriteLockMode(writeLockMode) .newTxnExecutor(); public PushThread(int id, Stack<String> stack) { super("PushThread-" + id); this.stack = stack; } @Override public void doRun() throws Exception { if (poolCallables) { runWithPooledCallables(); } else { runWithoutPooledCallables(); } } private void runWithoutPooledCallables() { while (!shutdown) { pushBlock.execute(new TxnVoidCallable() { @Override public void call(Txn tx) throws Exception { stack.push(tx, "item"); } }); count++; } for (int k = 0; k < popThreadCount; k++) { pushBlock.execute(new TxnVoidCallable() { @Override public void call(Txn tx) throws Exception { stack.push(tx, "end"); } }); } } private void runWithPooledCallables() { final PushCallable pushCallable = new PushCallable(); while (!shutdown) { pushCallable.item = "item"; pushBlock.execute(pushCallable); count++; } for (int k = 0; k < popThreadCount; k++) { pushCallable.item = "end"; pushBlock.execute(pushCallable); } } class PushCallable implements TxnVoidCallable { String item; @Override public void call(Txn tx) throws Exception { stack.push(tx, item); } } } class PopThread extends TestThread { private final Stack<String> stack; private long count; private final TxnExecutor popBlock = stm.newTxnFactoryBuilder() .setDirtyCheckEnabled(dirtyCheck) .setReadLockMode(readLockMode) .setWriteLockMode(writeLockMode) .newTxnExecutor(); public PopThread(int id, Stack<String> stack) { super("PopThread-" + id); this.stack = stack; } @Override public void doRun() throws Exception { if (poolCallables) { runWithoutPooledCallable(); } else { runWithPooledCallable(); } } private void runWithPooledCallable() { boolean end = false; while (!end) { end = popBlock.execute(new TxnBooleanCallable() { @Override public boolean call(Txn tx) throws Exception { return !stack.pop(tx).equals("end"); } }); count++; } } private void runWithoutPooledCallable() { PopCallable popCallable = new PopCallable(); boolean end = false; while (!end) { end = popBlock.execute(popCallable); count++; } } class PopCallable implements TxnBooleanCallable { @Override public boolean call(Txn tx) throws Exception { return !stack.pop(tx).endsWith("end"); } } } class Stack<E> { private final TxnRef<StackNode<E>> head = stm.getDefaultRefFactory().newTxnRef(null); private final TxnInteger size = stm.getTxRefFactoryBuilder().build().newTxnInteger(0); public void push(Txn tx, final E item) { if (capacity != Integer.MAX_VALUE) { if (size.get(tx) == capacity) { tx.retry(); } size.increment(tx); } head.set(tx, new StackNode<E>(item, head.get(tx))); } public E pop(Txn tx) { E value = head.awaitNotNullAndGet(tx).value; if (capacity != Integer.MAX_VALUE) { size.decrement(tx); } return value; } } static class StackNode<E> { final E value; final StackNode<E> next; StackNode(E value, StackNode<E> next) { this.value = value; this.next = next; } } }