Skip to content

Commit

Permalink
Merge pull request #461 from stfeng/master
Browse files Browse the repository at this point in the history
Add a new and optional measurement type called "RAW" which outputs every dat apoint of a run.
  • Loading branch information
allanbank committed Nov 4, 2015
2 parents 5d0aba3 + 8bfadae commit 624033b
Show file tree
Hide file tree
Showing 3 changed files with 265 additions and 11 deletions.
49 changes: 38 additions & 11 deletions core/src/main/java/com/yahoo/ycsb/measurements/Measurements.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,19 @@
* @author cooperb
*
*/
public class Measurements
{
public class Measurements {
/**
* All supported measurement types are defined in this enum.
*
*/
public enum MeasurementType {
HISTOGRAM,
HDRHISTOGRAM,
HDRHISTOGRAM_AND_HISTOGRAM,
HDRHISTOGRAM_AND_RAW,
TIMESERIES,
RAW
}

public static final String MEASUREMENT_TYPE_PROPERTY = "measurementtype";
private static final String MEASUREMENT_TYPE_PROPERTY_DEFAULT = "hdrhistogram";
Expand Down Expand Up @@ -61,7 +72,7 @@ public synchronized static Measurements getMeasurements()

final ConcurrentHashMap<String,OneMeasurement> _opToMesurementMap;
final ConcurrentHashMap<String,OneMeasurement> _opToIntendedMesurementMap;
final int _measurementType;
final MeasurementType _measurementType;
final int _measurementInterval;
private Properties _props;

Expand All @@ -78,19 +89,27 @@ public Measurements(Properties props)
String mTypeString = _props.getProperty(MEASUREMENT_TYPE_PROPERTY, MEASUREMENT_TYPE_PROPERTY_DEFAULT);
if (mTypeString.equals("histogram"))
{
_measurementType = 0;
_measurementType = MeasurementType.HISTOGRAM;
}
else if (mTypeString.equals("hdrhistogram"))
{
_measurementType = 1;
_measurementType = MeasurementType.HDRHISTOGRAM;
}
else if (mTypeString.equals("hdrhistogram+histogram"))
{
_measurementType = 2;
_measurementType = MeasurementType.HDRHISTOGRAM_AND_HISTOGRAM;
}
else if (mTypeString.equals("hdrhistogram+raw"))
{
_measurementType = MeasurementType.HDRHISTOGRAM_AND_RAW;
}
else if (mTypeString.equals("timeseries"))
{
_measurementType = 3;
_measurementType = MeasurementType.TIMESERIES;
}
else if (mTypeString.equals("raw"))
{
_measurementType = MeasurementType.RAW;
}
else {
throw new IllegalArgumentException("unknown "+MEASUREMENT_TYPE_PROPERTY+"="+mTypeString);
Expand Down Expand Up @@ -118,16 +137,24 @@ OneMeasurement constructOneMeasurement(String name)
{
switch (_measurementType)
{
case 0:
case HISTOGRAM:
return new OneMeasurementHistogram(name, _props);
case 1:
case HDRHISTOGRAM:
return new OneMeasurementHdrHistogram(name, _props);
case 2:
case HDRHISTOGRAM_AND_HISTOGRAM:
return new TwoInOneMeasurement(name,
new OneMeasurementHdrHistogram("Hdr"+name, _props),
new OneMeasurementHistogram("Bucket"+name, _props));
default:
case HDRHISTOGRAM_AND_RAW:
return new TwoInOneMeasurement(name,
new OneMeasurementHdrHistogram("Hdr"+name, _props),
new OneMeasurementHistogram("Raw"+name, _props));
case TIMESERIES:
return new OneMeasurementTimeSeries(name, _props);
case RAW:
return new OneMeasurementRaw(name, _props);
default:
throw new AssertionError("Impossible to be here. Dead code reached. Bugs?");
}
}

Expand Down
210 changes: 210 additions & 0 deletions core/src/main/java/com/yahoo/ycsb/measurements/OneMeasurementRaw.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
/**
* Copyright (c) 2015 Google Inc. All rights reserved.
*
* 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. See accompanying
* LICENSE file.
*/

package com.yahoo.ycsb.measurements;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Properties;
import java.util.LinkedList;
import java.util.Collections;
import java.util.Comparator;
import com.yahoo.ycsb.measurements.exporter.MeasurementsExporter;

/**
* Record a series of measurements as raw data points without down sampling,
* optionally write to an output file when configured.
*
* @author stfeng
*
*/
public class OneMeasurementRaw extends OneMeasurement {
/**
* One raw data point, two fields: timestamp (ms) when the datapoint is
* inserted, and the value.
*/
class RawDataPoint {
private final long timestamp;
private final int value;

public RawDataPoint(int value) {
this.timestamp = System.currentTimeMillis();
this.value = value;
}

public long timeStamp() {
return timestamp;
}

public int value() {
return value;
}
}

class RawDataPointComparator implements Comparator<RawDataPoint> {
@Override
public int compare(RawDataPoint p1, RawDataPoint p2){
if (p1.value() < p2.value()){
return -1;
} else if (p1.value() == p2.value()) {
return 0;
} else {
return 1;
}
}
}

/**
* Optionally, user can configure an output file to save the raw data points.
* Default is none, raw results will be written to stdout.
*
*/
public static final String OUTPUT_FILE_PATH = "measurement.raw.output_file";
public static final String OUTPUT_FILE_PATH_DEFAULT = "";

/**
* Optionally, user can request to not output summary stats. This is useful
* if the user chains the raw measurement type behind the HdrHistogram type
* which already outputs summary stats. But even in that case, the user may
* still want this class to compute summary stats for them, especially if
* they want accurate computation of percentiles (because percentils computed
* by histogram classes are still approximations).
*/
public static final String NO_SUMMARY_STATS = "measurement.raw.no_summary";
public static final String NO_SUMMARY_STATS_DEFAULT = "false";

private String outputFilePath = "";
private final PrintStream outputStream;

private boolean noSummaryStats = false;

private LinkedList<RawDataPoint> measurements;
private long totalLatency = 0;

// A window of stats to print summary for at the next getSummary() call.
// It's supposed to be a one line summary, so we will just print count and
// average.
private int windowOperations = 0;
private long windowTotalLatency = 0;

public OneMeasurementRaw(String name, Properties props) {
super(name);

outputFilePath = props.getProperty(OUTPUT_FILE_PATH,
OUTPUT_FILE_PATH_DEFAULT);
if (!outputFilePath.isEmpty()) {
System.out.println("Raw data measurement: will output to result file: " +
outputFilePath);

try {
outputStream = new PrintStream(
new FileOutputStream(outputFilePath, true),
true);
} catch (FileNotFoundException e) {
throw new RuntimeException("Failed to open raw data output file", e);
}

} else{
System.out.println("Raw data measurement: will output to stdout.");
outputStream = System.out;

}

noSummaryStats = Boolean.parseBoolean(props.getProperty(NO_SUMMARY_STATS,
NO_SUMMARY_STATS_DEFAULT));

measurements = new LinkedList<RawDataPoint>();
}

@Override
public synchronized void measure(int latency) {
totalLatency += latency;
windowTotalLatency += latency;
windowOperations++;

measurements.add(new RawDataPoint(latency));
}

@Override
public void exportMeasurements(MeasurementsExporter exporter)
throws IOException {
// Output raw data points first then print out a summary of percentiles to
// stdout.

outputStream.println(getName() +
" latency raw data: op, timestamp(ms), latency(us)");
for (RawDataPoint point : measurements) {
outputStream.println(
String.format("%s,%d,%d", getName(), point.timeStamp(),
point.value()));
}
if (outputStream != System.out) {
outputStream.close();
}

int totalOps = measurements.size();
exporter.write(getName(), "Total Operations", totalOps);
if (totalOps > 0 && !noSummaryStats) {
exporter.write(getName(),
"Below is a summary of latency in microseconds:", -1);
exporter.write(getName(), "Average",
(double)totalLatency / (double)totalOps);

Collections.sort(measurements, new RawDataPointComparator());

exporter.write(getName(), "Min", measurements.get(0).value());
exporter.write(
getName(), "Max", measurements.get(totalOps - 1).value());
exporter.write(
getName(), "p1", measurements.get((int)(totalOps*0.01)).value());
exporter.write(
getName(), "p5", measurements.get((int)(totalOps*0.05)).value());
exporter.write(
getName(), "p50", measurements.get((int)(totalOps*0.5)).value());
exporter.write(
getName(), "p90", measurements.get((int)(totalOps*0.9)).value());
exporter.write(
getName(), "p95", measurements.get((int)(totalOps*0.95)).value());
exporter.write(
getName(), "p99", measurements.get((int)(totalOps*0.99)).value());
exporter.write(getName(), "p99.9",
measurements.get((int)(totalOps*0.999)).value());
exporter.write(getName(), "p99.99",
measurements.get((int)(totalOps*0.9999)).value());
}

exportReturnCodes(exporter);
}

@Override
public synchronized String getSummary() {
if (windowOperations == 0) {
return "";
}

String toReturn = String.format("%s count: %d, average latency(us): %.2f",
getName(), windowOperations,
(double)windowTotalLatency / (double)windowOperations);

windowTotalLatency=0;
windowOperations=0;

return toReturn;
}
}
17 changes: 17 additions & 0 deletions workloads/workload_template
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,23 @@ table=usertable
# How the latency measurements are presented
measurementtype=histogram
#measurementtype=timeseries
#measurementtype=raw
# When measurementtype is set to raw, measurements will be output
# as RAW datapoints in the following csv format:
# "operation, timestamp of the measurement, latency in us"
#
# Raw datapoints are collected in-memory while the test is running. Each
# data point consumes about 50 bytes (including java object overhead).
# For a typical run of 1 million to 10 million operations, this should
# fit into memory most of the time. If you plan to do 100s of millions of
# operations per run, consider provisioning a machine with larger RAM when using
# the RAW measurement type, or split the run into multiple runs.
#
# Optionally, you can specify an output file to save raw datapoints.
# Otherwise, raw datapoints will be written to stdout.
# The output file will be appended to if it already exists, otherwise
# a new output file will be created.
#measurement.raw.output_file = /tmp/your_output_file_for_this_run

# The range of latencies to track in the histogram (milliseconds)
histogram.buckets=1000
Expand Down

0 comments on commit 624033b

Please sign in to comment.