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    
019    package org.apache.hadoop.metrics2.util;
020    
021    import org.apache.hadoop.classification.InterfaceAudience;
022    
023    /**
024     * Helper to compute running sample stats
025     */
026    @InterfaceAudience.Private
027    public 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    }