package org.apache.cassandra.stress.util; import java.util.Arrays; import java.util.List; import java.util.Random; import java.util.concurrent.CountDownLatch; // a timer - this timer must be used by a single thread, and co-ordinates with other timers by public final class Timer { private static final int SAMPLE_SIZE_SHIFT = 10; private static final int SAMPLE_SIZE_MASK = (1 << SAMPLE_SIZE_SHIFT) - 1; private final Random rnd = new Random(); // in progress snap start private long sampleStartNanos; // each entry is present with probability 1/p(opCount) or 1/(p(opCount)-1) private final long[] sample = new long[1 << SAMPLE_SIZE_SHIFT]; private int opCount; // aggregate info private int keyCount; private long total; private long max; private long maxStart; private long upToDateAsOf; private long lastSnap = System.nanoTime(); // communication with summary/logging thread private volatile CountDownLatch reportRequest; volatile TimingInterval report; private volatile TimingInterval finalReport; public void start(){ // decide if we're logging this event sampleStartNanos = System.nanoTime(); } private static int p(int index) { return 1 + (index >>> SAMPLE_SIZE_SHIFT); } public void stop(int keys) { maybeReport(); long now = System.nanoTime(); long time = now - sampleStartNanos; if (rnd.nextInt(p(opCount)) == 0) sample[index(opCount)] = time; if (time > max) { maxStart = sampleStartNanos; max = time; } total += time; opCount += 1; keyCount += keys; upToDateAsOf = now; } private static int index(int count) { return count & SAMPLE_SIZE_MASK; } private TimingInterval buildReport() { final List<SampleOfLongs> sampleLatencies = Arrays.asList ( new SampleOfLongs(Arrays.copyOf(sample, index(opCount)), p(opCount)), new SampleOfLongs(Arrays.copyOfRange(sample, index(opCount), Math.min(opCount, sample.length)), p(opCount) - 1) ); final TimingInterval report = new TimingInterval(lastSnap, upToDateAsOf, max, maxStart, max, keyCount, total, opCount, SampleOfLongs.merge(rnd, sampleLatencies, Integer.MAX_VALUE)); // reset counters opCount = 0; keyCount = 0; total = 0; max = 0; lastSnap = upToDateAsOf; return report; } // checks to see if a report has been requested, and if so produces the report, signals and clears the request private void maybeReport() { if (reportRequest != null) { synchronized (this) { report = buildReport(); reportRequest.countDown(); reportRequest = null; } } } // checks to see if the timer is dead; if not requests a report, and otherwise fulfills the request itself synchronized void requestReport(CountDownLatch signal) { if (finalReport != null) { report = finalReport; finalReport = new TimingInterval(0); signal.countDown(); } else reportRequest = signal; } // closes the timer; if a request is outstanding, it furnishes the request, otherwise it populates finalReport public synchronized void close() { if (reportRequest == null) finalReport = buildReport(); else { finalReport = new TimingInterval(0); report = buildReport(); reportRequest.countDown(); reportRequest = null; } } }