package org.multiverse.stms.gamma.benchmarks;

import org.benchy.BenchyUtils;
import org.junit.Ignore;
import org.junit.Test;
import org.multiverse.MultiverseConstants;
import org.multiverse.stms.gamma.GammaStm;
import org.multiverse.stms.gamma.transactionalobjects.GammaTxnLong;
import org.multiverse.utils.ToolUnsafe;
import sun.misc.Unsafe;

@Ignore
public class BasicPerformanceDriver {

    public static void main(String[] args) {
        BasicPerformanceDriver driver = new BasicPerformanceDriver();
        driver.casPerformance();
    }

    @Test
    public void test() {
        GammaStm stm = new GammaStm();
        GammaTxnLong ref = new GammaTxnLong(stm);

        final long transactionCount = 1000 * 1000 * 1000;

        final long startMs = System.currentTimeMillis();


        for (long k = 0; k < transactionCount; k++) {
            ref.arriveAndLock(1, MultiverseConstants.LOCKMODE_EXCLUSIVE);
            //ref.orec = 0;
            ref.departAfterUpdateAndUnlock();
        }

        long durationMs = System.currentTimeMillis() - startMs;

        String s = BenchyUtils.operationsPerSecondPerThreadAsString(transactionCount, durationMs, 1);

        System.out.printf("Performance is %s transactions/second/thread\n", s);
    }

    @Test
    public void casPerformance() {
        final long transactionCount = 1000 * 1000 * 1000;

        final long startMs = System.currentTimeMillis();

        Cas cas = new Cas();
        final long t = transactionCount / 10;
        for (long k = 0; k < t; k++) {
            cas.atomicInc();
            cas.atomicInc();
            cas.atomicInc();
            cas.atomicInc();
            cas.atomicInc();
            cas.atomicInc();
            cas.atomicInc();
            cas.atomicInc();
            cas.atomicInc();
            cas.atomicInc();
        }

        long durationMs = System.currentTimeMillis() - startMs;

        String s = BenchyUtils.operationsPerSecondPerThreadAsString(transactionCount, durationMs, 1);

        System.out.printf("Performance is %s transactions/second/thread\n", s);
    }

    @Test
    public void volatileWritePerformance() {
        final long transactionCount = 1000 * 1000 * 1000;

        final long startMs = System.currentTimeMillis();

        final Cas cas = new Cas();
        final long t = transactionCount / 10;
        for (long k = 0; k < t; k++) {
            cas.volatile_value++;
            cas.volatile_value++;
            cas.volatile_value++;
            cas.volatile_value++;
            cas.volatile_value++;
            cas.volatile_value++;
            cas.volatile_value++;
            cas.volatile_value++;
            cas.volatile_value++;
            cas.volatile_value++;
        }

        long durationMs = System.currentTimeMillis() - startMs;

        String s = BenchyUtils.operationsPerSecondPerThreadAsString(transactionCount, durationMs, 1);

        System.out.printf("Performance is %s transactions/second/thread\n", s);
    }

    @Test
    public void basicWritePerformance() {
        final long transactionCount = 1000 * 1000 * 1000;

        final long startMs = System.currentTimeMillis();

        final Cas cas = new Cas();
        for (long k = 0; k < transactionCount; k++) {
            cas.volatile_value++;//lock
            cas.volatile_value++;//version
            cas.volatile_value++;//value
            cas.volatile_value++;//unlock
        }

        long durationMs = System.currentTimeMillis() - startMs;

        String s = BenchyUtils.operationsPerSecondPerThreadAsString(transactionCount, durationMs, 1);

        System.out.printf("Performance is %s transactions/second/thread\n", s);
    }

    @Test
    public void basicWritePerformanceWithNonVolatileVersionAndValue() {
        final long transactionCount = 1000 * 1000 * 1000;

        final long startMs = System.currentTimeMillis();

        final Cas cas = new Cas();
        for (long k = 0; k < transactionCount; k++) {
            cas.volatile_value++;//lock
            cas.value++;//version
            cas.value++;//value
            cas.volatile_value++;//unlock
        }

        long durationMs = System.currentTimeMillis() - startMs;

        String s = BenchyUtils.operationsPerSecondPerThreadAsString(transactionCount, durationMs, 1);

        System.out.printf("Performance is %s transactions/second/thread\n", s);
    }


    class Cas {
        protected final Unsafe ___unsafe = ToolUnsafe.getUnsafe();
        protected final long valueOffset;

        {
            try {
                valueOffset = ___unsafe.objectFieldOffset(
                        Cas.class.getDeclaredField("volatile_value"));
            } catch (Exception ex) {
                throw new Error(ex);
            }
        }


        volatile long volatile_value;
        long value;

        void atomicInc() {
            final long oldValue = volatile_value;
            final long newValue = oldValue + 1;
            ___unsafe.compareAndSwapLong(this, valueOffset, oldValue, newValue);
        }
    }

}
