package org.basicmon.benchmark;

import com.google.caliper.Param;
import com.google.caliper.api.AfterRep;
import com.google.caliper.api.Macrobenchmark;
import org.basicmon.BasicMonManager;
import org.basicmon.BasicTimer;
import org.javasimon.SimonManager;
import org.javasimon.Split;
import org.javasimon.Stopwatch;
import org.javasimon.utils.SimonUtils;

import java.util.concurrent.CountDownLatch;

/**
 * Compares multi-threaded performance with javasimon. We compare both atomic and synchronization-based timers.
 *
 * Run: mvn exec:java -Dexec.classpathScope="test" -Dexec.mainClass=com.google.caliper.runner.CaliperMain -Dexec.args="--print-config --instrument runtime org.basicmon.benchmark.SimonMultiThreadedComparison"
 */
@SuppressWarnings("UnusedDeclaration")
public final class SimonMultiThreadedComparison {

    @Param({"8", "100"}) int threads;
    @Param({"100", "1000"}) int loops;

    private static final String NAME = SimonUtils.generateName();

    private static CountDownLatch latch;

    @AfterRep
    public void tearDown() throws Exception {
        BasicMonManager.reset();
        SimonManager.clear();
    }

    @Macrobenchmark
    public void basicAtomic() {
        int threads = this.threads;
        int loops = this.loops;
        doBasicAtomic(threads, loops);
    }

    @Macrobenchmark
    public void basicSynchronized() {
        int threads = this.threads;
        int loops = this.loops;
        doBasicSync(threads, loops);
    }

    @Macrobenchmark
    public void simon() {
        int threads = this.threads;
        int loops = this.loops;
        doSimon(threads, loops);
    }

    private void doSimon(int threads, int loops) {
        latch = new CountDownLatch(threads);
        for (int i = 0; i < threads; i++) {
            SimonMultithreadedStress test = new SimonMultithreadedStress(loops);
            test.start();
        }
        try {
            latch.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    private void doBasicAtomic(int threads, int loops) {
        latch = new CountDownLatch(threads);
        for (int i = 0; i < threads; i++) {
            new BasicAtomicTimerMultithreadedStress(loops).start();
        }
        try {
            latch.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    private void doBasicSync(int threads, int loops) {
        latch = new CountDownLatch(threads);
        for (int i = 0; i < threads; i++) {
            new BasicSyncTimerMultithreadedStress(loops).start();
        }
        try {
            latch.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    private static class SimonMultithreadedStress extends Thread {
        int loops;
        long result;
        public SimonMultithreadedStress(int loops) {
            this.loops = loops;
        }

        public void run() {
            Stopwatch stopwatch = SimonManager.getStopwatch(NAME);
            for (int i = 0; i < loops; i++) {
                Split split = stopwatch.start().stop();
                result |= split.runningFor();
            }
            latch.countDown();
        }
    }

    private static class BasicAtomicTimerMultithreadedStress extends Thread {
        int loops;
        long result;
        public BasicAtomicTimerMultithreadedStress(int loops) {
            this.loops = loops;
        }

        public void run() {
            BasicTimer timer = BasicMonManager.getAtomicTimer(NAME);
            for (int i = 0; i < loops; i++) {
                result |= timer.start().stop();
            }
            latch.countDown();
        }
    }

    private static class BasicSyncTimerMultithreadedStress extends Thread {
        int loops;
        long result;
        public BasicSyncTimerMultithreadedStress(int loops) {
            this.loops = loops;
        }

        public void run() {
            BasicTimer timer = BasicMonManager.getSyncTimer(NAME);
            for (int i = 0; i < loops; i++) {
                result |= timer.start().stop();
            }
            latch.countDown();
        }
    }
}

@@ -145,16 +145,73 @@
 chmark;%0A
+import com.google.caliper.api.SkipThisScenarioException;%0A
 import o
@@ -872,16 +872,67 @@
 ison %7B%0A%0A
+    @Param(%7B%22sync%22, %22atomic%22%7D) String timerMethod;%0A
     @Par
@@ -1285,18 +1285,59 @@
 asic
-Atomic() %7B
+() %7B%0A        String timerMethod = this.timerMethod;
 %0A   
@@ -1400,32 +1400,65 @@
 s.loops;%0A       
+ if(timerMethod.equals(%22atomic%22))
  doBasicAtomic(t
@@ -1481,144 +1481,47 @@
     
-%7D%0A%0A
     
-@Macrobenchmark%0A    public void basicSynchronized() %7B%0A        int threads = this.threads;%0A        int loops = this.loops;%0A       
+else if(timerMethod.equals(%22sync%22))
  doB
@@ -1595,24 +1595,71 @@
 d simon() %7B%0A
+        String timerMethod = this.timerMethod;%0A
         int 
@@ -1721,16 +1721,132 @@
 %0A       
+ if(timerMethod.equals(%22atomic%22)) throw new SkipThisScenarioException();%0A        else if(timerMethod.equals(%22sync%22))
  doSimon

Use a caliper parameter for sync vs atomic benchmarks