[Git][java-team/hdrhistogram][upstream] New upstream version 2.1.11
Emmanuel Bourg
gitlab at salsa.debian.org
Sat Jan 19 23:13:49 GMT 2019
Emmanuel Bourg pushed to branch upstream at Debian Java Maintainers / hdrhistogram
Commits:
ecf9920b by Emmanuel Bourg at 2019-01-19T22:33:10Z
New upstream version 2.1.11
- - - - -
29 changed files:
- GoogleChartsExample/plotFiles.html
- README.md
- pom.xml
- src/main/java/org/HdrHistogram/AbstractHistogram.java
- − src/main/java/org/HdrHistogram/AbstractHistogramLogReader.java
- src/main/java/org/HdrHistogram/Base64Helper.java
- src/main/java/org/HdrHistogram/ConcurrentHistogram.java
- src/main/java/org/HdrHistogram/DoubleHistogram.java
- src/main/java/org/HdrHistogram/DoubleRecorder.java
- + src/main/java/org/HdrHistogram/DoubleValueRecorder.java
- src/main/java/org/HdrHistogram/Histogram.java
- src/main/java/org/HdrHistogram/HistogramLogProcessor.java
- src/main/java/org/HdrHistogram/HistogramLogReader.java
- + src/main/java/org/HdrHistogram/HistogramLogScanner.java
- src/main/java/org/HdrHistogram/IntCountsHistogram.java
- src/main/java/org/HdrHistogram/LinearIterator.java
- src/main/java/org/HdrHistogram/Recorder.java
- src/main/java/org/HdrHistogram/ShortCountsHistogram.java
- src/main/java/org/HdrHistogram/SingleWriterDoubleRecorder.java
- src/main/java/org/HdrHistogram/SingleWriterRecorder.java
- src/main/java/org/HdrHistogram/SynchronizedDoubleHistogram.java
- src/main/java/org/HdrHistogram/SynchronizedHistogram.java
- + src/main/java/org/HdrHistogram/ValueRecorder.java
- src/perf/java/org/HdrHistogram/HistogramPerfTest.java
- src/test/java/org/HdrHistogram/DoubleHistogramTest.java
- src/test/java/org/HdrHistogram/HistogramAutosizingTest.java
- src/test/java/org/HdrHistogram/HistogramDataAccessTest.java
- src/test/java/org/HdrHistogram/HistogramTest.java
- src/test/java/org/HdrHistogram/RecorderTest.java
Changes:
=====================================
GoogleChartsExample/plotFiles.html
=====================================
@@ -188,7 +188,6 @@
var names = [];
var histos = [];
- var fileCount = 0;
fileDisplayArea.innerText = "file selected...\n";
@@ -375,7 +374,7 @@ Percentile range:
<script type="text/javascript">
function showValue(newValue) {
var x = Math.pow(10, newValue);
- percentile = 100.0 - (100.0 / x);
+ var percentile = 100.0 - (100.0 / x);
document.getElementById("percentileRange").innerHTML=percentile + "%";
maxPercentile = x;
drawChart();
=====================================
README.md
=====================================
@@ -2,15 +2,19 @@ HdrHistogram
----------------------------------------------
[](https://gitter.im/HdrHistogram/HdrHistogram?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[](https://travis-ci.org/HdrHistogram/HdrHistogram)
-[](http://www.javadoc.io/doc/org.hdrhistogram/HdrHistogram)
+[](http://www.javadoc.io/doc/org.hdrhistogram/HdrHistogram)
+[](https://lgtm.com/projects/g/HdrHistogram/HdrHistogram/alerts/)
+[](https://lgtm.com/projects/g/HdrHistogram/HdrHistogram/context:java)
----------------------------------------------------------------------------
HdrHistogram: A High Dynamic Range (HDR) Histogram
-This respository currently includes Java and C# implementations of
-HdrHistogram, C, Python, Erlang, and Go ports can be found in other
-respositories. All of which share common concepts and data
-representation capabilities.
+This respository currently includes a Java implementation of
+HdrHistogram. C, C#/.NET, Python, Javascript, Rust, Erlang, and Go ports
+can be found in other respositories. All of which share common concepts
+and data representation capabilities. Look at repositories under the
+[HdrHistogram organization](https://github.com/HdrHistogram) for various
+implementations and useful tools.
Note: The below is an excerpt from a Histogram JavaDoc. While much
of it generally applies to other language implementations as well,
=====================================
pom.xml
=====================================
@@ -2,15 +2,9 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>org.sonatype.oss</groupId>
- <artifactId>oss-parent</artifactId>
- <version>7</version>
- </parent>
-
<groupId>org.hdrhistogram</groupId>
<artifactId>HdrHistogram</artifactId>
- <version>2.1.10</version>
+ <version>2.1.11</version>
<name>HdrHistogram</name>
@@ -52,7 +46,7 @@
<url>scm:git:git://github.com/HdrHistogram/HdrHistogram.git</url>
<connection>scm:git:git://github.com/HdrHistogram/HdrHistogram.git</connection>
<developerConnection>scm:git:git at github.com:HdrHistogram/HdrHistogram.git</developerConnection>
- <tag>HdrHistogram-2.1.10</tag>
+ <tag>HdrHistogram-2.1.11</tag>
</scm>
@@ -127,7 +121,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
- <version>2.3.2</version>
+ <version>3.8.0</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
@@ -145,9 +139,12 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
- <version>2.5</version>
+ <version>2.5.3</version>
<configuration>
- <arguments>-Dgpg.passphrase=${gpg.passphrase}</arguments>
+ <autoVersionSubmodules>true</autoVersionSubmodules>
+ <useReleaseProfile>false</useReleaseProfile>
+ <releaseProfiles>release</releaseProfiles>
+ <goals>deploy</goals>
</configuration>
</plugin>
<plugin>
@@ -203,12 +200,44 @@
</execution>
</executions>
</plugin>
+ <plugin>
+ <groupId>org.sonatype.plugins</groupId>
+ <artifactId>nexus-staging-maven-plugin</artifactId>
+ <version>1.6.7</version>
+ <extensions>true</extensions>
+ <configuration>
+ <serverId>ossrh</serverId>
+ <nexusUrl>https://oss.sonatype.org/</nexusUrl>
+ <autoReleaseAfterClose>true</autoReleaseAfterClose>
+ </configuration>
+ </plugin>
</plugins>
</build>
<profiles>
<profile>
- <id>release-sign-artifacts</id>
+ <id>deploy</id>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-gpg-plugin</artifactId>
+ <version>1.5</version>
+ <executions>
+ <execution>
+ <id>sign-artifacts</id>
+ <phase>verify</phase>
+ <goals>
+ <goal>sign</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ <profile>
+ <id>release</id>
<activation>
<property>
<name>performRelease</name>
@@ -220,10 +249,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
- <version>1.4</version>
- <configuration>
- <passphrase>${gpg.passphrase}</passphrase>
- </configuration>
+ <version>1.5</version>
<executions>
<execution>
<id>sign-artifacts</id>
=====================================
src/main/java/org/HdrHistogram/AbstractHistogram.java
=====================================
@@ -70,10 +70,12 @@ abstract class AbstractHistogramBase extends EncodableHistogram {
return doubleToIntegerValueConversionRatio;
}
- void setIntegerToDoubleValueConversionRatio(double integerToDoubleValueConversionRatio) {
+ void nonConcurrentSetIntegerToDoubleValueConversionRatio(double integerToDoubleValueConversionRatio) {
this.integerToDoubleValueConversionRatio = integerToDoubleValueConversionRatio;
this.doubleToIntegerValueConversionRatio = 1.0/integerToDoubleValueConversionRatio;
}
+
+ abstract void setIntegerToDoubleValueConversionRatio(double integerToDoubleValueConversionRatio);
}
/**
@@ -95,7 +97,7 @@ abstract class AbstractHistogramBase extends EncodableHistogram {
* See package description for {@link org.HdrHistogram} for details.
*
*/
-public abstract class AbstractHistogram extends AbstractHistogramBase implements Serializable {
+public abstract class AbstractHistogram extends AbstractHistogramBase implements ValueRecorder, Serializable {
// "Hot" accessed fields (used in the the value recording code path) are bunched here, such
// that they will have a good chance of ending up in the same cache line as the totalCounts and
@@ -422,6 +424,7 @@ public abstract class AbstractHistogram extends AbstractHistogramBase implements
* @param value The value to be recorded
* @throws ArrayIndexOutOfBoundsException (may throw) if value is exceeds highestTrackableValue
*/
+ @Override
public void recordValue(final long value) throws ArrayIndexOutOfBoundsException {
recordSingleValue(value);
}
@@ -433,6 +436,7 @@ public abstract class AbstractHistogram extends AbstractHistogramBase implements
* @param count The number of occurrences of this value to record
* @throws ArrayIndexOutOfBoundsException (may throw) if value is exceeds highestTrackableValue
*/
+ @Override
public void recordValueWithCount(final long value, final long count) throws ArrayIndexOutOfBoundsException {
recordCountAtValue(count, value);
}
@@ -458,6 +462,7 @@ public abstract class AbstractHistogram extends AbstractHistogramBase implements
* than expectedIntervalBetweenValueSamples
* @throws ArrayIndexOutOfBoundsException (may throw) if value is exceeds highestTrackableValue
*/
+ @Override
public void recordValueWithExpectedInterval(final long value, final long expectedIntervalBetweenValueSamples)
throws ArrayIndexOutOfBoundsException {
recordSingleValueWithExpectedInterval(value, expectedIntervalBetweenValueSamples);
@@ -529,7 +534,7 @@ public abstract class AbstractHistogram extends AbstractHistogramBase implements
private void handleRecordException(final long count, final long value, Exception ex) {
if (!autoResize) {
- throw new ArrayIndexOutOfBoundsException("value outside of histogram covered range. Caused by: " + ex);
+ throw new ArrayIndexOutOfBoundsException("value " + value + " outside of histogram covered range. Caused by: " + ex);
}
resize(value);
int countsIndex = countsArrayIndex(value);
@@ -574,6 +579,7 @@ public abstract class AbstractHistogram extends AbstractHistogramBase implements
/**
* Reset the contents and stats of this histogram
*/
+ @Override
public void reset() {
clearCounts();
resetMaxValue(0);
@@ -831,7 +837,7 @@ public abstract class AbstractHistogram extends AbstractHistogramBase implements
long maxValueBeforeShift = maxValueUpdater.getAndSet(this, 0);
long minNonZeroValueBeforeShift = minNonZeroValueUpdater.getAndSet(this, Long.MAX_VALUE);
- boolean lowestHalfBucketPopulated = (minNonZeroValueBeforeShift < subBucketHalfCount);
+ boolean lowestHalfBucketPopulated = (minNonZeroValueBeforeShift < (subBucketHalfCount << unitMagnitude));
// Perform the shift:
shiftNormalizingIndexByOffset(shiftAmount, lowestHalfBucketPopulated, newIntegerToDoubleValueConversionRatio);
@@ -848,19 +854,27 @@ public abstract class AbstractHistogram extends AbstractHistogramBase implements
// Save and clear the 0 value count:
long zeroValueCount = getCountAtIndex(0);
setCountAtIndex(0, 0);
+ int preShiftZeroIndex = normalizeIndex(0, getNormalizingIndexOffset(), countsArrayLength);
setNormalizingIndexOffset(getNormalizingIndexOffset() + shiftAmount);
// Deal with lower half bucket if needed:
if (lowestHalfBucketPopulated) {
- shiftLowestHalfBucketContentsLeft(shiftAmount);
+ if (shiftAmount <= 0) {
+ // Shifts with lowest half bucket populated can only be to the left.
+ // Any right shift logic calling this should have already verified that
+ // the lowest half bucket is not populated.
+ throw new ArrayIndexOutOfBoundsException(
+ "Attempt to right-shift with already-recorded value counts that would underflow and lose precision");
+ }
+ shiftLowestHalfBucketContentsLeft(shiftAmount, preShiftZeroIndex);
}
// Restore the 0 value count:
setCountAtIndex(0, zeroValueCount);
}
- private void shiftLowestHalfBucketContentsLeft(int shiftAmount) {
+ private void shiftLowestHalfBucketContentsLeft(int shiftAmount, int preShiftZeroIndex) {
final int numberOfBinaryOrdersOfMagnitude = shiftAmount >> subBucketHalfCountMagnitude;
// The lowest half-bucket (not including the 0 value) is special: unlike all other half
@@ -881,9 +895,9 @@ public abstract class AbstractHistogram extends AbstractHistogramBase implements
for (int fromIndex = 1; fromIndex < subBucketHalfCount; fromIndex++) {
long toValue = valueFromIndex(fromIndex) << numberOfBinaryOrdersOfMagnitude;
int toIndex = countsArrayIndex(toValue);
- long countAtFromIndex = getCountAtNormalizedIndex(fromIndex);
+ long countAtFromIndex = getCountAtNormalizedIndex(fromIndex + preShiftZeroIndex);
setCountAtIndex(toIndex, countAtFromIndex);
- setCountAtNormalizedIndex(fromIndex, 0);
+ setCountAtNormalizedIndex(fromIndex + preShiftZeroIndex, 0);
}
// Note that the above loop only creates O(N) work for histograms that have values in
@@ -1008,9 +1022,25 @@ public abstract class AbstractHistogram extends AbstractHistogramBase implements
if (getMinNonZeroValue() != that.getMinNonZeroValue()) {
return false;
}
- for (int i = 0; i < countsArrayLength; i++) {
- if (getCountAtIndex(i) != that.getCountAtIndex(i)) {
- return false;
+ // 2 histograms may be equal but have different underlying array sizes. This can happen for instance due to
+ // resizing.
+ if (countsArrayLength == that.countsArrayLength) {
+ for (int i = 0; i < countsArrayLength; i++) {
+ if (getCountAtIndex(i) != that.getCountAtIndex(i)) {
+ return false;
+ }
+ }
+ }
+ else
+ {
+ // Comparing the values is valid here because we have already confirmed the histograms have the same total
+ // count. It would not be correct otherwise.
+ for (HistogramIterationValue value : this.recordedValues()) {
+ long countAtValueIteratedTo = value.getCountAtValueIteratedTo();
+ long valueIteratedTo = value.getValueIteratedTo();
+ if (that.getCountAtValue(valueIteratedTo) != countAtValueIteratedTo) {
+ return false;
+ }
}
}
return true;
@@ -1283,7 +1313,7 @@ public abstract class AbstractHistogram extends AbstractHistogramBase implements
while (recordedValuesIterator.hasNext()) {
HistogramIterationValue iterationValue = recordedValuesIterator.next();
totalValue += medianEquivalentValue(iterationValue.getValueIteratedTo())
- * iterationValue.getCountAtValueIteratedTo();
+ * (double) iterationValue.getCountAtValueIteratedTo();
}
return (totalValue * 1.0) / getTotalCount();
}
=====================================
src/main/java/org/HdrHistogram/AbstractHistogramLogReader.java deleted
=====================================
@@ -1,213 +0,0 @@
-/**
- * Written by Gil Tene of Azul Systems, and released to the public domain,
- * as explained at http://creativecommons.org/publicdomain/zero/1.0/
- *
- * @author Gil Tene
- */
-
-package org.HdrHistogram;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.InputStream;
-import java.nio.ByteBuffer;
-import java.util.Locale;
-import java.util.Scanner;
-import java.util.zip.DataFormatException;
-
-class AbstractHistogramLogReader {
-
- protected final Scanner scanner;
- private double startTimeSec = 0.0;
-
- /**
- * Constructs a new HistogramLogReader that produces intervals read from the specified file name.
- * @param inputFileName The name of the file to read from
- * @throws java.io.FileNotFoundException when unable to find inputFileName
- */
- public AbstractHistogramLogReader(final String inputFileName) throws FileNotFoundException {
- scanner = new Scanner(new File(inputFileName));
- initScanner();
- }
-
- /**
- * Constructs a new HistogramLogReader that produces intervals read from the specified InputStream.
- * @param inputStream The InputStream to read from
- */
- public AbstractHistogramLogReader(final InputStream inputStream) {
- scanner = new Scanner(inputStream);
- initScanner();
- }
-
- /**
- * Constructs a new HistogramLogReader that produces intervals read from the specified file.
- * @param inputFile The File to read from
- * @throws java.io.FileNotFoundException when unable to find inputFile
- */
- public AbstractHistogramLogReader(final File inputFile) throws FileNotFoundException {
- scanner = new Scanner(inputFile);
- initScanner();
- }
-
-
- private void initScanner() {
- scanner.useLocale(Locale.US);
- scanner.useDelimiter("[ ,\\r\\n]");
- }
-
- /**
- * get the latest start time found in the file so far (or 0.0),
- * per the log file format explained above. Assuming the "#[StartTime:" comment
- * line precedes the actual intervals recorded in the file, getStartTimeSec() can
- * be safely used after each interval is read to determine's the offset of that
- * interval's timestamp from the epoch.
- * @return latest Start Time found in the file (or 0.0 if non found)
- */
- public double getStartTimeSec() {
- return startTimeSec;
- }
-
- protected void setStartTimeSec(double startTimeSec) {
- this.startTimeSec = startTimeSec;
- }
-
- /**
- * Read the next interval histogram from the log, if interval falls within a time range.
- * <p>
- * Returns a histogram object if an interval line was found with an
- * associated start timestamp value that falls between startTimeSec and
- * endTimeSec, or null if no such interval line is found. Note that
- * the range is assumed to be in seconds relative to the actual
- * timestamp value found in each interval line in the log, and not
- * in absolute time.
- * <p>
- * Timestamps are assumed to appear in order in the log file, and as such
- * this method will return a null upon encountering a timestamp larger than
- * rangeEndTimeSec.
- * <p>
- * The histogram returned will have it's timestamp set to the absolute
- * timestamp calculated from adding the interval's indicated timestamp
- * value to the latest [optional] start time found in the log.
- * <p>
- * Upon encountering any unexpected format errors in reading the next
- * interval from the file, this method will return a null.
- * @param startTimeSec The (non-absolute time) start of the expected
- * time range, in seconds.
- * @param endTimeSec The (non-absolute time) end of the expected time
- * range, in seconds.
- * @return a histogram, or a null if no appropriate interval found
- */
- public EncodableHistogram nextIntervalHistogram(final Double startTimeSec,
- final Double endTimeSec) {
- return nextIntervalHistogram(startTimeSec, endTimeSec, false);
- }
-
- /**
- * Read the next interval histogram from the log, if interval falls within an absolute time range
- * <p>
- * Returns a histogram object if an interval line was found with an
- * associated absolute start timestamp value that falls between
- * absoluteStartTimeSec and absoluteEndTimeSec, or null if no such
- * interval line is found.
- * <p>
- * Timestamps are assumed to appear in order in the log file, and as such
- * this method will return a null upon encountering a timestamp larger than
- * rangeEndTimeSec.
- * <p>
- * The histogram returned will have it's timestamp set to the absolute
- * timestamp calculated from adding the interval's indicated timestamp
- * value to the latest [optional] start time found in the log.
- * <p>
- * Absolute timestamps are calculated by adding the timestamp found
- * with the recorded interval to the [latest, optional] start time
- * found in the log. The start time is indicated in the log with
- * a "#[StartTime: " followed by the start time in seconds.
- * <p>
- * Upon encountering any unexpected format errors in reading the next
- * interval from the file, this method will return a null.
- * @param absoluteStartTimeSec The (absolute time) start of the expected
- * time range, in seconds.
- * @param absoluteEndTimeSec The (absolute time) end of the expected
- * time range, in seconds.
- * @return A histogram, or a null if no appropriate interval found
- */
- public EncodableHistogram nextAbsoluteIntervalHistogram(final Double absoluteStartTimeSec,
- final Double absoluteEndTimeSec) {
- return nextIntervalHistogram(absoluteStartTimeSec, absoluteEndTimeSec, true);
- }
-
-
- /**
- * Read the next interval histogram from the log. Returns a Histogram object if
- * an interval line was found, or null if not.
- * <p>Upon encountering any unexpected format errors in reading the next interval
- * from the file, this method will return a null.
- * @return a DecodedInterval, or a null if no appropriate interval found
- */
- public EncodableHistogram nextIntervalHistogram() {
- return nextIntervalHistogram(0.0, Long.MAX_VALUE * 1.0, true);
- }
-
- private EncodableHistogram nextIntervalHistogram(final Double rangeStartTimeSec,
- final Double rangeEndTimeSec, boolean absolute) {
- while (scanner.hasNextLine()) {
- try {
- if (scanner.hasNext("\\#.*")) {
- // comment line
- if (scanner.hasNext("#\\[StartTime:")) {
- scanner.next("#\\[StartTime:");
- if (scanner.hasNextDouble()) {
- setStartTimeSec(scanner.nextDouble()); // start time represented as seconds since epoch
- }
- }
- scanner.nextLine();
- continue;
- }
-
- if (scanner.hasNext("\"StartTimestamp\".*")) {
- // Legend line
- scanner.nextLine();
- continue;
- }
-
- // Decode: startTimestamp, intervalLength, maxTime, histogramPayload
-
- final double offsetStartTimeStampSec = scanner.nextDouble(); // Timestamp start is expect to be in seconds
- final double absoluteStartTimeStampSec = getStartTimeSec() + offsetStartTimeStampSec;
-
- final double intervalLengthSec = scanner.nextDouble(); // Timestamp length is expect to be in seconds
- final double offsetEndTimeStampSec = offsetStartTimeStampSec + intervalLengthSec;
- final double absoluteEndTimeStampSec = getStartTimeSec() + offsetEndTimeStampSec;
-
- final double startTimeStampToCheckRangeOn = absolute ? absoluteStartTimeStampSec : offsetStartTimeStampSec;
-
- if (startTimeStampToCheckRangeOn < rangeStartTimeSec) {
- scanner.nextLine();
- continue;
- }
-
- if (startTimeStampToCheckRangeOn > rangeEndTimeSec) {
- return null;
- }
-
- scanner.nextDouble(); // Skip maxTime field, as max time can be deduced from the histogram.
- final String compressedPayloadString = scanner.next();
- final ByteBuffer buffer = ByteBuffer.wrap(
- Base64Helper.parseBase64Binary(compressedPayloadString));
-
- EncodableHistogram histogram = Histogram.decodeFromCompressedByteBuffer(buffer, 0);
-
- histogram.setStartTimeStamp((long) (absoluteStartTimeStampSec * 1000.0));
- histogram.setEndTimeStamp((long) (absoluteEndTimeStampSec * 1000.0));
-
- return histogram;
-
- } catch (java.util.NoSuchElementException ex) {
- return null;
- } catch (DataFormatException ex) {
- return null;
- }
- }
- return null;
- }
-}
=====================================
src/main/java/org/HdrHistogram/Base64Helper.java
=====================================
@@ -10,15 +10,20 @@ package org.HdrHistogram;
import java.lang.reflect.Method;
/**
- * Base64Helper exists to bridge gaps between Java SE platform support of Base64 encoding and decoding.
+ * Base64Helper exists to bridge inconsistencies in Java SE support of Base64 encoding and decoding.
* Earlier Java SE platforms (up to and including Java SE 8) supported base64 encode/decode via the
* javax.xml.bind.DatatypeConverter class, which was deprecated and eventually removed in Java SE 9.
- * Later Java SE platforms Java SE 8 and later support supported base64 encode/decode via the
+ * Later Java SE platforms (Java SE 8 and later) support base64 encode/decode via the
* java.util.Base64 class (first introduced in Java SE 8, and not available on e.g. Java SE 6 or 7).
*
+ * This makes it "hard" to write a single piece of source code that deals with base64 encodings and
+ * will compile and run on e.g. Java SE 7 AND Java SE 9. And such common source is a common need for
+ * libraries. This class is intended to encapsulate this "hard"-ness and hide the ugly pretzle-twising
+ * needed under the covers.
+ *
* Base64Helper provides a common API that works across Java SE 6..9 (and beyond hopefully), and
- * uses late binding (Reflection) to avoid javac-compile-time dependencies on a specific Java SE
- * version (e.g. beyond 6 or before 9).
+ * uses late binding (Reflection) internally to avoid javac-compile-time dependencies on a specific
+ * Java SE version (e.g. beyond 7 or before 9).
*
*/
public class Base64Helper {
=====================================
src/main/java/org/HdrHistogram/ConcurrentHistogram.java
=====================================
@@ -211,6 +211,8 @@ public class ConcurrentHistogram extends Histogram {
assert (countsArrayLength == activeCounts.length());
assert (countsArrayLength == inactiveCounts.length());
+ assert (activeCounts.getNormalizingIndexOffset() == inactiveCounts.getNormalizingIndexOffset());
+
if (normalizingIndexOffset == activeCounts.getNormalizingIndexOffset()) {
return; // Nothing to do.
}
@@ -225,13 +227,13 @@ public class ConcurrentHistogram extends Histogram {
// Handle the inactive lowest half bucket:
if ((shiftedAmount > 0) && lowestHalfBucketPopulated) {
- shiftLowestInactiveHalfBucketContentsLeft(shiftedAmount);
+ shiftLowestInactiveHalfBucketContentsLeft(shiftedAmount, zeroIndex);
}
// Restore the inactive 0 value count:
zeroIndex = normalizeIndex(0, inactiveCounts.getNormalizingIndexOffset(), inactiveCounts.length());
inactiveCounts.lazySet(zeroIndex, inactiveZeroValueCount);
-
+
inactiveCounts.doubleToIntegerValueConversionRatio = 1.0 / newIntegerToDoubleValueConversionRatio;
// switch active and inactive:
@@ -251,7 +253,7 @@ public class ConcurrentHistogram extends Histogram {
// Handle the newly inactive lowest half bucket:
if ((shiftedAmount > 0) && lowestHalfBucketPopulated) {
- shiftLowestInactiveHalfBucketContentsLeft(shiftedAmount);
+ shiftLowestInactiveHalfBucketContentsLeft(shiftedAmount, zeroIndex);
}
// Restore the newly inactive 0 value count:
@@ -275,7 +277,7 @@ public class ConcurrentHistogram extends Histogram {
}
}
- private void shiftLowestInactiveHalfBucketContentsLeft(final int shiftAmount) {
+ private void shiftLowestInactiveHalfBucketContentsLeft(final int shiftAmount, final int preShiftZeroIndex) {
final int numberOfBinaryOrdersOfMagnitude = shiftAmount >> subBucketHalfCountMagnitude;
// The lowest inactive half-bucket (not including the 0 value) is special: unlike all other half
@@ -298,9 +300,9 @@ public class ConcurrentHistogram extends Histogram {
int toIndex = countsArrayIndex(toValue);
int normalizedToIndex =
normalizeIndex(toIndex, inactiveCounts.getNormalizingIndexOffset(), inactiveCounts.length());
- long countAtFromIndex = inactiveCounts.get(fromIndex);
+ long countAtFromIndex = inactiveCounts.get(fromIndex + preShiftZeroIndex);
inactiveCounts.lazySet(normalizedToIndex, countAtFromIndex);
- inactiveCounts.lazySet(fromIndex, 0);
+ inactiveCounts.lazySet(fromIndex + preShiftZeroIndex, 0);
}
// Note that the above loop only creates O(N) work for histograms that have values in
@@ -344,34 +346,25 @@ public class ConcurrentHistogram extends Histogram {
return;
}
- int oldNormalizedZeroIndex =
- normalizeIndex(0, inactiveCounts.getNormalizingIndexOffset(), inactiveCounts.length());
-
- // Resize the current inactiveCounts:
- AtomicLongArray oldInactiveCounts = inactiveCounts;
- inactiveCounts =
+ // Allocate both counts arrays here, so if one allocation fails, neither will "take":
+ AtomicLongArrayWithNormalizingOffset newInactiveCounts1 =
new AtomicLongArrayWithNormalizingOffset(
newArrayLength,
inactiveCounts.getNormalizingIndexOffset()
);
+ AtomicLongArrayWithNormalizingOffset newInactiveCounts2 =
+ new AtomicLongArrayWithNormalizingOffset(
+ newArrayLength,
+ activeCounts.getNormalizingIndexOffset()
+ );
+
+
+ // Resize the current inactiveCounts:
+ AtomicLongArrayWithNormalizingOffset oldInactiveCounts = inactiveCounts;
+ inactiveCounts = newInactiveCounts1;
+
// Copy inactive contents to newly sized inactiveCounts:
- for (int i = 0 ; i < oldInactiveCounts.length(); i++) {
- inactiveCounts.lazySet(i, oldInactiveCounts.get(i));
- }
- if (oldNormalizedZeroIndex != 0) {
- // We need to shift the stuff from the zero index and up to the end of the array:
- int newNormalizedZeroIndex = oldNormalizedZeroIndex + countsDelta;
- int lengthToCopy = (newArrayLength - countsDelta) - oldNormalizedZeroIndex;
- int src, dst;
- for (src = oldNormalizedZeroIndex, dst = newNormalizedZeroIndex;
- src < oldNormalizedZeroIndex + lengthToCopy;
- src++, dst++) {
- inactiveCounts.lazySet(dst, oldInactiveCounts.get(src));
- }
- for (dst = oldNormalizedZeroIndex; dst < newNormalizedZeroIndex; dst++) {
- inactiveCounts.lazySet(dst, 0);
- }
- }
+ copyInactiveCountsContentsOnResize(oldInactiveCounts, countsDelta);
// switch active and inactive:
AtomicLongArrayWithNormalizingOffset tmp = activeCounts;
@@ -382,29 +375,10 @@ public class ConcurrentHistogram extends Histogram {
// Resize the newly inactiveCounts:
oldInactiveCounts = inactiveCounts;
- inactiveCounts =
- new AtomicLongArrayWithNormalizingOffset(
- newArrayLength,
- inactiveCounts.getNormalizingIndexOffset()
- );
+ inactiveCounts = newInactiveCounts2;
+
// Copy inactive contents to newly sized inactiveCounts:
- for (int i = 0 ; i < oldInactiveCounts.length(); i++) {
- inactiveCounts.lazySet(i, oldInactiveCounts.get(i));
- }
- if (oldNormalizedZeroIndex != 0) {
- // We need to shift the stuff from the zero index and up to the end of the array:
- int newNormalizedZeroIndex = oldNormalizedZeroIndex + countsDelta;
- int lengthToCopy = (newArrayLength - countsDelta) - oldNormalizedZeroIndex;
- int src, dst;
- for (src = oldNormalizedZeroIndex, dst = newNormalizedZeroIndex;
- src < oldNormalizedZeroIndex + lengthToCopy;
- src++, dst++) {
- inactiveCounts.lazySet(dst, oldInactiveCounts.get(src));
- }
- for (dst = oldNormalizedZeroIndex; dst < newNormalizedZeroIndex; dst++) {
- inactiveCounts.lazySet(dst, 0);
- }
- }
+ copyInactiveCountsContentsOnResize(oldInactiveCounts, countsDelta);
// switch active and inactive again:
tmp = activeCounts;
@@ -427,6 +401,34 @@ public class ConcurrentHistogram extends Histogram {
}
}
+ private void copyInactiveCountsContentsOnResize(
+ AtomicLongArrayWithNormalizingOffset oldInactiveCounts, int countsDelta) {
+ int oldNormalizedZeroIndex =
+ normalizeIndex(0,
+ oldInactiveCounts.getNormalizingIndexOffset(),
+ oldInactiveCounts.length());
+
+ // Copy old inactive contents to (current) newly sized inactiveCounts:
+ for (int i = 0; i < oldInactiveCounts.length(); i++) {
+ inactiveCounts.lazySet(i, oldInactiveCounts.get(i));
+ }
+ if (oldNormalizedZeroIndex != 0) {
+ // We need to shift the stuff from the zero index and up to the end of the array:
+ int newNormalizedZeroIndex = oldNormalizedZeroIndex + countsDelta;
+ int lengthToCopy = (inactiveCounts.length() - countsDelta) - oldNormalizedZeroIndex;
+ int src, dst;
+ for (src = oldNormalizedZeroIndex, dst = newNormalizedZeroIndex;
+ src < oldNormalizedZeroIndex + lengthToCopy;
+ src++, dst++) {
+ inactiveCounts.lazySet(dst, oldInactiveCounts.get(src));
+ }
+ for (dst = oldNormalizedZeroIndex; dst < newNormalizedZeroIndex; dst++) {
+ inactiveCounts.lazySet(dst, 0);
+ }
+ }
+ }
+
+
@Override
public void setAutoResize(final boolean autoResize) {
this.autoResize = true;
=====================================
src/main/java/org/HdrHistogram/DoubleHistogram.java
=====================================
@@ -50,7 +50,7 @@ import java.util.zip.Deflater;
* <p>
* See package description for {@link org.HdrHistogram} for details.
*/
-public class DoubleHistogram extends EncodableHistogram implements Serializable {
+public class DoubleHistogram extends EncodableHistogram implements DoubleValueRecorder, Serializable {
private static final double highestAllowedValueEver; // A value that will keep us from multiplying into infinity.
private long configuredHighestToLowestValueRatio;
@@ -287,8 +287,9 @@ public class DoubleHistogram extends EncodableHistogram implements Serializable
* Record a value in the histogram
*
* @param value The value to be recorded
- * @throws ArrayIndexOutOfBoundsException (may throw) if value is cannot be covered by the histogram's range
+ * @throws ArrayIndexOutOfBoundsException (may throw) if value cannot be covered by the histogram's range
*/
+ @Override
public void recordValue(final double value) throws ArrayIndexOutOfBoundsException {
recordSingleValue(value);
}
@@ -298,8 +299,9 @@ public class DoubleHistogram extends EncodableHistogram implements Serializable
*
* @param value The value to be recorded
* @param count The number of occurrences of this value to record
- * @throws ArrayIndexOutOfBoundsException (may throw) if value is cannot be covered by the histogram's range
+ * @throws ArrayIndexOutOfBoundsException (may throw) if value cannot be covered by the histogram's range
*/
+ @Override
public void recordValueWithCount(final double value, final long count) throws ArrayIndexOutOfBoundsException {
recordCountAtValue(count, value);
}
@@ -323,8 +325,9 @@ public class DoubleHistogram extends EncodableHistogram implements Serializable
* @param expectedIntervalBetweenValueSamples If expectedIntervalBetweenValueSamples is larger than 0, add
* auto-generated value records as appropriate if value is larger
* than expectedIntervalBetweenValueSamples
- * @throws ArrayIndexOutOfBoundsException (may throw) if value is cannot be covered by the histogram's range
+ * @throws ArrayIndexOutOfBoundsException (may throw) if value cannot be covered by the histogram's range
*/
+ @Override
public void recordValueWithExpectedInterval(final double value, final double expectedIntervalBetweenValueSamples)
throws ArrayIndexOutOfBoundsException {
recordValueWithCountAndExpectedInterval(value, 1, expectedIntervalBetweenValueSamples);
@@ -573,8 +576,11 @@ public class DoubleHistogram extends EncodableHistogram implements Serializable
/**
* Reset the contents and stats of this histogram
*/
+ @Override
public void reset() {
- integerValuesHistogram.clearCounts();
+ integerValuesHistogram.reset();
+ double initialLowestValueInAutoRange = Math.pow(2.0, 800);
+ init(configuredHighestToLowestValueRatio, initialLowestValueInAutoRange, integerValuesHistogram);
}
//
=====================================
src/main/java/org/HdrHistogram/DoubleRecorder.java
=====================================
@@ -22,10 +22,23 @@ import java.util.concurrent.atomic.AtomicLong;
* {@link DoubleRecorder#recordValueWithExpectedInterval} calls.
* Recording calls are wait-free on architectures that support atomic increment operations, and
* are lock-free on architectures that do not.
- *
+ * <p>
+ * A common pattern for using a {@link DoubleRecorder} looks like this:
+ * <br><pre><code>
+ * DoubleRecorder recorder = new DoubleRecorder(2); // Two decimal point accuracy
+ * DoubleHistogram intervalHistogram = null;
+ * ...
+ * [start of some loop construct that periodically wants to grab an interval histogram]
+ * ...
+ * // Get interval histogram, recycling previous interval histogram:
+ * intervalHistogram = recorder.getIntervalHistogram(intervalHistogram);
+ * histogramLogWriter.outputIntervalHistogram(intervalHistogram);
+ * ...
+ * [end of loop construct]
+ * </code></pre>
*/
-public class DoubleRecorder {
+public class DoubleRecorder implements DoubleValueRecorder {
private static AtomicLong instanceIdSequencer = new AtomicLong(1);
private final long instanceId = instanceIdSequencer.getAndIncrement();
@@ -70,6 +83,7 @@ public class DoubleRecorder {
* @param value the value to record
* @throws ArrayIndexOutOfBoundsException (may throw) if value is exceeds highestTrackableValue
*/
+ @Override
public void recordValue(final double value) {
long criticalValueAtEnter = recordingPhaser.writerCriticalSectionEnter();
try {
@@ -86,6 +100,7 @@ public class DoubleRecorder {
* @param count The number of occurrences of this value to record
* @throws ArrayIndexOutOfBoundsException (may throw) if value is exceeds highestTrackableValue
*/
+ @Override
public void recordValueWithCount(final double value, final long count) throws ArrayIndexOutOfBoundsException {
long criticalValueAtEnter = recordingPhaser.writerCriticalSectionEnter();
try {
@@ -111,6 +126,7 @@ public class DoubleRecorder {
* than expectedIntervalBetweenValueSamples
* @throws ArrayIndexOutOfBoundsException (may throw) if value is exceeds highestTrackableValue
*/
+ @Override
public void recordValueWithExpectedInterval(final double value, final double expectedIntervalBetweenValueSamples)
throws ArrayIndexOutOfBoundsException {
long criticalValueAtEnter = recordingPhaser.writerCriticalSectionEnter();
@@ -157,13 +173,49 @@ public class DoubleRecorder {
* getIntervalHistogram(histogramToRecycle)} will reset the value counts, and start accumulating value
* counts for the next interval
*
- * @param histogramToRecycle a previously returned interval histogram that may be recycled to avoid allocation and
+ * @param histogramToRecycle a previously returned interval histogram (from this instance of
+ * {@link DoubleRecorder}) that may be recycled to avoid allocation and
* copy operations.
* @return a histogram containing the value counts accumulated since the last interval histogram was taken.
*/
public synchronized DoubleHistogram getIntervalHistogram(DoubleHistogram histogramToRecycle) {
+ return getIntervalHistogram(histogramToRecycle, true);
+ }
+
+ /**
+ * Get an interval histogram, which will include a stable, consistent view of all value counts
+ * accumulated since the last interval histogram was taken.
+ * <p>
+ * {@link DoubleRecorder#getIntervalHistogram(DoubleHistogram histogramToRecycle)
+ * getIntervalHistogram(histogramToRecycle)}
+ * accepts a previously returned interval histogram that can be recycled internally to avoid allocation
+ * and content copying operations, and is therefore significantly more efficient for repeated use than
+ * {@link DoubleRecorder#getIntervalHistogram()} and
+ * {@link DoubleRecorder#getIntervalHistogramInto getIntervalHistogramInto()}. The provided
+ * {@code histogramToRecycle} must
+ * be either be null or an interval histogram returned by a previous call to
+ * {@link DoubleRecorder#getIntervalHistogram(DoubleHistogram histogramToRecycle)
+ * getIntervalHistogram(histogramToRecycle)} or
+ * {@link DoubleRecorder#getIntervalHistogram()}.
+ * <p>
+ * NOTE: The caller is responsible for not recycling the same returned interval histogram more than once. If
+ * the same interval histogram instance is recycled more than once, behavior is undefined.
+ * <p>
+ * Calling {@link DoubleRecorder#getIntervalHistogram(DoubleHistogram histogramToRecycle)
+ * getIntervalHistogram(histogramToRecycle)} will reset the value counts, and start accumulating value
+ * counts for the next interval
+ *
+ * @param histogramToRecycle a previously returned interval histogram that may be recycled to avoid allocation and
+ * copy operations.
+ * @param enforeContainingInstance if true, will only allow recycling of histograms previously returned from this
+ * instance of {@link DoubleRecorder}. If false, will allow recycling histograms
+ * previously returned by other instances of {@link DoubleRecorder}.
+ * @return a histogram containing the value counts accumulated since the last interval histogram was taken.
+ */
+ public synchronized DoubleHistogram getIntervalHistogram(DoubleHistogram histogramToRecycle,
+ boolean enforeContainingInstance) {
// Verify that replacement histogram can validly be used as an inactive histogram replacement:
- validateFitAsReplacementHistogram(histogramToRecycle);
+ validateFitAsReplacementHistogram(histogramToRecycle, enforeContainingInstance);
inactiveHistogram = (InternalConcurrentDoubleHistogram) histogramToRecycle;
performIntervalSample();
DoubleHistogram sampledHistogram = inactiveHistogram;
@@ -188,6 +240,7 @@ public class DoubleRecorder {
/**
* Reset any value counts accumulated thus far.
*/
+ @Override
public synchronized void reset() {
// the currently inactive histogram is reset each time we flip. So flipping twice resets both:
performIntervalSample();
@@ -245,15 +298,17 @@ public class DoubleRecorder {
}
}
- void validateFitAsReplacementHistogram(DoubleHistogram replacementHistogram) {
+ private void validateFitAsReplacementHistogram(DoubleHistogram replacementHistogram,
+ boolean enforeContainingInstance) {
boolean bad = true;
if (replacementHistogram == null) {
bad = false;
} else if ((replacementHistogram instanceof InternalConcurrentDoubleHistogram)
&&
- (((InternalConcurrentDoubleHistogram) replacementHistogram).containingInstanceId ==
- activeHistogram.containingInstanceId)
- ) {
+ ((!enforeContainingInstance) ||
+ (((InternalConcurrentDoubleHistogram) replacementHistogram).containingInstanceId ==
+ activeHistogram.containingInstanceId)
+ )) {
bad = false;
}
=====================================
src/main/java/org/HdrHistogram/DoubleValueRecorder.java
=====================================
@@ -0,0 +1,47 @@
+package org.HdrHistogram;
+
+public interface DoubleValueRecorder {
+
+ /**
+ * Record a value
+ *
+ * @param value The value to be recorded
+ * @throws ArrayIndexOutOfBoundsException (may throw) if value cannot be covered by the histogram's range
+ */
+ void recordValue(double value) throws ArrayIndexOutOfBoundsException;
+
+ /**
+ * Record a value (adding to the value's current count)
+ *
+ * @param value The value to be recorded
+ * @param count The number of occurrences of this value to record
+ * @throws ArrayIndexOutOfBoundsException (may throw) if value cannot be covered by the histogram's range
+ */
+ void recordValueWithCount(double value, long count) throws ArrayIndexOutOfBoundsException;
+
+ /**
+ * Record a value.
+ * <p>
+ * To compensate for the loss of sampled values when a recorded value is larger than the expected
+ * interval between value samples, will auto-generate an additional series of decreasingly-smaller
+ * (down to the expectedIntervalBetweenValueSamples) value records.
+ * <p>
+ * Note: This is a at-recording correction method, as opposed to the post-recording correction method provided
+ * by {@link DoubleHistogram#copyCorrectedForCoordinatedOmission(double)}.
+ * The two methods are mutually exclusive, and only one of the two should be be used on a given data set to correct
+ * for the same coordinated omission issue.
+ *
+ * @param value The value to record
+ * @param expectedIntervalBetweenValueSamples If expectedIntervalBetweenValueSamples is larger than 0, add
+ * auto-generated value records as appropriate if value is larger
+ * than expectedIntervalBetweenValueSamples
+ * @throws ArrayIndexOutOfBoundsException (may throw) if value cannot be covered by the histogram's range
+ */
+ void recordValueWithExpectedInterval(double value, double expectedIntervalBetweenValueSamples)
+ throws ArrayIndexOutOfBoundsException;
+
+ /**
+ * Reset the contents and collected stats
+ */
+ void reset();
+}
=====================================
src/main/java/org/HdrHistogram/Histogram.java
=====================================
@@ -87,6 +87,11 @@ public class Histogram extends AbstractHistogram {
this.normalizingIndexOffset = normalizingIndexOffset;
}
+ @Override
+ void setIntegerToDoubleValueConversionRatio(double integerToDoubleValueConversionRatio) {
+ nonConcurrentSetIntegerToDoubleValueConversionRatio(integerToDoubleValueConversionRatio);
+ }
+
@Override
void shiftNormalizingIndexByOffset(int offsetToAdd,
boolean lowestHalfBucketPopulated,
=====================================
src/main/java/org/HdrHistogram/HistogramLogProcessor.java
=====================================
@@ -51,36 +51,37 @@ import java.util.*;
*/
public class HistogramLogProcessor extends Thread {
- public static final String versionString = "Histogram Log Processor version " + Version.version;
+ static final String versionString = "Histogram Log Processor version " + Version.version;
private final HistogramLogProcessorConfiguration config;
private HistogramLogReader logReader;
private static class HistogramLogProcessorConfiguration {
- public boolean verbose = false;
- public String outputFileName = null;
- public String inputFileName = null;
- public String tag = null;
+ boolean verbose = false;
+ String outputFileName = null;
+ String inputFileName = null;
+ String tag = null;
- public double rangeStartTimeSec = 0.0;
- public double rangeEndTimeSec = Double.MAX_VALUE;
+ double rangeStartTimeSec = 0.0;
+ double rangeEndTimeSec = Double.MAX_VALUE;
- public boolean logFormatCsv = false;
- public boolean listTags = false;
- public boolean allTags = false;
+ boolean logFormatCsv = false;
+ boolean listTags = false;
+ boolean allTags = false;
- public boolean movingWindow = false;
- public double movingWindowPercentileToReport = 99.0;
- public long movingWindowLengthInMsec = 60000; // 1 minute
+ boolean movingWindow = false;
+ double movingWindowPercentileToReport = 99.0;
+ long movingWindowLengthInMsec = 60000; // 1 minute
- public int percentilesOutputTicksPerHalf = 5;
- public Double outputValueUnitRatio = 1000000.0; // default to msec units for output.
+ int percentilesOutputTicksPerHalf = 5;
+ Double outputValueUnitRatio = 1000000.0; // default to msec units for output.
- public boolean error = false;
- public String errorMessage = "";
+ double expectedIntervalForCoordinatedOmissionCorrection = 0.0;
- public HistogramLogProcessorConfiguration(final String[] args) {
+ String errorMessage = "";
+
+ HistogramLogProcessorConfiguration(final String[] args) {
boolean askedForHelp= false;
try {
for (int i = 0; i < args.length; ++i) {
@@ -93,25 +94,28 @@ public class HistogramLogProcessor extends Thread {
} else if (args[i].equals("-alltags")) {
allTags = true;
} else if (args[i].equals("-i")) {
- inputFileName = args[++i];
+ inputFileName = args[++i]; // lgtm [java/index-out-of-bounds]
} else if (args[i].equals("-tag")) {
- tag = args[++i];
+ tag = args[++i]; // lgtm [java/index-out-of-bounds]
} else if (args[i].equals("-mwp")) {
- movingWindowPercentileToReport = Double.parseDouble(args[++i]);
+ movingWindowPercentileToReport = Double.parseDouble(args[++i]); // lgtm [java/index-out-of-bounds]
movingWindow = true;
} else if (args[i].equals("-mwpl")) {
- movingWindowLengthInMsec = Long.parseLong(args[++i]);
+ movingWindowLengthInMsec = Long.parseLong(args[++i]); // lgtm [java/index-out-of-bounds]
movingWindow = true;
} else if (args[i].equals("-start")) {
- rangeStartTimeSec = Double.parseDouble(args[++i]);
+ rangeStartTimeSec = Double.parseDouble(args[++i]); // lgtm [java/index-out-of-bounds]
} else if (args[i].equals("-end")) {
- rangeEndTimeSec = Double.parseDouble(args[++i]);
+ rangeEndTimeSec = Double.parseDouble(args[++i]); // lgtm [java/index-out-of-bounds]
} else if (args[i].equals("-o")) {
- outputFileName = args[++i];
+ outputFileName = args[++i]; // lgtm [java/index-out-of-bounds]
} else if (args[i].equals("-percentilesOutputTicksPerHalf")) {
- percentilesOutputTicksPerHalf = Integer.parseInt(args[++i]);
+ percentilesOutputTicksPerHalf = Integer.parseInt(args[++i]); // lgtm [java/index-out-of-bounds]
} else if (args[i].equals("-outputValueUnitRatio")) {
- outputValueUnitRatio = Double.parseDouble(args[++i]);
+ outputValueUnitRatio = Double.parseDouble(args[++i]); // lgtm [java/index-out-of-bounds]
+ } else if (args[i].equals("-correctLogWithKnownCoordinatedOmission")) {
+ expectedIntervalForCoordinatedOmissionCorrection =
+ Double.parseDouble(args[++i]); // lgtm [java/index-out-of-bounds]
} else if (args[i].equals("-h")) {
askedForHelp = true;
throw new Exception("Help: " + args[i]);
@@ -119,9 +123,7 @@ public class HistogramLogProcessor extends Thread {
throw new Exception("Invalid args: " + args[i]);
}
}
-
} catch (Exception e) {
- error = true;
errorMessage = "Error: " + versionString + " launched with the following args:\n";
for (String arg : args) {
@@ -135,22 +137,28 @@ public class HistogramLogProcessor extends Thread {
final String validArgs =
"\"[-csv] [-v] [-i inputFileName] [-o outputFileName] [-tag tag] " +
"[-start rangeStartTimeSec] [-end rangeEndTimeSec] " +
- "[-outputValueUnitRatio r] [-listtags]";
+ "[-outputValueUnitRatio r] [-correctLogWithKnownCoordinatedOmission i] [-listtags]";
System.err.println("valid arguments = " + validArgs);
System.err.println(
- " [-h] help\n" +
- " [-v] Provide verbose error output\n" +
- " [-csv] Use CSV format for output log files\n" +
- " [-i logFileName] File name of Histogram Log to process (default is standard input)\n" +
- " [-o outputFileName] File name to output to (default is standard output)\n" +
- " [-tag tag] The tag (default no tag) of the histogram lines to be processed\n" +
- " [-start rangeStartTimeSec] The start time for the range in the file, in seconds (default 0.0)\n" +
- " [-end rangeEndTimeSec] The end time for the range in the file, in seconds (default is infinite)\n" +
- " [-outputValueUnitRatio r] The scaling factor by which to divide histogram recorded values units\n" +
- " in output. [default = 1000000.0 (1 msec in nsec)]\n" +
- " [-listtags] list all tags found on histogram lines the input file."
+ " [-h] help\n" +
+ " [-v] Provide verbose error output\n" +
+ " [-csv] Use CSV format for output log files\n" +
+ " [-i logFileName] File name of Histogram Log to process (default is standard input)\n" +
+ " [-o outputFileName] File name to output to (default is standard output)\n" +
+ " [-tag tag] The tag (default no tag) of the histogram lines to be processed\n" +
+ " [-start rangeStartTimeSec] The start time for the range in the file, in seconds (default 0.0)\n" +
+ " [-end rangeEndTimeSec] The end time for the range in the file, in seconds (default is infinite)\n" +
+ " [-outputValueUnitRatio r] The scaling factor by which to divide histogram recorded values units\n" +
+ " in output. [default = 1000000.0 (1 msec in nsec)]\n" +
+ " [-correctLogWithKnownCoordinatedOmission i] When the supplied expected interval i is than 0, performs coordinated\n" +
+ " omission corection on the input log's interval histograms by adding\n" +
+ " missing values as appropriate based on the supplied expected interval\n" +
+ " value i (in wahtever units the log histograms were recorded with). This\n" +
+ " feature should only be used when the input log is known to have been\n" +
+ " recorded with coordinated ommisions, and when an expected interval is known.\n" +
+ " [-listtags] list all tags found on histogram lines the input file."
);
System.exit(1);
}
@@ -172,12 +180,33 @@ public class HistogramLogProcessor extends Thread {
startTime, (new Date((long) (startTime * 1000))).toString());
}
- int lineNumber = 0;
+ EncodableHistogram copyCorrectedForCoordinatedOmission(final EncodableHistogram inputHistogram) {
+ EncodableHistogram histogram = inputHistogram;
+ if (histogram instanceof DoubleHistogram) {
+ if (config.expectedIntervalForCoordinatedOmissionCorrection > 0.0) {
+ histogram = ((DoubleHistogram) histogram).copyCorrectedForCoordinatedOmission(
+ config.expectedIntervalForCoordinatedOmissionCorrection);
+ }
+ } else if (histogram instanceof Histogram) {
+ long expectedInterval = (long) config.expectedIntervalForCoordinatedOmissionCorrection;
+ if (expectedInterval > 0) {
+ histogram = ((Histogram) histogram).copyCorrectedForCoordinatedOmission(expectedInterval);
+ }
+ }
+ return histogram;
+ }
+
+ private int lineNumber = 0;
private EncodableHistogram getIntervalHistogram() {
EncodableHistogram histogram = null;
try {
histogram = logReader.nextIntervalHistogram(config.rangeStartTimeSec, config.rangeEndTimeSec);
+ if (config.expectedIntervalForCoordinatedOmissionCorrection > 0.0) {
+ // Apply Coordinated Omission correction to log histograms when arguments indicate that
+ // such correction is desired, and an expected interval is provided.
+ histogram = copyCorrectedForCoordinatedOmission(histogram);
+ }
} catch (RuntimeException ex) {
System.err.println("Log file parsing error at line number " + lineNumber +
": line appears to be malformed.");
@@ -213,14 +242,11 @@ public class HistogramLogProcessor extends Thread {
PrintStream timeIntervalLog = null;
PrintStream movingWindowLog = null;
PrintStream histogramPercentileLog = System.out;
- Double firstStartTime = 0.0;
+ double firstStartTime = 0.0;
boolean timeIntervalLogLegendWritten = false;
boolean movingWindowLogLegendWritten = false;
-// EncodableHistogram[] movingWindow = new EncodableHistogram[config.movingWindowIntervalCount];
- EncodableHistogram movingWindowSumHistogram = null;
Queue<EncodableHistogram> movingWindowQueue = new LinkedList<EncodableHistogram>();
- int movingWindowIndex = 0;
if (config.listTags) {
Set<String> tags = new TreeSet<String>();
@@ -283,35 +309,36 @@ public class HistogramLogProcessor extends Thread {
}
EncodableHistogram intervalHistogram = getIntervalHistogram(config.tag);
+ boolean logUsesDoubleHistograms = (intervalHistogram instanceof DoubleHistogram);
- Histogram accumulatedRegularHistogram = null;
- DoubleHistogram accumulatedDoubleHistogram = null;
+ Histogram accumulatedRegularHistogram = logUsesDoubleHistograms ?
+ new Histogram(3) :
+ ((Histogram) intervalHistogram).copy();
+ accumulatedRegularHistogram.reset();
+ accumulatedRegularHistogram.setAutoResize(true);
+
+ DoubleHistogram accumulatedDoubleHistogram = logUsesDoubleHistograms ?
+ ((DoubleHistogram) intervalHistogram).copy() :
+ new DoubleHistogram(3);
+ accumulatedDoubleHistogram.reset();
+ accumulatedDoubleHistogram.setAutoResize(true);
+
+
+ EncodableHistogram movingWindowSumHistogram = logUsesDoubleHistograms ?
+ new DoubleHistogram(3) :
+ new Histogram(3);
- if (intervalHistogram != null) {
- // Shape the accumulated histogram like the histograms in the log file (but clear their contents):
- if (intervalHistogram instanceof DoubleHistogram) {
- accumulatedDoubleHistogram = ((DoubleHistogram) intervalHistogram).copy();
- accumulatedDoubleHistogram.reset();
- accumulatedDoubleHistogram.setAutoResize(true);
- movingWindowSumHistogram = new DoubleHistogram(3);
- } else {
- accumulatedRegularHistogram = ((Histogram) intervalHistogram).copy();
- accumulatedRegularHistogram.reset();
- accumulatedRegularHistogram.setAutoResize(true);
- movingWindowSumHistogram = new Histogram(3);
- }
- }
while (intervalHistogram != null) {
// handle accumulated histogram:
if (intervalHistogram instanceof DoubleHistogram) {
- if (accumulatedDoubleHistogram == null) {
+ if (!logUsesDoubleHistograms) {
throw new IllegalStateException("Encountered a DoubleHistogram line in a log of Histograms.");
}
accumulatedDoubleHistogram.add((DoubleHistogram) intervalHistogram);
} else {
- if (accumulatedRegularHistogram == null) {
+ if (logUsesDoubleHistograms) {
throw new IllegalStateException("Encountered a Histogram line in a log of DoubleHistograms.");
}
accumulatedRegularHistogram.add((Histogram) intervalHistogram);
@@ -366,7 +393,7 @@ public class HistogramLogProcessor extends Thread {
}
}
- if (intervalHistogram instanceof DoubleHistogram) {
+ if (logUsesDoubleHistograms) {
timeIntervalLog.format(Locale.US, logFormat,
((intervalHistogram.getEndTimeStamp() / 1000.0) - logReader.getStartTimeSec()),
// values recorded during the last reporting interval
@@ -420,7 +447,7 @@ public class HistogramLogProcessor extends Thread {
((DoubleHistogram) movingWindowSumHistogram).getTotalCount(),
((DoubleHistogram) movingWindowSumHistogram).getValueAtPercentile(config.movingWindowPercentileToReport) / config.outputValueUnitRatio,
((DoubleHistogram) movingWindowSumHistogram).getMaxValue() / config.outputValueUnitRatio
- );
+ );
} else {
movingWindowLog.format(Locale.US, movingWindowLogFormat,
((intervalHistogram.getEndTimeStamp() / 1000.0) - logReader.getStartTimeSec()),
@@ -428,7 +455,7 @@ public class HistogramLogProcessor extends Thread {
((Histogram) movingWindowSumHistogram).getTotalCount(),
((Histogram) movingWindowSumHistogram).getValueAtPercentile(config.movingWindowPercentileToReport) / config.outputValueUnitRatio,
((Histogram) movingWindowSumHistogram).getMaxValue() / config.outputValueUnitRatio
- );
+ );
}
}
@@ -436,21 +463,21 @@ public class HistogramLogProcessor extends Thread {
intervalHistogram = getIntervalHistogram(config.tag);
}
- if (accumulatedDoubleHistogram != null) {
+ if (logUsesDoubleHistograms) {
accumulatedDoubleHistogram.outputPercentileDistribution(histogramPercentileLog,
config.percentilesOutputTicksPerHalf, config.outputValueUnitRatio, config.logFormatCsv);
} else {
- if (accumulatedRegularHistogram == null) {
- // If there were no histograms in the log file, we still need an empty histogram for the
- // one line output (shape/range doesn't matter because it is empty):
- accumulatedRegularHistogram = new Histogram(1000000L, 2);
- }
accumulatedRegularHistogram.outputPercentileDistribution(histogramPercentileLog,
config.percentilesOutputTicksPerHalf, config.outputValueUnitRatio, config.logFormatCsv);
}
} finally {
- if (config.outputFileName != null) {
+ if (timeIntervalLog != null) {
timeIntervalLog.close();
+ }
+ if (movingWindowLog != null) {
+ movingWindowLog.close();
+ }
+ if (histogramPercentileLog != System.out) {
histogramPercentileLog.close();
}
}
@@ -460,15 +487,22 @@ public class HistogramLogProcessor extends Thread {
* Construct a {@link org.HdrHistogram.HistogramLogProcessor} with the given arguments
* (provided in command line style).
* <pre>
- * [-h] help
- * [-csv] Use CSV format for output log files
- * [-i logFileName] File name of Histogram Log to process (default is standard input)
- * [-o outputFileName] File name to output to (default is standard output)
- * (will replace occurrences of %pid and %date with appropriate information)
- * [-start rangeStartTimeSec] The start time for the range in the file, in seconds (default 0.0)
- * [-end rangeEndTimeSec] The end time for the range in the file, in seconds (default is infinite)
- * [-outputValueUnitRatio r] The scaling factor by which to divide histogram recorded values units
- * in output. [default = 1000000.0 (1 msec in nsec)]"
+ * [-h] help
+ * [-csv] Use CSV format for output log files
+ * [-i logFileName] File name of Histogram Log to process (default is standard input)
+ * [-o outputFileName] File name to output to (default is standard output)
+ * (will replace occurrences of %pid and %date with appropriate information)
+ * [-tag tag] The tag (default no tag) of the histogram lines to be processed\n
+ * [-start rangeStartTimeSec] The start time for the range in the file, in seconds (default 0.0)
+ * [-end rangeEndTimeSec] The end time for the range in the file, in seconds (default is infinite)
+ * [-correctLogWithKnownCoordinatedOmission expectedInterval] When the supplied expected interval i is than 0, performs coordinated
+ * omission corection on the input log's interval histograms by adding
+ * missing values as appropriate based on the supplied expected interval
+ * value i (in wahtever units the log histograms were recorded with). This
+ * feature should only be used when the input log is known to have been
+ * recorded with coordinated ommisions, and when an expected interval is known.
+ * [-outputValueUnitRatio r] The scaling factor by which to divide histogram recorded values units
+ * in output. [default = 1000000.0 (1 msec in nsec)]"
* </pre>
* @param args command line arguments
* @throws FileNotFoundException if specified input file is not found
=====================================
src/main/java/org/HdrHistogram/HistogramLogReader.java
=====================================
@@ -7,12 +7,7 @@
package org.HdrHistogram;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.InputStream;
-import java.nio.ByteBuffer;
-import java.util.Locale;
-import java.util.Scanner;
+import java.io.*;
import java.util.zip.DataFormatException;
/**
@@ -56,13 +51,117 @@ import java.util.zip.DataFormatException;
* that may be added to timestamps in the file to determine an absolute
* timestamp (e.g. since the epoch) for each interval.
*/
-public class HistogramLogReader {
+public class HistogramLogReader implements Closeable {
+
+ private final HistogramLogScanner scanner;
+ private final HistogramLogScanner.EventHandler handler = new HistogramLogScanner.EventHandler()
+ {
+ @Override
+ public boolean onComment(String comment)
+ {
+ return false;
+ }
+
+ @Override
+ public boolean onBaseTime(double secondsSinceEpoch)
+ {
+ baseTimeSec = secondsSinceEpoch; // base time represented as seconds since epoch
+ observedBaseTime = true;
+ return false;
+ }
+
+ @Override
+ public boolean onStartTime(double secondsSinceEpoch)
+ {
+ startTimeSec = secondsSinceEpoch; // start time represented as seconds since epoch
+ observedStartTime = true;
+ return false;
+ }
+
+ @Override
+ public boolean onHistogram(String tag, double timestamp, double length,
+ HistogramLogScanner.EncodableHistogramSupplier lazyReader) {
+ final double logTimeStampInSec = timestamp; // Timestamp is expected to be in seconds
+
+ if (!observedStartTime) {
+ // No explicit start time noted. Use 1st observed time:
+ startTimeSec = logTimeStampInSec;
+ observedStartTime = true;
+ }
+ if (!observedBaseTime) {
+ // No explicit base time noted. Deduce from 1st observed time (compared to start time):
+ if (logTimeStampInSec < startTimeSec - (365 * 24 * 3600.0)) {
+ // Criteria Note: if log timestamp is more than a year in the past (compared to
+ // StartTime), we assume that timestamps in the log are not absolute
+ baseTimeSec = startTimeSec;
+ } else {
+ // Timestamps are absolute
+ baseTimeSec = 0.0;
+ }
+ observedBaseTime = true;
+ }
+
+ final double absoluteStartTimeStampSec = logTimeStampInSec + baseTimeSec;
+ final double offsetStartTimeStampSec = absoluteStartTimeStampSec - startTimeSec;
+
+ final double intervalLengthSec = length; // Timestamp length is expect to be in seconds
+ final double absoluteEndTimeStampSec = absoluteStartTimeStampSec + intervalLengthSec;
+
+ final double startTimeStampToCheckRangeOn = absolute ? absoluteStartTimeStampSec : offsetStartTimeStampSec;
- private final Scanner scanner;
+ if (startTimeStampToCheckRangeOn < rangeStartTimeSec) {
+ // keep on trucking
+ return false;
+ }
+
+ if (startTimeStampToCheckRangeOn > rangeEndTimeSec) {
+ // after limit we stop on each line
+ return true;
+ }
+ EncodableHistogram histogram;
+ try
+ {
+ histogram = lazyReader.read();
+ }
+ catch (DataFormatException e)
+ {
+ // stop after exception
+ return true;
+ }
+
+ histogram.setStartTimeStamp((long) (absoluteStartTimeStampSec * 1000.0));
+ histogram.setEndTimeStamp((long) (absoluteEndTimeStampSec * 1000.0));
+ histogram.setTag(tag);
+ nextHistogram = histogram;
+ return true;
+ }
+
+ @Override
+ public boolean onException(Throwable t) {
+
+ // We ignore NoSuchElementException, but stop processing.
+ // Next call to nextIntervalHistogram may return null.
+ if (t instanceof java.util.NoSuchElementException){
+ return true;
+ }
+ // rethrow
+ if (t instanceof RuntimeException)
+ throw (RuntimeException)t;
+ else
+ throw new RuntimeException(t);
+ }
+ };
+
private double startTimeSec = 0.0;
private boolean observedStartTime = false;
private double baseTimeSec = 0.0;
private boolean observedBaseTime = false;
+
+ // scanner handling state
+ private boolean absolute;
+ private double rangeStartTimeSec;
+ private double rangeEndTimeSec;
+ private EncodableHistogram nextHistogram;
/**
* Constructs a new HistogramLogReader that produces intervals read from the specified file name.
@@ -70,8 +169,7 @@ public class HistogramLogReader {
* @throws java.io.FileNotFoundException when unable to find inputFileName
*/
public HistogramLogReader(final String inputFileName) throws FileNotFoundException {
- scanner = new Scanner(new File(inputFileName));
- initScanner();
+ scanner = new HistogramLogScanner(new File(inputFileName));
}
/**
@@ -79,8 +177,7 @@ public class HistogramLogReader {
* @param inputStream The InputStream to read from
*/
public HistogramLogReader(final InputStream inputStream) {
- scanner = new Scanner(inputStream);
- initScanner();
+ scanner = new HistogramLogScanner(inputStream);
}
/**
@@ -89,14 +186,7 @@ public class HistogramLogReader {
* @throws java.io.FileNotFoundException when unable to find inputFile
*/
public HistogramLogReader(final File inputFile) throws FileNotFoundException {
- scanner = new Scanner(inputFile);
- initScanner();
- }
-
-
- private void initScanner() {
- scanner.useLocale(Locale.US);
- scanner.useDelimiter("[, \\r\\n]");
+ scanner = new HistogramLogScanner(inputFile);
}
/**
@@ -195,105 +285,26 @@ public class HistogramLogReader {
private EncodableHistogram nextIntervalHistogram(final double rangeStartTimeSec,
final double rangeEndTimeSec, boolean absolute) {
- while (scanner.hasNextLine()) {
- try {
- if (scanner.hasNext("\\#.*")) {
- // comment line.
- // Look for explicit start time or base time notes in comments:
- if (scanner.hasNext("#\\[StartTime:")) {
- scanner.next("#\\[StartTime:");
- if (scanner.hasNextDouble()) {
- startTimeSec = scanner.nextDouble(); // start time represented as seconds since epoch
- observedStartTime = true;
- }
- } else if (scanner.hasNext("#\\[BaseTime:")) {
- scanner.next("#\\[BaseTime:");
- if (scanner.hasNextDouble()) {
- baseTimeSec = scanner.nextDouble(); // base time represented as seconds since epoch
- observedBaseTime = true;
- }
- }
- continue;
- }
-
- if (scanner.hasNext("\"StartTimestamp\".*")) {
- // Legend line
- continue;
- }
-
- String tagString = null;
- if (scanner.hasNext("Tag\\=.*")) {
- tagString = scanner.next("Tag\\=.*").substring(4);
- }
-
- // Decode: startTimestamp, intervalLength, maxTime, histogramPayload
-
- final double logTimeStampInSec = scanner.nextDouble(); // Timestamp is expected to be in seconds
-
- if (!observedStartTime) {
- // No explicit start time noted. Use 1st observed time:
- startTimeSec = logTimeStampInSec;
- observedStartTime = true;
- }
- if (!observedBaseTime) {
- // No explicit base time noted. Deduce from 1st observed time (compared to start time):
- if (logTimeStampInSec < startTimeSec - (365 * 24 * 3600.0)) {
- // Criteria Note: if log timestamp is more than a year in the past (compared to
- // StartTime), we assume that timestamps in the log are not absolute
- baseTimeSec = startTimeSec;
- } else {
- // Timestamps are absolute
- baseTimeSec = 0.0;
- }
- observedBaseTime = true;
- }
-
- final double absoluteStartTimeStampSec = logTimeStampInSec + baseTimeSec;
- final double offsetStartTimeStampSec = absoluteStartTimeStampSec - startTimeSec;
-
- final double intervalLengthSec = scanner.nextDouble(); // Timestamp length is expect to be in seconds
- final double absoluteEndTimeStampSec = absoluteStartTimeStampSec + intervalLengthSec;
-
- final double startTimeStampToCheckRangeOn = absolute ? absoluteStartTimeStampSec : offsetStartTimeStampSec;
-
- if (startTimeStampToCheckRangeOn < rangeStartTimeSec) {
- continue;
- }
-
- if (startTimeStampToCheckRangeOn > rangeEndTimeSec) {
- return null;
- }
-
- scanner.nextDouble(); // Skip maxTime field, as max time can be deduced from the histogram.
- final String compressedPayloadString = scanner.next();
- final ByteBuffer buffer = ByteBuffer.wrap(
- Base64Helper.parseBase64Binary(compressedPayloadString));
-
- EncodableHistogram histogram = EncodableHistogram.decodeFromCompressedByteBuffer(buffer, 0);
-
- histogram.setStartTimeStamp((long) (absoluteStartTimeStampSec * 1000.0));
- histogram.setEndTimeStamp((long) (absoluteEndTimeStampSec * 1000.0));
- histogram.setTag(tagString);
-
- return histogram;
-
- } catch (java.util.NoSuchElementException ex) {
- return null;
- } catch (DataFormatException ex) {
- return null;
- } finally {
- scanner.nextLine(); // Move to next line.
- }
- }
- return null;
+ this.rangeStartTimeSec = rangeStartTimeSec;
+ this.rangeEndTimeSec = rangeEndTimeSec;
+ this.absolute = absolute;
+ scanner.process(handler);
+ EncodableHistogram histogram = this.nextHistogram;
+ nextHistogram = null;
+ return histogram;
}
/**
- * Indicates whther or not additional intervals may exist in the log
- * @return ture if additional intervals may exist in the log
+ * Indicates whether or not additional intervals may exist in the log
+ * @return true if additional intervals may exist in the log
*/
public boolean hasNext() {
return scanner.hasNextLine();
}
+ @Override
+ public void close()
+ {
+ scanner.close();
+ }
}
=====================================
src/main/java/org/HdrHistogram/HistogramLogScanner.java
=====================================
@@ -0,0 +1,198 @@
+/**
+ * Written by Gil Tene of Azul Systems, and released to the public domain,
+ * as explained at http://creativecommons.org/publicdomain/zero/1.0/
+ *
+ * @author Gil Tene
+ */
+
+package org.HdrHistogram;
+
+import java.io.*;
+import java.nio.ByteBuffer;
+import java.util.Locale;
+import java.util.Scanner;
+import java.util.zip.DataFormatException;
+
+public class HistogramLogScanner implements Closeable {
+
+ // can't use lambdas, and anyway we need to let the handler take the exception
+ public interface EncodableHistogramSupplier
+ {
+ EncodableHistogram read() throws DataFormatException;
+ }
+
+ /**
+ * Handles log events, return true to stop processing.
+ */
+ public interface EventHandler
+ {
+ boolean onComment(String comment);
+ boolean onBaseTime(double secondsSinceEpoch);
+ boolean onStartTime(double secondsSinceEpoch);
+
+ /**
+ * A lazy reader is provided to allow fast skipping of bulk of work where tag or timestamp are to be used as
+ * a basis for filtering the {@link EncodableHistogram} anyway. The reader is to be called only once.
+ *
+ * @param tag histogram tag or null if none exist
+ * @param timestamp logged timestamp
+ * @param length logged interval length
+ * @param lazyReader to be called if the histogram needs to be deserialized, given the tag/timestamp etc.
+ * @return
+ */
+ boolean onHistogram(String tag, double timestamp, double length, EncodableHistogramSupplier lazyReader);
+ boolean onException(Throwable t);
+ }
+
+ private static class LazyHistogramReader implements EncodableHistogramSupplier {
+
+ private final Scanner scanner;
+ private boolean gotIt = true;
+
+ private LazyHistogramReader(Scanner scanner)
+ {
+ this.scanner = scanner;
+ }
+
+ private void allowGet()
+ {
+ gotIt = false;
+ }
+
+ @Override
+ public EncodableHistogram read() throws DataFormatException
+ {
+ // prevent double calls to this method
+ if (gotIt)
+ throw new IllegalStateException();
+ gotIt = true;
+
+ final String compressedPayloadString = scanner.next();
+ final ByteBuffer buffer = ByteBuffer.wrap(Base64Helper.parseBase64Binary(compressedPayloadString));
+
+ EncodableHistogram histogram = EncodableHistogram.decodeFromCompressedByteBuffer(buffer, 0);
+
+ return histogram;
+ }
+ }
+
+ private final LazyHistogramReader lazyReader;
+ protected final Scanner scanner;
+
+ /**
+ * Constructs a new HistogramLogReader that produces intervals read from the specified file name.
+ * @param inputFileName The name of the file to read from
+ * @throws java.io.FileNotFoundException when unable to find inputFileName
+ */
+ public HistogramLogScanner(final String inputFileName) throws FileNotFoundException {
+ this(new Scanner(new File(inputFileName)));
+ }
+
+ /**
+ * Constructs a new HistogramLogReader that produces intervals read from the specified InputStream. Note that
+ * log readers constructed through this constructor do not assume ownership of stream and will not close it on
+ * {@link #close()}.
+ *
+ * @param inputStream The InputStream to read from
+ */
+ public HistogramLogScanner(final InputStream inputStream) {
+ this(new Scanner(inputStream));
+ }
+
+ /**
+ * Constructs a new HistogramLogReader that produces intervals read from the specified file.
+ * @param inputFile The File to read from
+ * @throws java.io.FileNotFoundException when unable to find inputFile
+ */
+ public HistogramLogScanner(final File inputFile) throws FileNotFoundException {
+ this(new Scanner(inputFile));
+ }
+
+ private HistogramLogScanner(Scanner scanner)
+ {
+ this.scanner = scanner;
+ this.lazyReader = new LazyHistogramReader(scanner);
+ initScanner();
+ }
+
+ private void initScanner() {
+ scanner.useLocale(Locale.US);
+ scanner.useDelimiter("[ ,\\r\\n]");
+ }
+
+ /**
+ * Close underlying scanner.
+ */
+ @Override
+ public void close()
+ {
+ scanner.close();
+ }
+
+ public void process(EventHandler handler) {
+ while (scanner.hasNextLine()) {
+ try {
+ if (scanner.hasNext("\\#.*")) {
+ // comment line.
+ // Look for explicit start time or base time notes in comments:
+ if (scanner.hasNext("#\\[StartTime:")) {
+ scanner.next("#\\[StartTime:");
+ if (scanner.hasNextDouble()) {
+ double startTimeSec = scanner.nextDouble(); // start time represented as seconds since epoch
+ if (handler.onStartTime(startTimeSec)) {
+ return;
+ }
+ }
+ } else if (scanner.hasNext("#\\[BaseTime:")) {
+ scanner.next("#\\[BaseTime:");
+ if (scanner.hasNextDouble()) {
+ double baseTimeSec = scanner.nextDouble(); // base time represented as seconds since epoch
+ if (handler.onBaseTime(baseTimeSec))
+ {
+ return;
+ }
+ }
+ } else if (handler.onComment(scanner.next("\\#.*"))) {
+ return;
+ }
+ continue;
+ }
+
+ if (scanner.hasNext("\"StartTimestamp\".*")) {
+ // Legend line
+ continue;
+ }
+
+ String tagString = null;
+ if (scanner.hasNext("Tag\\=.*")) {
+ tagString = scanner.next("Tag\\=.*").substring(4);
+ }
+
+ // Decode: startTimestamp, intervalLength, maxTime, histogramPayload
+ final double logTimeStampInSec = scanner.nextDouble(); // Timestamp is expected to be in seconds
+ final double intervalLengthSec = scanner.nextDouble(); // Timestamp length is expect to be in seconds
+ scanner.nextDouble(); // Skip maxTime field, as max time can be deduced from the histogram.
+
+ lazyReader.allowGet();
+ if (handler.onHistogram(tagString, logTimeStampInSec, intervalLengthSec, lazyReader))
+ return;
+
+ } catch (Throwable ex) {
+ if (handler.onException(ex))
+ return;
+ } finally {
+ scanner.nextLine(); // Move to next line.
+ }
+ }
+ return;
+ }
+
+ /**
+ * Indicates whether or not additional intervals may exist in the log
+ *
+ * @return true if additional intervals may exist in the log
+ */
+ public boolean hasNextLine() {
+ return scanner.hasNextLine();
+ }
+}
=====================================
src/main/java/org/HdrHistogram/IntCountsHistogram.java
=====================================
@@ -81,6 +81,12 @@ public class IntCountsHistogram extends AbstractHistogram {
this.normalizingIndexOffset = normalizingIndexOffset;
}
+
+ @Override
+ void setIntegerToDoubleValueConversionRatio(double integerToDoubleValueConversionRatio) {
+ nonConcurrentSetIntegerToDoubleValueConversionRatio(integerToDoubleValueConversionRatio);
+ }
+
@Override
void shiftNormalizingIndexByOffset(int offsetToAdd,
boolean lowestHalfBucketPopulated,
=====================================
src/main/java/org/HdrHistogram/LinearIterator.java
=====================================
@@ -48,11 +48,14 @@ public class LinearIterator extends AbstractHistogramIterator implements Iterato
if (super.hasNext()) {
return true;
}
- // If the next iterate will not move to the next sub bucket index (which is empty if
+ // If the next iteration will not move to the next sub bucket index (which is empty if
// if we reached this point), then we are not yet done iterating (we want to iterate
// until we are no longer on a value that has a count, rather than util we first reach
// the last value that has a count. The difference is subtle but important)...
- return (currentStepHighestValueReportingLevel + 1 < nextValueAtIndex);
+ // When this is called, we're about to begin the "next" iteration, so
+ // currentStepHighestValueReportingLevel has already been incremented, and we use it
+ // without incrementing its value.
+ return (currentStepHighestValueReportingLevel < nextValueAtIndex);
}
@Override
=====================================
src/main/java/org/HdrHistogram/Recorder.java
=====================================
@@ -22,10 +22,24 @@ import java.util.concurrent.atomic.AtomicLong;
* {@link Recorder#recordValueWithExpectedInterval} calls.
* Recording calls are wait-free on architectures that support atomic increment operations, and
* are lock-free on architectures that do not.
+ * <p>
+ * A common pattern for using a {@link Recorder} looks like this:
+ * <br><pre><code>
+ * Recorder recorder = new Recorder(2); // Two decimal point accuracy
+ * Histogram intervalHistogram = null;
+ * ...
+ * [start of some loop construct that periodically wants to grab an interval histogram]
+ * ...
+ * // Get interval histogram, recycling previous interval histogram:
+ * intervalHistogram = recorder.getIntervalHistogram(intervalHistogram);
+ * histogramLogWriter.outputIntervalHistogram(intervalHistogram);
+ * ...
+ * [end of loop construct]
+ * </code></pre>
*
*/
-public class Recorder {
+public class Recorder implements ValueRecorder {
private static AtomicLong instanceIdSequencer = new AtomicLong(1);
private final long instanceId = instanceIdSequencer.getAndIncrement();
@@ -93,6 +107,7 @@ public class Recorder {
* @param value the value to record
* @throws ArrayIndexOutOfBoundsException (may throw) if value is exceeds highestTrackableValue
*/
+ @Override
public void recordValue(final long value) throws ArrayIndexOutOfBoundsException {
long criticalValueAtEnter = recordingPhaser.writerCriticalSectionEnter();
try {
@@ -109,6 +124,7 @@ public class Recorder {
* @param count The number of occurrences of this value to record
* @throws ArrayIndexOutOfBoundsException (may throw) if value is exceeds highestTrackableValue
*/
+ @Override
public void recordValueWithCount(final long value, final long count) throws ArrayIndexOutOfBoundsException {
long criticalValueAtEnter = recordingPhaser.writerCriticalSectionEnter();
try {
@@ -134,6 +150,7 @@ public class Recorder {
* than expectedIntervalBetweenValueSamples
* @throws ArrayIndexOutOfBoundsException (may throw) if value is exceeds highestTrackableValue
*/
+ @Override
public void recordValueWithExpectedInterval(final long value, final long expectedIntervalBetweenValueSamples)
throws ArrayIndexOutOfBoundsException {
long criticalValueAtEnter = recordingPhaser.writerCriticalSectionEnter();
@@ -180,13 +197,49 @@ public class Recorder {
* getIntervalHistogram(histogramToRecycle)} will reset the value counts, and start accumulating value
* counts for the next interval
*
- * @param histogramToRecycle a previously returned interval histogram that may be recycled to avoid allocation and
+ * @param histogramToRecycle a previously returned interval histogram (from this instance of
+ * {@link Recorder}) that may be recycled to avoid allocation and
* copy operations.
* @return a histogram containing the value counts accumulated since the last interval histogram was taken.
*/
public synchronized Histogram getIntervalHistogram(Histogram histogramToRecycle) {
+ return getIntervalHistogram(histogramToRecycle, true);
+ }
+
+ /**
+ * Get an interval histogram, which will include a stable, consistent view of all value counts
+ * accumulated since the last interval histogram was taken.
+ * <p>
+ * {@link Recorder#getIntervalHistogram(Histogram histogramToRecycle)
+ * getIntervalHistogram(histogramToRecycle)}
+ * accepts a previously returned interval histogram that can be recycled internally to avoid allocation
+ * and content copying operations, and is therefore significantly more efficient for repeated use than
+ * {@link Recorder#getIntervalHistogram()} and
+ * {@link Recorder#getIntervalHistogramInto getIntervalHistogramInto()}. The provided
+ * {@code histogramToRecycle} must
+ * be either be null or an interval histogram returned by a previous call to
+ * {@link Recorder#getIntervalHistogram(Histogram histogramToRecycle)
+ * getIntervalHistogram(histogramToRecycle)} or
+ * {@link Recorder#getIntervalHistogram()}.
+ * <p>
+ * NOTE: The caller is responsible for not recycling the same returned interval histogram more than once. If
+ * the same interval histogram instance is recycled more than once, behavior is undefined.
+ * <p>
+ * Calling {@link Recorder#getIntervalHistogram(Histogram histogramToRecycle)
+ * getIntervalHistogram(histogramToRecycle)} will reset the value counts, and start accumulating value
+ * counts for the next interval
+ *
+ * @param histogramToRecycle a previously returned interval histogram that may be recycled to avoid allocation and
+ * copy operations.
+ * @param enforeContainingInstance if true, will only allow recycling of histograms previously returned from this
+ * instance of {@link Recorder}. If false, will allow recycling histograms
+ * previously returned by other instances of {@link Recorder}.
+ * @return a histogram containing the value counts accumulated since the last interval histogram was taken.
+ */
+ public synchronized Histogram getIntervalHistogram(Histogram histogramToRecycle,
+ boolean enforeContainingInstance) {
// Verify that replacement histogram can validly be used as an inactive histogram replacement:
- validateFitAsReplacementHistogram(histogramToRecycle);
+ validateFitAsReplacementHistogram(histogramToRecycle, enforeContainingInstance);
inactiveHistogram = histogramToRecycle;
performIntervalSample();
Histogram sampledHistogram = inactiveHistogram;
@@ -211,6 +264,7 @@ public class Recorder {
/**
* Reset any value counts accumulated thus far.
*/
+ @Override
public synchronized void reset() {
// the currently inactive histogram is reset each time we flip. So flipping twice resets both:
performIntervalSample();
@@ -278,30 +332,34 @@ public class Recorder {
}
}
- void validateFitAsReplacementHistogram(Histogram replacementHistogram) {
+ private void validateFitAsReplacementHistogram(Histogram replacementHistogram,
+ boolean enforeContainingInstance) {
boolean bad = true;
if (replacementHistogram == null) {
bad = false;
} else if (replacementHistogram instanceof InternalAtomicHistogram) {
if ((activeHistogram instanceof InternalAtomicHistogram)
&&
- (((InternalAtomicHistogram)replacementHistogram).containingInstanceId ==
- ((InternalAtomicHistogram)activeHistogram).containingInstanceId)
- ) {
+ ((!enforeContainingInstance) ||
+ (((InternalAtomicHistogram)replacementHistogram).containingInstanceId ==
+ ((InternalAtomicHistogram)activeHistogram).containingInstanceId)
+ )) {
bad = false;
}
} else if (replacementHistogram instanceof InternalConcurrentHistogram) {
if ((activeHistogram instanceof InternalConcurrentHistogram)
&&
- (((InternalConcurrentHistogram)replacementHistogram).containingInstanceId ==
- ((InternalConcurrentHistogram)activeHistogram).containingInstanceId)
- ) {
+ ((!enforeContainingInstance) ||
+ (((InternalConcurrentHistogram)replacementHistogram).containingInstanceId ==
+ ((InternalConcurrentHistogram)activeHistogram).containingInstanceId)
+ )) {
bad = false;
}
}
if (bad) {
throw new IllegalArgumentException("replacement histogram must have been obtained via a previous" +
- " getIntervalHistogram() call from this " + this.getClass().getName() +" instance");
+ " getIntervalHistogram() call from this " + this.getClass().getName() +
+ (enforeContainingInstance ? " insatnce" : " class"));
}
}
}
=====================================
src/main/java/org/HdrHistogram/ShortCountsHistogram.java
=====================================
@@ -80,6 +80,11 @@ public class ShortCountsHistogram extends AbstractHistogram {
this.normalizingIndexOffset = normalizingIndexOffset;
}
+ @Override
+ void setIntegerToDoubleValueConversionRatio(double integerToDoubleValueConversionRatio) {
+ nonConcurrentSetIntegerToDoubleValueConversionRatio(integerToDoubleValueConversionRatio);
+ }
+
@Override
void shiftNormalizingIndexByOffset(int offsetToAdd,
boolean lowestHalfBucketPopulated,
=====================================
src/main/java/org/HdrHistogram/SingleWriterDoubleRecorder.java
=====================================
@@ -20,7 +20,20 @@ import java.util.concurrent.atomic.AtomicLong;
* call {@link SingleWriterDoubleRecorder#recordValue} or
* {@link SingleWriterDoubleRecorder#recordValueWithExpectedInterval} at any point in time.
* It DOES NOT support concurrent recording calls.
- *
+ * <p>
+ * A common pattern for using a {@link SingleWriterDoubleRecorder} looks like this:
+ * <br><pre><code>
+ * SingleWriterDoubleRecorder recorder = new SingleWriterDoubleRecorder(2); // Two decimal point accuracy
+ * DoubleHistogram intervalHistogram = null;
+ * ...
+ * [start of some loop construct that periodically wants to grab an interval histogram]
+ * ...
+ * // Get interval histogram, recycling previous interval histogram:
+ * intervalHistogram = recorder.getIntervalHistogram(intervalHistogram);
+ * histogramLogWriter.outputIntervalHistogram(intervalHistogram);
+ * ...
+ * [end of loop construct]
+ * </code></pre>
*/
public class SingleWriterDoubleRecorder {
@@ -156,13 +169,50 @@ public class SingleWriterDoubleRecorder {
* getIntervalHistogram(histogramToRecycle)} will reset the value counts, and start accumulating value
* counts for the next interval
*
- * @param histogramToRecycle a previously returned interval histogram that may be recycled to avoid allocation and
+ * @param histogramToRecycle a previously returned interval histogram (from this instance of
+ * {@link SingleWriterDoubleRecorder}) that may be recycled to avoid allocation and
* copy operations.
* @return a histogram containing the value counts accumulated since the last interval histogram was taken.
*/
public synchronized DoubleHistogram getIntervalHistogram(DoubleHistogram histogramToRecycle) {
+ return getIntervalHistogram(histogramToRecycle, true);
+ }
+
+ /**
+ * Get an interval histogram, which will include a stable, consistent view of all value counts
+ * accumulated since the last interval histogram was taken.
+ * <p>
+ * {@link SingleWriterDoubleRecorder#getIntervalHistogram(DoubleHistogram histogramToRecycle)
+ * getIntervalHistogram(histogramToRecycle)}
+ * accepts a previously returned interval histogram that can be recycled internally to avoid allocation
+ * and content copying operations, and is therefore significantly more efficient for repeated use than
+ * {@link SingleWriterDoubleRecorder#getIntervalHistogram()} and
+ * {@link SingleWriterDoubleRecorder#getIntervalHistogramInto getIntervalHistogramInto()}. The
+ * provided {@code histogramToRecycle} must
+ * be either be null or an interval histogram returned by a previous call to
+ * {@link SingleWriterDoubleRecorder#getIntervalHistogram(DoubleHistogram histogramToRecycle)
+ * getIntervalHistogram(histogramToRecycle)} or
+ * {@link SingleWriterDoubleRecorder#getIntervalHistogram()}.
+ * <p>
+ * NOTE: The caller is responsible for not recycling the same returned interval histogram more than once. If
+ * the same interval histogram instance is recycled more than once, behavior is undefined.
+ * <p>
+ * Calling
+ * {@link SingleWriterDoubleRecorder#getIntervalHistogram(DoubleHistogram histogramToRecycle)
+ * getIntervalHistogram(histogramToRecycle)} will reset the value counts, and start accumulating value
+ * counts for the next interval
+ *
+ * @param histogramToRecycle a previously returned interval histogram that may be recycled to avoid allocation and
+ * copy operations.
+ * @param enforeContainingInstance if true, will only allow recycling of histograms previously returned from this
+ * instance of {@link SingleWriterDoubleRecorder}. If false, will allow recycling histograms
+ * previously returned by other instances of {@link SingleWriterDoubleRecorder}.
+ * @return a histogram containing the value counts accumulated since the last interval histogram was taken.
+ */
+ public synchronized DoubleHistogram getIntervalHistogram(DoubleHistogram histogramToRecycle,
+ boolean enforeContainingInstance) {
// Verify that replacement histogram can validly be used as an inactive histogram replacement:
- validateFitAsReplacementHistogram(histogramToRecycle);
+ validateFitAsReplacementHistogram(histogramToRecycle, enforeContainingInstance);
inactiveHistogram = (InternalDoubleHistogram) histogramToRecycle;
performIntervalSample();
DoubleHistogram sampledHistogram = inactiveHistogram;
@@ -244,20 +294,22 @@ public class SingleWriterDoubleRecorder {
}
}
- void validateFitAsReplacementHistogram(DoubleHistogram replacementHistogram) {
+ private void validateFitAsReplacementHistogram(DoubleHistogram replacementHistogram,
+ boolean enforeContainingInstance) {
boolean bad = true;
if (replacementHistogram == null) {
bad = false;
} else if ((replacementHistogram instanceof InternalDoubleHistogram)
&&
- (((InternalDoubleHistogram) replacementHistogram).containingInstanceId ==
+ ((!enforeContainingInstance) ||
+ (((InternalDoubleHistogram) replacementHistogram).containingInstanceId ==
activeHistogram.containingInstanceId)
- ) {
+ )) {
bad = false;
}
if (bad) {
- throw new IllegalArgumentException("replacement histogram must have been obtained via a previous" +
+ throw new IllegalArgumentException("replacement histogram must have been obtained via a previous " +
"getIntervalHistogram() call from this " + this.getClass().getName() +" instance");
}
}
=====================================
src/main/java/org/HdrHistogram/SingleWriterRecorder.java
=====================================
@@ -21,9 +21,23 @@ import java.util.concurrent.atomic.AtomicLong;
* call {@link SingleWriterRecorder#recordValue} or
* {@link SingleWriterRecorder#recordValueWithExpectedInterval} at any point in time.
* It DOES NOT safely support concurrent recording calls.
+ * * <p>
+ * A common pattern for using a {@link SingleWriterRecorder} looks like this:
+ * <br><pre><code>
+ * SingleWriterRecorder recorder = new SingleWriterRecorder(2); // Two decimal point accuracy
+ * Histogram intervalHistogram = null;
+ * ...
+ * [start of some loop construct that periodically wants to grab an interval histogram]
+ * ...
+ * // Get interval histogram, recycling previous interval histogram:
+ * intervalHistogram = recorder.getIntervalHistogram(intervalHistogram);
+ * histogramLogWriter.outputIntervalHistogram(intervalHistogram);
+ * ...
+ * [end of loop construct]
+ * </code></pre>
*/
-public class SingleWriterRecorder {
+public class SingleWriterRecorder implements ValueRecorder {
private static AtomicLong instanceIdSequencer = new AtomicLong(1);
private final long instanceId = instanceIdSequencer.getAndIncrement();
@@ -92,6 +106,7 @@ public class SingleWriterRecorder {
* @param value the value to record
* @throws ArrayIndexOutOfBoundsException (may throw) if value is exceeds highestTrackableValue
*/
+ @Override
public void recordValue(final long value) {
long criticalValueAtEnter = recordingPhaser.writerCriticalSectionEnter();
try {
@@ -108,6 +123,7 @@ public class SingleWriterRecorder {
* @param count The number of occurrences of this value to record
* @throws ArrayIndexOutOfBoundsException (may throw) if value is exceeds highestTrackableValue
*/
+ @Override
public void recordValueWithCount(final long value, final long count) throws ArrayIndexOutOfBoundsException {
long criticalValueAtEnter = recordingPhaser.writerCriticalSectionEnter();
try {
@@ -133,6 +149,7 @@ public class SingleWriterRecorder {
* than expectedIntervalBetweenValueSamples
* @throws ArrayIndexOutOfBoundsException (may throw) if value is exceeds highestTrackableValue
*/
+ @Override
public void recordValueWithExpectedInterval(final long value, final long expectedIntervalBetweenValueSamples)
throws ArrayIndexOutOfBoundsException {
long criticalValueAtEnter = recordingPhaser.writerCriticalSectionEnter();
@@ -179,13 +196,49 @@ public class SingleWriterRecorder {
* getIntervalHistogram(histogramToRecycle)} will reset the value counts, and start accumulating value
* counts for the next interval
*
- * @param histogramToRecycle a previously returned interval histogram that may be recycled to avoid allocation and
+ * @param histogramToRecycle a previously returned interval histogram (from this instance of
+ * {@link SingleWriterRecorder}) that may be recycled to avoid allocation and
* copy operations.
* @return a histogram containing the value counts accumulated since the last interval histogram was taken.
*/
public synchronized Histogram getIntervalHistogram(Histogram histogramToRecycle) {
+ return getIntervalHistogram(histogramToRecycle, true);
+ }
+
+ /**
+ * Get an interval histogram, which will include a stable, consistent view of all value counts
+ * accumulated since the last interval histogram was taken.
+ * <p>
+ * {@link SingleWriterRecorder#getIntervalHistogram(Histogram histogramToRecycle)
+ * getIntervalHistogram(histogramToRecycle)}
+ * accepts a previously returned interval histogram that can be recycled internally to avoid allocation
+ * and content copying operations, and is therefore significantly more efficient for repeated use than
+ * {@link SingleWriterRecorder#getIntervalHistogram()} and
+ * {@link SingleWriterRecorder#getIntervalHistogramInto getIntervalHistogramInto()}. The provided
+ * {@code histogramToRecycle} must
+ * be either be null or an interval histogram returned by a previous call to
+ * {@link SingleWriterRecorder#getIntervalHistogram(Histogram histogramToRecycle)
+ * getIntervalHistogram(histogramToRecycle)} or
+ * {@link SingleWriterRecorder#getIntervalHistogram()}.
+ * <p>
+ * NOTE: The caller is responsible for not recycling the same returned interval histogram more than once. If
+ * the same interval histogram instance is recycled more than once, behavior is undefined.
+ * <p>
+ * Calling {@link SingleWriterRecorder#getIntervalHistogram(Histogram histogramToRecycle)
+ * getIntervalHistogram(histogramToRecycle)} will reset the value counts, and start accumulating value
+ * counts for the next interval
+ *
+ * @param histogramToRecycle a previously returned interval histogram that may be recycled to avoid allocation and
+ * copy operations.
+ * @param enforeContainingInstance if true, will only allow recycling of histograms previously returned from this
+ * instance of {@link SingleWriterRecorder}. If false, will allow recycling histograms
+ * previously returned by other instances of {@link SingleWriterRecorder}.
+ * @return a histogram containing the value counts accumulated since the last interval histogram was taken.
+ */
+ public synchronized Histogram getIntervalHistogram(Histogram histogramToRecycle,
+ boolean enforeContainingInstance) {
// Verify that replacement histogram can validly be used as an inactive histogram replacement:
- validateFitAsReplacementHistogram(histogramToRecycle);
+ validateFitAsReplacementHistogram(histogramToRecycle, enforeContainingInstance);
inactiveHistogram = (InternalHistogram) histogramToRecycle;
performIntervalSample();
Histogram sampledHistogram = inactiveHistogram;
@@ -210,6 +263,7 @@ public class SingleWriterRecorder {
/**
* Reset any value counts accumulated thus far.
*/
+ @Override
public synchronized void reset() {
// the currently inactive histogram is reset each time we flip. So flipping twice resets both:
performIntervalSample();
@@ -268,21 +322,24 @@ public class SingleWriterRecorder {
}
}
- void validateFitAsReplacementHistogram(Histogram replacementHistogram) {
+ private void validateFitAsReplacementHistogram(Histogram replacementHistogram,
+ boolean enforeContainingInstance) {
boolean bad = true;
if (replacementHistogram == null) {
bad = false;
} else if ((replacementHistogram instanceof InternalHistogram)
&&
- (((InternalHistogram) replacementHistogram).containingInstanceId ==
- activeHistogram.containingInstanceId)
- ) {
+ ((!enforeContainingInstance) ||
+ (((InternalHistogram) replacementHistogram).containingInstanceId ==
+ activeHistogram.containingInstanceId)
+ )) {
bad = false;
}
if (bad) {
- throw new IllegalArgumentException("replacement histogram must have been obtained via a previous" +
- "getIntervalHistogram() call from this " + this.getClass().getName() +" instance");
+ throw new IllegalArgumentException("replacement histogram must have been obtained via a previous " +
+ "getIntervalHistogram() call from this " + this.getClass().getName() +
+ (enforeContainingInstance ? " insatnce" : " class"));
}
}
}
=====================================
src/main/java/org/HdrHistogram/SynchronizedDoubleHistogram.java
=====================================
@@ -264,6 +264,11 @@ public class SynchronizedDoubleHistogram extends DoubleHistogram {
}
}
+ @Override
+ public synchronized int hashCode() {
+ return super.hashCode();
+ }
+
@Override
public synchronized long getTotalCount() {
return super.getTotalCount();
=====================================
src/main/java/org/HdrHistogram/SynchronizedHistogram.java
=====================================
@@ -309,6 +309,11 @@ public class SynchronizedHistogram extends Histogram {
}
}
+ @Override
+ public synchronized int hashCode() {
+ return super.hashCode();
+ }
+
@Override
public synchronized long getLowestDiscernibleValue() {
return super.getLowestDiscernibleValue();
=====================================
src/main/java/org/HdrHistogram/ValueRecorder.java
=====================================
@@ -0,0 +1,47 @@
+package org.HdrHistogram;
+
+public interface ValueRecorder {
+
+ /**
+ * Record a value
+ *
+ * @param value The value to be recorded
+ * @throws ArrayIndexOutOfBoundsException (may throw) if value cannot be covered by the histogram's range
+ */
+ void recordValue(long value) throws ArrayIndexOutOfBoundsException;
+
+ /**
+ * Record a value (adding to the value's current count)
+ *
+ * @param value The value to be recorded
+ * @param count The number of occurrences of this value to record
+ * @throws ArrayIndexOutOfBoundsException (may throw) if value cannot be covered by the histogram's range
+ */
+ void recordValueWithCount(long value, long count) throws ArrayIndexOutOfBoundsException;
+
+ /**
+ * Record a value.
+ * <p>
+ * To compensate for the loss of sampled values when a recorded value is larger than the expected
+ * interval between value samples, will auto-generate an additional series of decreasingly-smaller
+ * (down to the expectedIntervalBetweenValueSamples) value records.
+ * <p>
+ * Note: This is a at-recording correction method, as opposed to the post-recording correction method provided
+ * by {@link AbstractHistogram#copyCorrectedForCoordinatedOmission(long)}.
+ * The two methods are mutually exclusive, and only one of the two should be be used on a given data set to correct
+ * for the same coordinated omission issue.
+ *
+ * @param value The value to record
+ * @param expectedIntervalBetweenValueSamples If expectedIntervalBetweenValueSamples is larger than 0, add
+ * auto-generated value records as appropriate if value is larger
+ * than expectedIntervalBetweenValueSamples
+ * @throws ArrayIndexOutOfBoundsException (may throw) if value cannot be covered by the histogram's range
+ */
+ void recordValueWithExpectedInterval(long value, long expectedIntervalBetweenValueSamples)
+ throws ArrayIndexOutOfBoundsException;
+
+ /**
+ * Reset the contents and collected stats
+ */
+ void reset();
+}
=====================================
src/perf/java/org/HdrHistogram/HistogramPerfTest.java
=====================================
@@ -18,7 +18,7 @@ public class HistogramPerfTest {
static final int numberOfSignificantValueDigits = 3;
static final long testValueLevel = 12340;
static final long warmupLoopLength = 50000;
- static final long rawTimingLoopCount = 500000000L;
+ static final long rawTimingLoopCount = 800000000L;
static final long rawDoubleTimingLoopCount = 300000000L;
static final long singleWriterIntervalTimingLoopCount = 100000000L;
static final long singleWriterDoubleIntervalTimingLoopCount = 100000000L;
@@ -27,6 +27,11 @@ public class HistogramPerfTest {
static final long atomicTimingLoopCount = 80000000L;
static final long concurrentTimingLoopCount = 50000000L;
+ void recordLoop(AbstractHistogram histogram, long loopCount) {
+ for (long i = 0; i < loopCount; i++)
+ histogram.recordValue(testValueLevel + (i & 0x8000));
+ }
+
void recordLoopWithExpectedInterval(AbstractHistogram histogram, long loopCount, long expectedInterval) {
for (long i = 0; i < loopCount; i++)
histogram.recordValueWithExpectedInterval(testValueLevel + (i & 0x8000), expectedInterval);
@@ -74,6 +79,35 @@ public class HistogramPerfTest {
return sum;
}
+ public void testRawRecordingSpeedSingleValue(String label, AbstractHistogram histogram, long timingLoopCount) throws Exception {
+ System.out.println("\nTiming recording speed with single value per recording:");
+ // Warm up:
+ long startTime = System.nanoTime();
+ recordLoop(histogram, warmupLoopLength);
+ long endTime = System.nanoTime();
+ long deltaUsec = (endTime - startTime) / 1000L;
+ long rate = 1000000 * warmupLoopLength / deltaUsec;
+ System.out.println(label + "Warmup: " + warmupLoopLength + " value recordings completed in " +
+ deltaUsec + " usec, rate = " + rate + " value recording calls per sec.");
+ histogram.reset();
+ // Wait a bit to make sure compiler had a cache to do it's stuff:
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ }
+ startTime = System.nanoTime();
+ recordLoop(histogram, timingLoopCount);
+ endTime = System.nanoTime();
+ deltaUsec = (endTime - startTime) / 1000L;
+ rate = 1000000 * timingLoopCount / deltaUsec;
+ System.out.println(label + "Hot code timing:");
+ System.out.println(label + timingLoopCount + " value recordings completed in " +
+ deltaUsec + " usec, rate = " + rate + " value recording calls per sec.");
+ rate = 1000000 * histogram.getTotalCount() / deltaUsec;
+ System.out.println(label + histogram.getTotalCount() + " raw recorded entries completed in " +
+ deltaUsec + " usec, rate = " + rate + " recorded values per sec.");
+ }
+
public void testRawRecordingSpeedAtExpectedInterval(String label, AbstractHistogram histogram,
long expectedInterval, long timingLoopCount) throws Exception {
System.out.println("\nTiming recording speed with expectedInterval = " + expectedInterval + " :");
@@ -258,6 +292,14 @@ public class HistogramPerfTest {
deltaUsec + " usec, rate = " + rate + " recorded values per sec.");
}
+ @Test
+ public void testRawRecordingSpeedSingleValue() throws Exception {
+ AbstractHistogram histogram;
+ histogram = new Histogram(highestTrackableValue, numberOfSignificantValueDigits);
+ System.out.println("\n\nTiming Histogram:");
+ testRawRecordingSpeedSingleValue("Histogram: ", histogram, rawTimingLoopCount);
+ }
+
@Test
public void testRawRecordingSpeed() throws Exception {
AbstractHistogram histogram;
=====================================
src/test/java/org/HdrHistogram/DoubleHistogramTest.java
=====================================
@@ -144,9 +144,17 @@ public class DoubleHistogramTest {
public void testReset() throws Exception {
DoubleHistogram histogram = new DoubleHistogram(trackableValueRangeSize, numberOfSignificantValueDigits);
histogram.recordValue(testValueLevel);
+ histogram.recordValue(10);
+ histogram.recordValue(100);
+ Assert.assertEquals(histogram.getMinValue(), Math.min(10.0, testValueLevel), 1.0);
+ Assert.assertEquals(histogram.getMaxValue(), Math.max(100.0, testValueLevel), 1.0);
histogram.reset();
assertEquals(0L, histogram.getCountAtValue(testValueLevel));
assertEquals(0L, histogram.getTotalCount());
+ histogram.recordValue(20);
+ histogram.recordValue(80);
+ Assert.assertEquals(histogram.getMinValue(), 20.0, 1.0);
+ Assert.assertEquals(histogram.getMaxValue(), 80.0, 1.0);
}
@Test
=====================================
src/test/java/org/HdrHistogram/HistogramAutosizingTest.java
=====================================
@@ -28,6 +28,23 @@ public class HistogramAutosizingTest {
Assert.assertEquals(55296, histogram.countsArrayLength);
}
+ @Test
+ public void testHistogramEqualsAfterResizing() throws Exception {
+ Histogram histogram = new Histogram(3);
+ histogram.recordValue((1L << 62) - 1);
+ Assert.assertEquals(52, histogram.bucketCount);
+ Assert.assertEquals(54272, histogram.countsArrayLength);
+ histogram.recordValue(Long.MAX_VALUE);
+ Assert.assertEquals(53, histogram.bucketCount);
+ Assert.assertEquals(55296, histogram.countsArrayLength);
+ histogram.reset();
+ histogram.recordValue((1L << 62) - 1);
+
+ Histogram histogram1 = new Histogram(3);
+ histogram1.recordValue((1L << 62) - 1);
+ Assert.assertEquals(histogram, histogram1);
+ }
+
@Test
public void testDoubleHistogramAutoSizingEdges() throws Exception {
DoubleHistogram histogram = new DoubleHistogram(3);
=====================================
src/test/java/org/HdrHistogram/HistogramDataAccessTest.java
=====================================
@@ -605,4 +605,96 @@ public class HistogramDataAccessTest {
Assert.assertTrue("Histograms should be equal", histogram1.equals(histogram2));
}
+
+ @Test
+ public void testLinearIteratorVisitsBucketsWiderThanStepSizeMultipleTimes() {
+ Histogram h = new Histogram(1, Long.MAX_VALUE, 3);
+
+ h.recordValue(1);
+ h.recordValue(2047);
+ // bucket size 2
+ h.recordValue(2048);
+ h.recordValue(2049);
+ h.recordValue(4095);
+ // bucket size 4
+ h.recordValue(4096);
+ h.recordValue(4097);
+ h.recordValue(4098);
+ h.recordValue(4099);
+ // 2nd bucket in size 4
+ h.recordValue(4100);
+
+ // sadly verbose helper class to hang on to iteration information for later comparison
+ class IteratorValueSnapshot {
+ private final long value;
+ private final long count;
+
+ private IteratorValueSnapshot(HistogramIterationValue iv) {
+ this.value = iv.getValueIteratedTo();
+ this.count = iv.getCountAddedInThisIterationStep();
+ }
+
+ private IteratorValueSnapshot(long value, long count) {
+ this.value = value;
+ this.count = count;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) { return true; }
+ if (o == null || getClass() != o.getClass()) { return false; }
+
+ IteratorValueSnapshot that = (IteratorValueSnapshot) o;
+
+ if (value != that.value) { return false; }
+ return count == that.count;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = (int) (value ^ (value >>> 32));
+ result = 31 * result + (int) (count ^ (count >>> 32));
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "IteratorValueSnapshot{" +
+ "value=" + value +
+ ", count=" + count +
+ '}';
+ }
+ }
+
+ List<IteratorValueSnapshot> snapshots = new ArrayList<IteratorValueSnapshot>();
+
+ for (HistogramIterationValue iv : h.linearBucketValues(1)) {
+ snapshots.add(new IteratorValueSnapshot(iv));
+ }
+
+ // bucket size 1
+ Assert.assertEquals(new IteratorValueSnapshot(0, 0), snapshots.get(0));
+ Assert.assertEquals(new IteratorValueSnapshot(1, 1), snapshots.get(1));
+ Assert.assertEquals(new IteratorValueSnapshot(2046, 0), snapshots.get(2046));
+ Assert.assertEquals(new IteratorValueSnapshot(2047, 1), snapshots.get(2047));
+ // bucket size 2
+ Assert.assertEquals(new IteratorValueSnapshot(2048, 2), snapshots.get(2048));
+ Assert.assertEquals(new IteratorValueSnapshot(2049, 0), snapshots.get(2049));
+ Assert.assertEquals(new IteratorValueSnapshot(2050, 0), snapshots.get(2050));
+ Assert.assertEquals(new IteratorValueSnapshot(2051, 0), snapshots.get(2051));
+ Assert.assertEquals(new IteratorValueSnapshot(4094, 1), snapshots.get(4094));
+ Assert.assertEquals(new IteratorValueSnapshot(4095, 0), snapshots.get(4095));
+ // bucket size 4
+ Assert.assertEquals(new IteratorValueSnapshot(4096, 4), snapshots.get(4096));
+ Assert.assertEquals(new IteratorValueSnapshot(4097, 0), snapshots.get(4097));
+ Assert.assertEquals(new IteratorValueSnapshot(4098, 0), snapshots.get(4098));
+ Assert.assertEquals(new IteratorValueSnapshot(4099, 0), snapshots.get(4099));
+ // also size 4, last bucket
+ Assert.assertEquals(new IteratorValueSnapshot(4100, 1), snapshots.get(4100));
+ Assert.assertEquals(new IteratorValueSnapshot(4101, 0), snapshots.get(4101));
+ Assert.assertEquals(new IteratorValueSnapshot(4102, 0), snapshots.get(4102));
+ Assert.assertEquals(new IteratorValueSnapshot(4103, 0), snapshots.get(4103));
+
+ Assert.assertEquals(4104, snapshots.size());
+ }
}
=====================================
src/test/java/org/HdrHistogram/HistogramTest.java
=====================================
@@ -377,10 +377,18 @@ public class HistogramTest {
public void testReset() throws Exception {
Histogram histogram = new Histogram(highestTrackableValue, numberOfSignificantValueDigits);
histogram.recordValue(testValueLevel);
+ histogram.recordValue(10);
+ histogram.recordValue(100);
+ Assert.assertEquals(histogram.getMinValue(), Math.min(10, testValueLevel));
+ Assert.assertEquals(histogram.getMaxValue(), Math.max(100, testValueLevel));
histogram.reset();
Assert.assertEquals(0L, histogram.getCountAtValue(testValueLevel));
Assert.assertEquals(0L, histogram.getTotalCount());
verifyMaxValue(histogram);
+ histogram.recordValue(20);
+ histogram.recordValue(80);
+ Assert.assertEquals(histogram.getMinValue(), 20);
+ Assert.assertEquals(histogram.getMaxValue(), 80);
}
@Test
=====================================
src/test/java/org/HdrHistogram/RecorderTest.java
=====================================
@@ -119,4 +119,135 @@ public class RecorderTest {
Histogram histogram = recorder.getIntervalHistogram();
}
+ // Recorder Recycling tests:
+
+ @Test
+ public void testRecycling() throws Exception {
+ Recorder recorder = new Recorder(3);
+ Histogram histogramA = recorder.getIntervalHistogram();
+ Histogram histogramB = recorder.getIntervalHistogram(histogramA);
+ Histogram histogramC = recorder.getIntervalHistogram(histogramA, true);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testRecyclingContainingClassEnforcement() throws Exception {
+ Histogram histToRecycle = new Histogram(3);
+ Recorder recorder = new Recorder(3);
+ Histogram histogramA = recorder.getIntervalHistogram(histToRecycle);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testRecyclingContainingInstanceEnforcement() throws Exception {
+ Recorder recorder1 = new Recorder(3);
+ Recorder recorder2 = new Recorder(3);
+ Histogram histToRecycle = recorder1.getIntervalHistogram();
+ Histogram histToRecycle2 = recorder2.getIntervalHistogram(histToRecycle);
+ }
+
+ @Test
+ public void testRecyclingContainingInstanceNonEnforcement() throws Exception {
+ Recorder recorder1 = new Recorder(3);
+ Recorder recorder2 = new Recorder(3);
+ Histogram histToRecycle = recorder1.getIntervalHistogram();
+ Histogram histToRecycle2 = recorder2.getIntervalHistogram(histToRecycle, false);
+ }
+
+ // SingleWriterRecorder Recycling tests:
+
+ @Test
+ public void testSWRecycling() throws Exception {
+ SingleWriterRecorder recorder = new SingleWriterRecorder(3);
+ Histogram histogramA = recorder.getIntervalHistogram();
+ Histogram histogramB = recorder.getIntervalHistogram(histogramA);
+ Histogram histogramC = recorder.getIntervalHistogram(histogramA, true);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSWRecyclingContainingClassEnforcement() throws Exception {
+ Histogram histToRecycle = new Histogram(3);
+ SingleWriterRecorder recorder = new SingleWriterRecorder(3);
+ Histogram histogramA = recorder.getIntervalHistogram(histToRecycle);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSWRecyclingContainingInstanceEnforcement() throws Exception {
+ SingleWriterRecorder recorder1 = new SingleWriterRecorder(3);
+ SingleWriterRecorder recorder2 = new SingleWriterRecorder(3);
+ Histogram histToRecycle = recorder1.getIntervalHistogram();
+ Histogram histToRecycle2 = recorder2.getIntervalHistogram(histToRecycle);
+ }
+
+ @Test
+ public void testSWRecyclingContainingInstanceNonEnforcement() throws Exception {
+ SingleWriterRecorder recorder1 = new SingleWriterRecorder(3);
+ SingleWriterRecorder recorder2 = new SingleWriterRecorder(3);
+ Histogram histToRecycle = recorder1.getIntervalHistogram();
+ Histogram histToRecycle2 = recorder2.getIntervalHistogram(histToRecycle, false);
+ }
+
+ // DoubleRecorder Recycling tests:
+
+ @Test
+ public void testDRecycling() throws Exception {
+ DoubleRecorder recorder = new DoubleRecorder(3);
+ DoubleHistogram histogramA = recorder.getIntervalHistogram();
+ DoubleHistogram histogramB = recorder.getIntervalHistogram(histogramA);
+ DoubleHistogram histogramC = recorder.getIntervalHistogram(histogramA, true);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testDRecyclingContainingClassEnforcement() throws Exception {
+ DoubleHistogram histToRecycle = new DoubleHistogram(3);
+ DoubleRecorder recorder = new DoubleRecorder(3);
+ DoubleHistogram histogramA = recorder.getIntervalHistogram(histToRecycle);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testDRecyclingContainingInstanceEnforcement() throws Exception {
+ DoubleRecorder recorder1 = new DoubleRecorder(3);
+ DoubleRecorder recorder2 = new DoubleRecorder(3);
+ DoubleHistogram histToRecycle = recorder1.getIntervalHistogram();
+ DoubleHistogram histToRecycle2 = recorder2.getIntervalHistogram(histToRecycle);
+ }
+
+ @Test
+ public void testDRecyclingContainingInstanceNonEnforcement() throws Exception {
+ DoubleRecorder recorder1 = new DoubleRecorder(3);
+ DoubleRecorder recorder2 = new DoubleRecorder(3);
+ DoubleHistogram histToRecycle = recorder1.getIntervalHistogram();
+ DoubleHistogram histToRecycle2 = recorder2.getIntervalHistogram(histToRecycle, false);
+ }
+
+ // SingleWriterDoubleRecorder Recycling tests:
+
+ @Test
+ public void testSWDRecycling() throws Exception {
+ SingleWriterDoubleRecorder recorder = new SingleWriterDoubleRecorder(3);
+ DoubleHistogram histogramA = recorder.getIntervalHistogram();
+ DoubleHistogram histogramB = recorder.getIntervalHistogram(histogramA);
+ DoubleHistogram histogramC = recorder.getIntervalHistogram(histogramA, true);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSWDRecyclingContainingClassEnforcement() throws Exception {
+ DoubleHistogram histToRecycle = new DoubleHistogram(3);
+ SingleWriterDoubleRecorder recorder = new SingleWriterDoubleRecorder(3);
+ DoubleHistogram histogramA = recorder.getIntervalHistogram(histToRecycle);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSWDRecyclingContainingInstanceEnforcement() throws Exception {
+ SingleWriterDoubleRecorder recorder1 = new SingleWriterDoubleRecorder(3);
+ SingleWriterDoubleRecorder recorder2 = new SingleWriterDoubleRecorder(3);
+ DoubleHistogram histToRecycle = recorder1.getIntervalHistogram();
+ DoubleHistogram histToRecycle2 = recorder2.getIntervalHistogram(histToRecycle);
+ }
+
+ @Test
+ public void testSWDRecyclingContainingInstanceNonEnforcement() throws Exception {
+ SingleWriterDoubleRecorder recorder1 = new SingleWriterDoubleRecorder(3);
+ SingleWriterDoubleRecorder recorder2 = new SingleWriterDoubleRecorder(3);
+ DoubleHistogram histToRecycle = recorder1.getIntervalHistogram();
+ DoubleHistogram histToRecycle2 = recorder2.getIntervalHistogram(histToRecycle, false);
+ }
}
View it on GitLab: https://salsa.debian.org/java-team/hdrhistogram/commit/ecf9920b08d12eb87eb1d010e2061570497f4e71
--
View it on GitLab: https://salsa.debian.org/java-team/hdrhistogram/commit/ecf9920b08d12eb87eb1d010e2061570497f4e71
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-java-commits/attachments/20190119/c5d5bbcb/attachment.html>
More information about the pkg-java-commits
mailing list