HADOOP-8929. Add toString, other improvements for SampleQuantiles. Contributed by Todd Lipcon.

git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/branches/branch-2@1398659 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Todd Lipcon 2012-10-16 06:06:28 +00:00
parent 85bd8e0b72
commit da255e41f4
5 changed files with 58 additions and 37 deletions

View File

@ -49,6 +49,8 @@ Release 2.0.3-alpha - Unreleased
HADOOP-8784. Improve IPC.Client's token use (daryn) HADOOP-8784. Improve IPC.Client's token use (daryn)
HADOOP-8929. Add toString, other improvements for SampleQuantiles (todd)
OPTIMIZATIONS OPTIMIZATIONS
HADOOP-8866. SampleQuantiles#query is O(N^2) instead of O(N). (Andrew Wang HADOOP-8866. SampleQuantiles#query is O(N^2) instead of O(N). (Andrew Wang

View File

@ -20,7 +20,6 @@ package org.apache.hadoop.metrics2.lib;
import static org.apache.hadoop.metrics2.lib.Interns.info; import static org.apache.hadoop.metrics2.lib.Interns.info;
import java.io.IOException;
import java.util.Map; import java.util.Map;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
@ -150,14 +149,8 @@ public class MutableQuantiles extends MutableMetric {
@Override @Override
public void run() { public void run() {
synchronized (parent) { synchronized (parent) {
try { parent.previousCount = parent.estimator.getCount();
parent.previousCount = parent.estimator.getCount(); parent.previousSnapshot = parent.estimator.snapshot();
parent.previousSnapshot = parent.estimator.snapshot();
} catch (IOException e) {
// Couldn't get a new snapshot because the window was empty
parent.previousCount = 0;
parent.previousSnapshot = null;
}
parent.estimator.clear(); parent.estimator.clear();
} }
parent.setChanged(); parent.setChanged();

View File

@ -20,12 +20,14 @@ package org.apache.hadoop.metrics2.util;
import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceAudience;
import com.google.common.collect.ComparisonChain;
/** /**
* Specifies a quantile (with error bounds) to be watched by a * Specifies a quantile (with error bounds) to be watched by a
* {@link SampleQuantiles} object. * {@link SampleQuantiles} object.
*/ */
@InterfaceAudience.Private @InterfaceAudience.Private
public class Quantile { public class Quantile implements Comparable<Quantile> {
public final double quantile; public final double quantile;
public final double error; public final double error;
@ -57,4 +59,19 @@ public class Quantile {
return (int) (Double.doubleToLongBits(quantile) ^ Double return (int) (Double.doubleToLongBits(quantile) ^ Double
.doubleToLongBits(error)); .doubleToLongBits(error));
} }
@Override
public int compareTo(Quantile other) {
return ComparisonChain.start()
.compare(quantile, other.quantile)
.compare(error, other.error)
.result();
}
@Override
public String toString() {
return String.format("%.2f %%ile +/- %.2f%%",
quantile * 100, error * 100);
}
} }

View File

@ -18,16 +18,17 @@
package org.apache.hadoop.metrics2.util; package org.apache.hadoop.metrics2.util;
import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.ListIterator; import java.util.ListIterator;
import java.util.Map; import java.util.Map;
import java.util.TreeMap;
import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceAudience;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
/** /**
* Implementation of the Cormode, Korn, Muthukrishnan, and Srivastava algorithm * Implementation of the Cormode, Korn, Muthukrishnan, and Srivastava algorithm
@ -202,10 +203,8 @@ public class SampleQuantiles {
* @param quantile Queried quantile, e.g. 0.50 or 0.99. * @param quantile Queried quantile, e.g. 0.50 or 0.99.
* @return Estimated value at that quantile. * @return Estimated value at that quantile.
*/ */
private long query(double quantile) throws IOException { private long query(double quantile) {
if (samples.size() == 0) { Preconditions.checkState(!samples.isEmpty(), "no data in estimator");
throw new IOException("No samples present");
}
int rankMin = 0; int rankMin = 0;
int desired = (int) (quantile * count); int desired = (int) (quantile * count);
@ -231,14 +230,18 @@ public class SampleQuantiles {
/** /**
* Get a snapshot of the current values of all the tracked quantiles. * Get a snapshot of the current values of all the tracked quantiles.
* *
* @return snapshot of the tracked quantiles * @return snapshot of the tracked quantiles. If no items are added
* @throws IOException * to the estimator, returns null.
* if no items have been added to the estimator
*/ */
synchronized public Map<Quantile, Long> snapshot() throws IOException { synchronized public Map<Quantile, Long> snapshot() {
// flush the buffer first for best results // flush the buffer first for best results
insertBatch(); insertBatch();
Map<Quantile, Long> values = new HashMap<Quantile, Long>(quantiles.length);
if (samples.isEmpty()) {
return null;
}
Map<Quantile, Long> values = new TreeMap<Quantile, Long>();
for (int i = 0; i < quantiles.length; i++) { for (int i = 0; i < quantiles.length; i++) {
values.put(quantiles[i], query(quantiles[i].quantile)); values.put(quantiles[i], query(quantiles[i].quantile));
} }
@ -274,6 +277,16 @@ public class SampleQuantiles {
samples.clear(); samples.clear();
} }
@Override
synchronized public String toString() {
Map<Quantile, Long> data = snapshot();
if (data == null) {
return "[no samples]";
} else {
return Joiner.on("\n").withKeyValueSeparator(": ").join(data);
}
}
/** /**
* Describes a measured value passed to the estimator, tracking additional * Describes a measured value passed to the estimator, tracking additional
* metadata required by the CKMS algorithm. * metadata required by the CKMS algorithm.

View File

@ -18,9 +18,7 @@
package org.apache.hadoop.metrics2.util; package org.apache.hadoop.metrics2.util;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.*;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
@ -28,7 +26,6 @@ import java.util.Collections;
import java.util.Map; import java.util.Map;
import java.util.Random; import java.util.Random;
import org.apache.hadoop.test.GenericTestUtils;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -54,18 +51,22 @@ public class TestSampleQuantiles {
// Counts start off zero // Counts start off zero
assertEquals(estimator.getCount(), 0); assertEquals(estimator.getCount(), 0);
assertEquals(estimator.getSampleCount(), 0); assertEquals(estimator.getSampleCount(), 0);
try {
estimator.snapshot(); // Snapshot should be null if there are no entries.
fail("Expected IOException from empty window"); assertNull(estimator.snapshot());
} catch (IOException e) {
GenericTestUtils.assertExceptionContains("No samples", e);
}
// Count increment correctly by 1 // Count increment correctly by 1
estimator.insert(1337); estimator.insert(1337);
assertEquals(estimator.getCount(), 1); assertEquals(estimator.getCount(), 1);
estimator.snapshot(); estimator.snapshot();
assertEquals(estimator.getSampleCount(), 1); assertEquals(estimator.getSampleCount(), 1);
assertEquals(
"50.00 %ile +/- 5.00%: 1337\n" +
"75.00 %ile +/- 2.50%: 1337\n" +
"90.00 %ile +/- 1.00%: 1337\n" +
"95.00 %ile +/- 0.50%: 1337\n" +
"99.00 %ile +/- 0.10%: 1337", estimator.toString());
} }
/** /**
@ -80,12 +81,7 @@ public class TestSampleQuantiles {
estimator.clear(); estimator.clear();
assertEquals(estimator.getCount(), 0); assertEquals(estimator.getCount(), 0);
assertEquals(estimator.getSampleCount(), 0); assertEquals(estimator.getSampleCount(), 0);
try { assertNull(estimator.snapshot());
estimator.snapshot();
fail("Expected IOException for an empty window.");
} catch (IOException e) {
GenericTestUtils.assertExceptionContains("No samples", e);
}
} }
/** /**