1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.metrics.histogram;
20
21 import java.util.concurrent.atomic.AtomicLong;
22 import java.util.concurrent.atomic.AtomicReference;
23
24 import org.apache.hadoop.metrics.MetricsRecord;
25 import org.apache.hadoop.metrics.util.MetricsBase;
26 import org.apache.hadoop.metrics.util.MetricsRegistry;
27
28 import com.yammer.metrics.stats.Sample;
29 import com.yammer.metrics.stats.Snapshot;
30 import com.yammer.metrics.stats.UniformSample;
31 import com.yammer.metrics.stats.ExponentiallyDecayingSample;
32
33 public class MetricsHistogram extends MetricsBase {
34
35
36
37 private static final int DEFAULT_SAMPLE_SIZE = 1028;
38
39
40
41 private static final double DEFAULT_ALPHA = 0.015;
42 public static final String NUM_OPS_METRIC_NAME = "_num_ops";
43 public static final String MIN_METRIC_NAME = "_min";
44 public static final String MAX_METRIC_NAME = "_max";
45 public static final String MEAN_METRIC_NAME = "_mean";
46 public static final String STD_DEV_METRIC_NAME = "_std_dev";
47 public static final String MEDIAN_METRIC_NAME = "_median";
48 public static final String SEVENTY_FIFTH_PERCENTILE_METRIC_NAME = "_75th_percentile";
49 public static final String NINETY_FIFTH_PERCENTILE_METRIC_NAME = "_95th_percentile";
50 public static final String NINETY_NINETH_PERCENTILE_METRIC_NAME = "_99th_percentile";
51
52
53
54
55
56
57
58
59
60
61 public MetricsHistogram(final String nam, final MetricsRegistry registry,
62 final String description, boolean forwardBiased) {
63 super(nam, description);
64
65 this.min = new AtomicLong();
66 this.max = new AtomicLong();
67 this.sum = new AtomicLong();
68 this.sample = forwardBiased ?
69 new ExponentiallyDecayingSample(DEFAULT_SAMPLE_SIZE, DEFAULT_ALPHA)
70 : new UniformSample(DEFAULT_SAMPLE_SIZE);
71
72 this.variance = new AtomicReference<double[]>(new double[]{-1, 0});
73 this.count = new AtomicLong();
74
75 this.clear();
76
77 if (registry != null) {
78 registry.add(nam, this);
79 }
80 }
81
82
83
84
85
86
87
88 public MetricsHistogram(final String nam, MetricsRegistry registry,
89 final String description) {
90 this(nam, registry, NO_DESCRIPTION, true);
91 }
92
93
94
95
96
97
98 public MetricsHistogram(final String nam, MetricsRegistry registry) {
99 this(nam, registry, NO_DESCRIPTION);
100 }
101
102 private final Sample sample;
103 private final AtomicLong min;
104 private final AtomicLong max;
105 private final AtomicLong sum;
106
107
108
109 private final AtomicReference<double[]> variance;
110 private final AtomicLong count;
111
112
113
114
115 public void clear() {
116 this.sample.clear();
117 this.count.set(0);
118 this.max.set(Long.MIN_VALUE);
119 this.min.set(Long.MAX_VALUE);
120 this.sum.set(0);
121 variance.set(new double[]{-1, 0});
122 }
123
124 public void update(int val) {
125 update((long) val);
126 }
127
128 public void update(final long val) {
129 count.incrementAndGet();
130 sample.update(val);
131 setMax(val);
132 setMin(val);
133 sum.getAndAdd(val);
134 updateVariance(val);
135 }
136
137 private void setMax(final long potentialMax) {
138 boolean done = false;
139 while (!done) {
140 final long currentMax = max.get();
141 done = currentMax >= potentialMax
142 || max.compareAndSet(currentMax, potentialMax);
143 }
144 }
145
146 private void setMin(long potentialMin) {
147 boolean done = false;
148 while (!done) {
149 final long currentMin = min.get();
150 done = currentMin <= potentialMin
151 || min.compareAndSet(currentMin, potentialMin);
152 }
153 }
154
155 private void updateVariance(long value) {
156 boolean done = false;
157 while (!done) {
158 final double[] oldValues = variance.get();
159 final double[] newValues = new double[2];
160 if (oldValues[0] == -1) {
161 newValues[0] = value;
162 newValues[1] = 0;
163 } else {
164 final double oldM = oldValues[0];
165 final double oldS = oldValues[1];
166
167 final double newM = oldM + ((value - oldM) / getCount());
168 final double newS = oldS + ((value - oldM) * (value - newM));
169
170 newValues[0] = newM;
171 newValues[1] = newS;
172 }
173 done = variance.compareAndSet(oldValues, newValues);
174 }
175 }
176
177
178 public long getCount() {
179 return count.get();
180 }
181
182 public long getMax() {
183 if (getCount() > 0) {
184 return max.get();
185 }
186 return 0L;
187 }
188
189 public long getMin() {
190 if (getCount() > 0) {
191 return min.get();
192 }
193 return 0L;
194 }
195
196 public double getMean() {
197 if (getCount() > 0) {
198 return sum.get() / (double) getCount();
199 }
200 return 0.0;
201 }
202
203 public double getStdDev() {
204 if (getCount() > 0) {
205 return Math.sqrt(getVariance());
206 }
207 return 0.0;
208 }
209
210 public Snapshot getSnapshot() {
211 return sample.getSnapshot();
212 }
213
214 private double getVariance() {
215 if (getCount() <= 1) {
216 return 0.0;
217 }
218 return variance.get()[1] / (getCount() - 1);
219 }
220
221 @Override
222 public void pushMetric(MetricsRecord mr) {
223 final Snapshot s = this.getSnapshot();
224 mr.setMetric(getName() + NUM_OPS_METRIC_NAME, this.getCount());
225 mr.setMetric(getName() + MIN_METRIC_NAME, this.getMin());
226 mr.setMetric(getName() + MAX_METRIC_NAME, this.getMax());
227
228 mr.setMetric(getName() + MEAN_METRIC_NAME, (float) this.getMean());
229 mr.setMetric(getName() + STD_DEV_METRIC_NAME, (float) this.getStdDev());
230
231 mr.setMetric(getName() + MEDIAN_METRIC_NAME, (float) s.getMedian());
232 mr.setMetric(getName() + SEVENTY_FIFTH_PERCENTILE_METRIC_NAME,
233 (float) s.get75thPercentile());
234 mr.setMetric(getName() + NINETY_FIFTH_PERCENTILE_METRIC_NAME,
235 (float) s.get95thPercentile());
236 mr.setMetric(getName() + NINETY_NINETH_PERCENTILE_METRIC_NAME,
237 (float) s.get99thPercentile());
238 }
239 }