Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ public class Measurements
{

public static final String MEASUREMENT_TYPE_PROPERTY = "measurementtype";

private static final String MEASUREMENT_TYPE_PROPERTY_DEFAULT = "histogram";
private static final String MEASUREMENT_TYPE_PROPERTY_DEFAULT = "hdrhistogram";

public static final String MEASUREMENT_INTERVAL = "measurement.interval";
private static final String MEASUREMENT_INTERVAL_DEFAULT = "op";

Expand Down
15 changes: 10 additions & 5 deletions core/src/main/java/com/yahoo/ycsb/measurements/OneMeasurement.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@
*/
public abstract class OneMeasurement {

String _name;
final ConcurrentHashMap<Integer, AtomicInteger> returncodes;
private final String _name;
private final ConcurrentHashMap<Integer, AtomicInteger> _returncodes;

public String getName() {
return _name;
Expand All @@ -41,7 +41,7 @@ public String getName() {
*/
public OneMeasurement(String _name) {
this._name = _name;
this.returncodes = new ConcurrentHashMap<Integer, AtomicInteger>();
this._returncodes = new ConcurrentHashMap<Integer, AtomicInteger>();
}

public abstract void measure(int latency);
Expand All @@ -53,10 +53,10 @@ public OneMeasurement(String _name) {
*/
public void reportReturnCode(int code) {
Integer Icode = code;
AtomicInteger counter = returncodes.get(Icode);
AtomicInteger counter = _returncodes.get(Icode);

if (counter == null) {
AtomicInteger other = returncodes.putIfAbsent(Icode, counter = new AtomicInteger());
AtomicInteger other = _returncodes.putIfAbsent(Icode, counter = new AtomicInteger());
if (other != null) {
counter = other;
}
Expand All @@ -73,4 +73,9 @@ public void reportReturnCode(int code) {
*/
public abstract void exportMeasurements(MeasurementsExporter exporter) throws IOException;

protected final void exportReturnCodes(MeasurementsExporter exporter) throws IOException {
for (Map.Entry<Integer, AtomicInteger> entry : _returncodes.entrySet()) {
exporter.write(getName(), "Return=" + entry.getKey(), entry.getValue().get());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,63 +34,63 @@
import com.yahoo.ycsb.measurements.exporter.MeasurementsExporter;

/**
* Take measurements and maintain a HdrHistogram of a given metric, such as
* READ LATENCY.
* Take measurements and maintain a HdrHistogram of a given metric, such as READ
* LATENCY.
*
* @author nitsanw
*
*/
public class OneMeasurementHdrHistogram extends OneMeasurement {

// we need one log per measurement histogram
final PrintStream log;
final HistogramLogWriter histogramLogWriter;

final Recorder histogram = new Recorder(3);

Histogram totalHistogram;

public OneMeasurementHdrHistogram(String name, Properties props) {
super(name);
boolean shouldLog = Boolean.parseBoolean(props.getProperty("hdrhistogram.fileoutput", "false"));
if (!shouldLog) {
log = null;
histogramLogWriter = null;
return;
}
try {
final String hdrOutputFilename = props.getProperty("hdrhistogram.output.path", "") +name+".hdr";
log = new PrintStream(new FileOutputStream(hdrOutputFilename), false);
} catch (FileNotFoundException e) {
throw new RuntimeException("Failed to open hdr histogram output file", e);
}
histogramLogWriter = new HistogramLogWriter(log);
histogramLogWriter.outputComment("[Logging for: " + name + "]");
histogramLogWriter.outputLogFormatVersion();
histogramLogWriter.outputStartTime(System.currentTimeMillis());
histogramLogWriter.outputLegend();
}

/**
* It appears latency is reported in micros.
* Using {@link Recorder} to support concurrent updates to histogram.
*
* @see com.yahoo.ycsb.OneMeasurement#measure(int)
*/
public void measure(int latencyInMicros) {
histogram.recordValue(latencyInMicros);
}

/**
* This is called from a main thread, on orderly termination.
*
* @see com.yahoo.ycsb.measurements.OneMeasurement#exportMeasurements(com.yahoo.ycsb.measurements.exporter.MeasurementsExporter)
*/
// we need one log per measurement histogram
final PrintStream log;
final HistogramLogWriter histogramLogWriter;

final Recorder histogram = new Recorder(3);

Histogram totalHistogram;

public OneMeasurementHdrHistogram(String name, Properties props) {
super(name);
boolean shouldLog = Boolean.parseBoolean(props.getProperty("hdrhistogram.fileoutput", "false"));
if (!shouldLog) {
log = null;
histogramLogWriter = null;
return;
}
try {
final String hdrOutputFilename = props.getProperty("hdrhistogram.output.path", "") + name + ".hdr";
log = new PrintStream(new FileOutputStream(hdrOutputFilename), false);
} catch (FileNotFoundException e) {
throw new RuntimeException("Failed to open hdr histogram output file", e);
}
histogramLogWriter = new HistogramLogWriter(log);
histogramLogWriter.outputComment("[Logging for: " + name + "]");
histogramLogWriter.outputLogFormatVersion();
histogramLogWriter.outputStartTime(System.currentTimeMillis());
histogramLogWriter.outputLegend();
}

/**
* It appears latency is reported in micros. Using {@link Recorder} to
* support concurrent updates to histogram.
*
* @see com.yahoo.ycsb.OneMeasurement#measure(int)
*/
public void measure(int latencyInMicros) {
histogram.recordValue(latencyInMicros);
}

/**
* This is called from a main thread, on orderly termination.
*
* @see com.yahoo.ycsb.measurements.OneMeasurement#exportMeasurements(com.yahoo.ycsb.measurements.exporter.MeasurementsExporter)
*/
@Override
public void exportMeasurements(MeasurementsExporter exporter) throws IOException {
// accumulate the last interval which was not caught by status thread
Histogram intervalHistogram = getIntervalHistogramAndAccumulate();
if(histogramLogWriter != null) {
if (histogramLogWriter != null) {
histogramLogWriter.outputIntervalHistogram(intervalHistogram);
// we can close now
log.close();
Expand All @@ -99,49 +99,45 @@ public void exportMeasurements(MeasurementsExporter exporter) throws IOException
exporter.write(getName(), "AverageLatency(us)", totalHistogram.getMean());
exporter.write(getName(), "MinLatency(us)", totalHistogram.getMinValue());
exporter.write(getName(), "MaxLatency(us)", totalHistogram.getMaxValue());
exporter.write(getName(), "95thPercentileLatency(ms)", totalHistogram.getValueAtPercentile(90)/1000);
exporter.write(getName(), "99thPercentileLatency(ms)", totalHistogram.getValueAtPercentile(99)/1000);

for (Map.Entry<Integer, AtomicInteger> entry : returncodes.entrySet()) {
exporter.write(getName(), "Return=" + entry.getKey(), entry.getValue().get());
}
exporter.write(getName(), "95thPercentileLatency(us)", totalHistogram.getValueAtPercentile(90));
exporter.write(getName(), "99thPercentileLatency(us)", totalHistogram.getValueAtPercentile(99));

exportReturnCodes(exporter);
}

/**
* This is called periodically from the StatusThread. There's a single StatusThread per Client process.
* We optionally serialize the interval to log on this opportunity.
* @see com.yahoo.ycsb.measurements.OneMeasurement#getSummary()
*/
@Override
public String getSummary() {
Histogram intervalHistogram = getIntervalHistogramAndAccumulate();
// we use the summary interval as the histogram file interval.
if(histogramLogWriter != null) {
histogramLogWriter.outputIntervalHistogram(intervalHistogram);
}

DecimalFormat d = new DecimalFormat("#.##");
return "[" + getName() +
": Count=" + intervalHistogram.getTotalCount() +
", Max=" + intervalHistogram.getMaxValue() +
", Min=" + intervalHistogram.getMinValue() +
", Avg=" + d.format(intervalHistogram.getMean()) +
", 90=" + d.format(intervalHistogram.getValueAtPercentile(90)) +
", 99=" + d.format(intervalHistogram.getValueAtPercentile(99)) +
", 99.9=" + d.format(intervalHistogram.getValueAtPercentile(99.9)) +
", 99.99=" + d.format(intervalHistogram.getValueAtPercentile(99.99)) +"]";
}

private Histogram getIntervalHistogramAndAccumulate() {
Histogram intervalHistogram = histogram.getIntervalHistogram();
// add this to the total time histogram.
if (totalHistogram == null) {
totalHistogram = intervalHistogram;
}
else {
totalHistogram.add(intervalHistogram);
}
return intervalHistogram;
}
/**
* This is called periodically from the StatusThread. There's a single
* StatusThread per Client process. We optionally serialize the interval to
* log on this opportunity.
*
* @see com.yahoo.ycsb.measurements.OneMeasurement#getSummary()
*/
@Override
public String getSummary() {
Histogram intervalHistogram = getIntervalHistogramAndAccumulate();
// we use the summary interval as the histogram file interval.
if (histogramLogWriter != null) {
histogramLogWriter.outputIntervalHistogram(intervalHistogram);
}

DecimalFormat d = new DecimalFormat("#.##");
return "[" + getName() + ": Count=" + intervalHistogram.getTotalCount() + ", Max="
+ intervalHistogram.getMaxValue() + ", Min=" + intervalHistogram.getMinValue() + ", Avg="
+ d.format(intervalHistogram.getMean()) + ", 90=" + d.format(intervalHistogram.getValueAtPercentile(90))
+ ", 99=" + d.format(intervalHistogram.getValueAtPercentile(99)) + ", 99.9="
+ d.format(intervalHistogram.getValueAtPercentile(99.9)) + ", 99.99="
+ d.format(intervalHistogram.getValueAtPercentile(99.99)) + "]";
}

private Histogram getIntervalHistogramAndAccumulate() {
Histogram intervalHistogram = histogram.getIntervalHistogram();
// add this to the total time histogram.
if (totalHistogram == null) {
totalHistogram = intervalHistogram;
} else {
totalHistogram.add(intervalHistogram);
}
return intervalHistogram;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -109,19 +109,17 @@ public void exportMeasurements(MeasurementsExporter exporter) throws IOException
opcounter+=histogram[i];
if ( (!done95th) && (((double)opcounter)/((double)operations)>=0.95) )
{
exporter.write(getName(), "95thPercentileLatency(ms)", i);
exporter.write(getName(), "95thPercentileLatency(us)", i*1000);
done95th=true;
}
if (((double)opcounter)/((double)operations)>=0.99)
{
exporter.write(getName(), "99thPercentileLatency(ms)", i);
exporter.write(getName(), "99thPercentileLatency(us)", i*1000);
break;
}
}

for (Map.Entry<Integer, AtomicInteger> entry : returncodes.entrySet()) {
exporter.write(getName(), "Return=" + entry.getKey(), entry.getValue().get());
}
exportReturnCodes(exporter);

for (int i=0; i<_buckets; i++)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,24 +125,18 @@ public void measure(int latency)


@Override
public void exportMeasurements(MeasurementsExporter exporter) throws IOException
{
public void exportMeasurements(MeasurementsExporter exporter) throws IOException {
checkEndOfUnit(true);

exporter.write(getName(), "Operations", operations);
exporter.write(getName(), "AverageLatency(us)", (((double)totallatency)/((double)operations)));
exporter.write(getName(), "AverageLatency(us)", (((double) totallatency) / ((double) operations)));
exporter.write(getName(), "MinLatency(us)", min);
exporter.write(getName(), "MaxLatency(us)", max);

//TODO: 95th and 99th percentile latency

// TODO: 95th and 99th percentile latency

for (Map.Entry<Integer, AtomicInteger> entry : returncodes.entrySet()) {
exporter.write(getName(), "Return=" + entry.getKey(), entry.getValue().get());
}

for (SeriesUnit unit : _measurements)
{
exportReturnCodes(exporter);
for (SeriesUnit unit : _measurements) {
exporter.write(getName(), Long.toString(unit.time), unit.average);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@
public class TestMeasurementsExporter {
@Test
public void testJSONArrayMeasurementsExporter() throws IOException {
Measurements mm = new Measurements(new Properties());
Properties props = new Properties();
props.put(Measurements.MEASUREMENT_TYPE_PROPERTY, "histogram");
Measurements mm = new Measurements(props);
ByteArrayOutputStream out = new ByteArrayOutputStream();
JSONArrayMeasurementsExporter export = new JSONArrayMeasurementsExporter(out);

Expand Down