
org.reveno.atp.metrics.meter.impl.TwoBufferHistogram Maven / Gradle / Ivy
/**
* Copyright (c) 2015 The original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.reveno.atp.metrics.meter.impl;
import org.reveno.atp.metrics.Sink;
import org.reveno.atp.metrics.meter.Histogram;
import org.reveno.atp.metrics.meter.HistogramType;
import org.reveno.atp.utils.UnsafeUtils;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAdder;
public class TwoBufferHistogram implements Histogram {
protected static final int BITS_PER_LONG = 63;
protected static final int BYTES_PER_LONG = 8;
protected static final byte WRITING = 1;
protected static final byte NEED_RESET = 2;
protected static final byte RESET_DONE = 3;
@Override
public void sendTo(List sinks, boolean sync) {
switchNext(sync);
ByteBuffer buffer = this.bufs[prevIndex()];
long sum = count.sumThenReset();
long amount = Math.min(sum, buffer.limit() / BYTES_PER_LONG);
long timestamp = System.currentTimeMillis() / 1000;
buffer.clear();
long mean = 0;
long min = Long.MAX_VALUE;
long max = 0;
long stddev = 0;
int count = 0;
while (buffer.position() <= buffer.limit() && (count += 1) <= amount) {
final long metric = buffer.getLong();
mean += metric;
if (metric > max) {
max = metric;
}
if (metric < min) {
min = metric;
}
if (prevMean != -1) {
stddev += Math.pow(prevMean - metric, 2);
}
}
mean /= count - 1;
if (prevMean == -1) {
stddev = 0;
} else {
stddev /= count;
stddev = (long) Math.sqrt(stddev);
}
prevMean = mean;
for (Sink sink : sinks) {
sink.send(name + ".count", Long.toString(sum), timestamp);
sink.send(name + ".mean", Long.toString(mean), timestamp);
sink.send(name + ".min", Long.toString(min), timestamp);
sink.send(name + ".max", Long.toString(max), timestamp);
sink.send(name + ".stddev", Long.toString(stddev), timestamp);
}
buffer.clear();
}
public void switchNext(boolean sync) {
int nextIndex = (int) (switcher.incrementAndGet() % bufferCount);
bit = (nextIndex << 2) | NEED_RESET;
if (sync) {
for (;;) {
if ((bit << 30) >>> 30 == RESET_DONE)
break;
else
Thread.yield();
}
}
}
@Override
public Histogram update(long value) {
count.increment();
int currBit = bit;
int currentIndex = currBit >> 2;
ByteBuffer cur = bufs[currentIndex];
if ((currBit << 30) >>> 30 == NEED_RESET) {
bit = currentIndex << 2 | RESET_DONE;
}
if (cur.remaining() > 0) {
cur.putLong(value);
} else if (type == HistogramType.RANDOM_VITTERS_R) {
long next;
for (;;) {
if ((next = nextLong(cur.limit())) <= cur.limit() - BYTES_PER_LONG)
break;
}
int pos = (int) Math.round((double) next / BYTES_PER_LONG) * BYTES_PER_LONG;
cur.position(pos);
cur.putLong(value);
}
return this;
}
@Override
public boolean isReady() {
return bufs[bit >> 2].remaining() == 0;
}
@Override
public void destroy() {
UnsafeUtils.destroyDirectBuffer(this.bufs[0]);
UnsafeUtils.destroyDirectBuffer(this.bufs[1]);
}
protected int currentIndex() {
return (int) (switcher.get() % bufferCount);
}
protected int prevIndex() {
if (currentIndex() == 1) return 0;
else return 1;
}
/**
* Get a pseudo-random long uniformly between 0 and n-1. Stolen from
* {@link java.util.Random#nextInt()}.
*
* @param n the bound
* @return a value select randomly from the range {@code [0..n)}.
*/
protected static long nextLong(long n) {
long bits, val;
do {
bits = ThreadLocalRandom.current().nextLong() & (~(1L << BITS_PER_LONG));
val = bits % n;
} while (bits - val + (n - 1) < 0L);
return val;
}
public TwoBufferHistogram(String name, int bufferSize) {
this(name, bufferSize, HistogramType.RANDOM_VITTERS_R);
}
public TwoBufferHistogram(String name, int bufferSize, HistogramType type) {
if (Integer.bitCount(bufferSize) != 1) {
throw new IllegalArgumentException("Buffer size must be pow(2, n) number!");
}
this.bufferSize = bufferSize;
this.bufs = new ByteBuffer[bufferCount];
for (int i = 0; i < bufferCount; i++) {
this.bufs[i] = ByteBuffer.allocateDirect(bufferSize);
}
this.type = type;
this.name = name;
}
protected long prevMean = -1L;
protected LongAdder count = new LongAdder();
protected volatile int bit = WRITING;
protected final String name;
protected final ByteBuffer[] bufs;
protected final int bufferSize;
protected final int bufferCount = 2;
protected final AtomicLong switcher = new AtomicLong();
protected final HistogramType type;
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy