001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019package org.apache.hadoop.metrics2.util;
020
021import org.apache.hadoop.classification.InterfaceAudience;
022
023/**
024 * Helper to compute running sample stats
025 */
026@InterfaceAudience.Private
027public class SampleStat {
028  private final MinMax minmax = new MinMax();
029  private long numSamples = 0;
030  private double a0, a1, s0, s1;
031
032  /**
033   * Construct a new running sample stat
034   */
035  public SampleStat() {
036    a0 = s0 = 0.0;
037  }
038
039  public void reset() {
040    numSamples = 0;
041    a0 = s0 = 0.0;
042    minmax.reset();
043  }
044
045  // We want to reuse the object, sometimes.
046  void reset(long numSamples, double a0, double a1, double s0, double s1,
047             MinMax minmax) {
048    this.numSamples = numSamples;
049    this.a0 = a0;
050    this.a1 = a1;
051    this.s0 = s0;
052    this.s1 = s1;
053    this.minmax.reset(minmax);
054  }
055
056  /**
057   * Copy the values to other (saves object creation and gc.)
058   * @param other the destination to hold our values
059   */
060  public void copyTo(SampleStat other) {
061    other.reset(numSamples, a0, a1, s0, s1, minmax);
062  }
063
064  /**
065   * Add a sample the running stat.
066   * @param x the sample number
067   * @return  self
068   */
069  public SampleStat add(double x) {
070    minmax.add(x);
071    return add(1, x);
072  }
073
074  /**
075   * Add some sample and a partial sum to the running stat.
076   * Note, min/max is not evaluated using this method.
077   * @param nSamples  number of samples
078   * @param x the partial sum
079   * @return  self
080   */
081  public SampleStat add(long nSamples, double x) {
082    numSamples += nSamples;
083
084    if (numSamples == 1) {
085      a0 = a1 = x;
086      s0 = 0.0;
087    }
088    else {
089      // The Welford method for numerical stability
090      a1 = a0 + (x - a0) / numSamples;
091      s1 = s0 + (x - a0) * (x - a1);
092      a0 = a1;
093      s0 = s1;
094    }
095    return this;
096  }
097
098  /**
099   * @return  the total number of samples
100   */
101  public long numSamples() {
102    return numSamples;
103  }
104
105  /**
106   * @return  the arithmetic mean of the samples
107   */
108  public double mean() {
109    return numSamples > 0 ? a1 : 0.0;
110  }
111
112  /**
113   * @return  the variance of the samples
114   */
115  public double variance() {
116    return numSamples > 1 ? s1 / (numSamples - 1) : 0.0;
117  }
118
119  /**
120   * @return  the standard deviation of the samples
121   */
122  public double stddev() {
123    return Math.sqrt(variance());
124  }
125
126  /**
127   * @return  the minimum value of the samples
128   */
129  public double min() {
130    return minmax.min();
131  }
132
133  /**
134   * @return  the maximum value of the samples
135   */
136  public double max() {
137    return minmax.max();
138  }
139
140  /**
141   * Helper to keep running min/max
142   */
143  @SuppressWarnings("PublicInnerClass")
144  public static class MinMax {
145
146    // Float.MAX_VALUE is used rather than Double.MAX_VALUE, even though the
147    // min and max variables are of type double.
148    // Float.MAX_VALUE is big enough, and using Double.MAX_VALUE makes 
149    // Ganglia core due to buffer overflow.
150    // The same reasoning applies to the MIN_VALUE counterparts.
151    static final double DEFAULT_MIN_VALUE = Float.MAX_VALUE;
152    static final double DEFAULT_MAX_VALUE = Float.MIN_VALUE;
153
154    private double min = DEFAULT_MIN_VALUE;
155    private double max = DEFAULT_MAX_VALUE;
156
157    public void add(double value) {
158      if (value > max) max = value;
159      if (value < min) min = value;
160    }
161
162    public double min() { return min; }
163    public double max() { return max; }
164
165    public void reset() {
166      min = DEFAULT_MIN_VALUE;
167      max = DEFAULT_MAX_VALUE;
168    }
169
170    public void reset(MinMax other) {
171      min = other.min();
172      max = other.max();
173    }
174  }
175}