package org.multiverse.stms.gamma.integration.isolation; import org.junit.Before; import org.junit.Test; import org.multiverse.TestThread; import org.multiverse.api.Txn; import org.multiverse.api.TxnExecutor; import org.multiverse.api.LockMode; import org.multiverse.api.callables.TxnVoidCallable; import org.multiverse.stms.gamma.GammaConstants; import org.multiverse.stms.gamma.GammaStm; import org.multiverse.stms.gamma.transactionalobjects.GammaTxnLong; import org.multiverse.stms.gamma.transactions.GammaTxn; import static java.lang.System.currentTimeMillis; import static org.junit.Assert.assertEquals; import static org.multiverse.TestUtils.joinAll; import static org.multiverse.TestUtils.startAll; import static org.multiverse.api.GlobalStmInstance.getGlobalStmInstance; import static org.multiverse.api.TxnThreadLocal.clearThreadLocalTxn; import static org.multiverse.stms.gamma.BenchmarkUtils.transactionsPerSecondAsString; import static org.multiverse.stms.gamma.BenchmarkUtils.transactionsPerSecondPerThreadAsString; /** * A StressTest that checks if the system is able to deal with concurrent increments on a TxnLong * So there is a lot of contention. * * @author Peter Veentjer */ public abstract class Isolation_AbstractTest implements GammaConstants { public long transactionsPerThread = 50 * 1000 * 1000; public final int threadCount = 2; protected GammaStm stm; @Before public void setUp() { clearThreadLocalTxn(); stm = (GammaStm) getGlobalStmInstance(); } protected abstract TxnExecutor newBlock(LockMode lockMode, boolean dirtyCheckEnabled); @Test public void withNoLockingDirtyCheck() { test(LockMode.None, true); } @Test public void withNoLockAndNoDirtyCheck() { test(LockMode.None, false); } @Test public void withReadLockAndDirtyCheck() { test(LockMode.Read, true); } @Test public void withReadLockAndNoDirtyCheck() { test(LockMode.Read, false); } @Test public void withWriteLockingAndDirtyCheck() { test(LockMode.Write, true); } @Test public void withWriteLockAndNoDirtyCheck() { test(LockMode.Write, false); } @Test public void withExclusiveLockAndDirtyCheck() { test(LockMode.Exclusive, true); } @Test public void withExclusiveLockNoDirtyCheck() { test(LockMode.Exclusive, false); } @Test public void withMixedSettings() { transactionsPerThread = 10000000; GammaTxnLong ref = new GammaTxnLong(stm); UpdateThread[] threads = new UpdateThread[8]; threads[0] = new UpdateThread(0, ref, LockMode.None, true); threads[1] = new UpdateThread(1, ref, LockMode.None, false); threads[2] = new UpdateThread(0, ref, LockMode.Read, true); threads[3] = new UpdateThread(1, ref, LockMode.Read, false); threads[4] = new UpdateThread(2, ref, LockMode.Write, true); threads[5] = new UpdateThread(3, ref, LockMode.Write, false); threads[6] = new UpdateThread(4, ref, LockMode.Exclusive, true); threads[7] = new UpdateThread(5, ref, LockMode.Exclusive, false); startAll(threads); joinAll(threads); long totalDurationMs = 0; for (UpdateThread thread : threads) { totalDurationMs += thread.durationMs; } System.out.println("--------------------------------------------------------"); System.out.printf("Threadcount: %s\n", threads.length); System.out.printf("Performance: %s transactions/second/thread\n", transactionsPerSecondPerThreadAsString(transactionsPerThread, totalDurationMs, threads.length)); System.out.printf("Performance: %s transactions/second\n", transactionsPerSecondAsString(transactionsPerThread, totalDurationMs, threads.length)); assertEquals(threads.length * transactionsPerThread, ref.atomicGet()); System.out.println("ref.orec: " + ref.___toOrecString()); } public void test(LockMode lockMode, boolean dirtyCheckEnabled) { UpdateThread[] threads = new UpdateThread[threadCount]; GammaTxnLong ref = new GammaTxnLong(stm); for (int k = 0; k < threads.length; k++) { threads[k] = new UpdateThread(k, ref, lockMode, dirtyCheckEnabled); } startAll(threads); joinAll(threads); long totalDurationMs = 0; for (UpdateThread thread : threads) { totalDurationMs += thread.durationMs; } System.out.println("--------------------------------------------------------"); System.out.printf("Threadcount: %s\n", threadCount); System.out.printf("Performance: %s transactions/second/thread\n", transactionsPerSecondPerThreadAsString(transactionsPerThread, totalDurationMs, threadCount)); System.out.printf("Performance: %s transactions/second\n", transactionsPerSecondAsString(transactionsPerThread, totalDurationMs, threadCount)); System.out.println("ref.orec: " + ref.___toOrecString()); assertEquals(threadCount * transactionsPerThread, ref.atomicGet()); } class UpdateThread extends TestThread { private final boolean dirtyCheckEnabled; private final GammaTxnLong ref; private final LockMode lockMode; private long durationMs; public UpdateThread(int id, GammaTxnLong ref, LockMode lockMode, boolean dirtyCheckEnabled) { super("UpdateThread-" + id); this.ref = ref; this.lockMode = lockMode; this.dirtyCheckEnabled = dirtyCheckEnabled; } @Override public void doRun() { TxnExecutor executor = newBlock(lockMode, dirtyCheckEnabled); TxnVoidCallable callable = new TxnVoidCallable() { @Override public void call(Txn tx) throws Exception { GammaTxn btx = (GammaTxn) tx; btx.richmansMansConflictScan = false; ref.openForWrite(btx, LOCKMODE_NONE).long_value++; } }; long startMs = currentTimeMillis(); for (long k = 0; k < transactionsPerThread; k++) { executor.execute(callable); if (k % 500000 == 0) { System.out.printf("%s is at %s\n", getName(), k); } } durationMs = currentTimeMillis() - startMs; System.out.printf("finished %s after %s ms\n", getName(), durationMs); } } }