Skip to content

Commit

Permalink
Introduced usage of HdrHistogram to record values.
Browse files Browse the repository at this point in the history
HdrHistogram (https://github.com/HdrHistogram/HdrHistogram) is able
to record values without allocation, maintaining a fixed memory cost
and with high performance.

As such, it replaces the in house naive implementation provided by
MeasureRecorder.
  • Loading branch information
sbordet committed Dec 22, 2014
1 parent 52cbd9d commit 0cc2a50
Show file tree
Hide file tree
Showing 4 changed files with 173 additions and 13 deletions.
20 changes: 14 additions & 6 deletions jetty-perf-helper/pom.xml
Expand Up @@ -18,6 +18,13 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<scm>
<connection>scm:git:http://git.eclipse.org/gitroot/jetty/org.eclipse.jetty.toolchain.git</connection>
<developerConnection>scm:git:ssh://git.eclipse.org/gitroot/jetty/org.eclipse.jetty.toolchain.git</developerConnection>
<url>http://git.eclipse.org/c/jetty/org.eclipse.jetty.toolchain.git/tree/jetty-perf-helper</url>
<tag>HEAD</tag>
</scm>

<build>
<plugins>
<plugin>
Expand All @@ -31,12 +38,13 @@
</plugins>
</build>

<scm>
<connection>scm:git:http://git.eclipse.org/gitroot/jetty/org.eclipse.jetty.toolchain.git</connection>
<developerConnection>scm:git:ssh://git.eclipse.org/gitroot/jetty/org.eclipse.jetty.toolchain.git</developerConnection>
<url>http://git.eclipse.org/c/jetty/org.eclipse.jetty.toolchain.git/tree/jetty-perf-helper</url>
<tag>HEAD</tag>
</scm>
<dependencies>
<dependency>
<groupId>org.hdrhistogram</groupId>
<artifactId>HdrHistogram</artifactId>
<version>2.1.2</version>
</dependency>
</dependencies>

</project>

@@ -0,0 +1,123 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//

package org.eclipse.jetty.toolchain.perf;

import java.util.concurrent.TimeUnit;

import org.HdrHistogram.Histogram;
import org.HdrHistogram.HistogramIterationValue;

public class HistogramSnapshot implements MeasureConverter
{
private final Histogram histogram;
private final long buckets;
private final String name;
private final String unit;
private final MeasureConverter converter;

public HistogramSnapshot(Histogram histogram)
{
this(histogram, 32);
}

public HistogramSnapshot(Histogram histogram, long buckets)
{
this(histogram, buckets, "Measures", "ms", null);
}

public HistogramSnapshot(Histogram histogram, long buckets, String name, String unit, MeasureConverter converter)
{
this.histogram = histogram;
this.buckets = buckets;
this.name = name;
this.unit = unit;
this.converter = converter == null ? this : converter;
}

@Override
public long convert(long measure)
{
return TimeUnit.NANOSECONDS.toMillis(measure);
}

@Override
public String toString()
{
StringBuilder builder = new StringBuilder();

long range = histogram.getMaxValue() - histogram.getMinValue();

long maxBucketCount = 0;
for (HistogramIterationValue value : histogram.linearBucketValues(range / buckets))
{
long bucketCount = value.getCountAddedInThisIterationStep();
if (bucketCount > maxBucketCount)
maxBucketCount = bucketCount;
}

double previousPercentile = 0;
for (HistogramIterationValue value : histogram.linearBucketValues(range / buckets))
{
long bucketCount = value.getCountAddedInThisIterationStep();

// Draw the ASCII "gaussian" point.
long point = maxBucketCount == 0 ? 0 : Math.round((double)bucketCount / maxBucketCount * buckets);
if (point == buckets)
--point;
for (long j = 0; j < point; ++j)
builder.append(" ");
builder.append("@");
for (long j = point + 1; j < buckets; ++j)
builder.append(" ");

// Print the measure and its frequency.
builder.append(" _ ");
builder.append(String.format("%,d %s (%d, %.2f%%)",
converter.convert(value.getValueIteratedTo()),
unit,
bucketCount,
100D * bucketCount / histogram.getTotalCount()));
double percentile = value.getPercentile();
if (previousPercentile < 50D && percentile >= 50D)
builder.append(" ^50%");
if (previousPercentile < 85D && percentile >= 85D)
builder.append(" ^85%");
if (previousPercentile < 95D && percentile >= 95D)
builder.append(" ^95%");
if (previousPercentile < 99D && percentile >= 99D)
builder.append(" ^99%");
if (previousPercentile < 99.9D && percentile >= 99.9D)
builder.append(" ^99.9%");
previousPercentile = percentile;
builder.append(System.lineSeparator());
}

builder.append(String.format("%s: %d samples | min/avg/50th%%/99th%%/max = %,d/%,d/%,d/%,d/%,d %s",
name,
histogram.getTotalCount(),
converter.convert(histogram.getMinValue()),
converter.convert(Math.round(histogram.getMean())),
converter.convert(histogram.getValueAtPercentile(50D)),
converter.convert(histogram.getValueAtPercentile(99D)),
converter.convert(histogram.getMaxValue()),
unit));

return builder.toString();
}
}
@@ -0,0 +1,24 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//

package org.eclipse.jetty.toolchain.perf;

public interface MeasureConverter
{
public long convert(long measure);
}
Expand Up @@ -25,18 +25,24 @@
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicLong;

/**
* @deprecated Use {@link org.HdrHistogram.Histogram} to record measures instead,
* and {@link org.eclipse.jetty.toolchain.perf.HistogramSnapshot} to
* format the histogram values.
*/
@Deprecated
public class MeasureRecorder
{
private final AtomicLong count = new AtomicLong();
private final AtomicLong min = new AtomicLong();
private final AtomicLong max = new AtomicLong();
private final AtomicLong total = new AtomicLong();
private final ConcurrentMap<Long, AtomicLong> measures = new ConcurrentHashMap<>();
private final Converter converter;
private final MeasureConverter converter;
private final String name;
private final String unit;

public MeasureRecorder(Converter converter, String name, String unit)
public MeasureRecorder(MeasureConverter converter, String name, String unit)
{
this.converter = converter;
this.name = name;
Expand Down Expand Up @@ -103,6 +109,10 @@ public static void updateMax(AtomicLong currentMax, long newValue)
}
}

/**
* @deprecated Use {@link org.eclipse.jetty.toolchain.perf.HistogramSnapshot} instead
*/
@Deprecated
public class Snapshot
{
public final long count;
Expand Down Expand Up @@ -210,9 +220,4 @@ else if (count > 1)
return builder.toString();
}
}

public interface Converter
{
public long convert(long measure);
}
}

0 comments on commit 0cc2a50

Please sign in to comment.