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.hbase.util;
020
021import org.apache.yetus.audience.InterfaceAudience;
022
023/**
024 * Instead of calculate a whole time average, this class focus on the last N.
025 * The last N is stored in a circle array.
026 */
027@InterfaceAudience.Private
028public class WindowMovingAverage extends MovingAverage {
029  protected final static int DEFAULT_SIZE = 5;
030
031  // The last n statistics.
032  protected long[] lastN;
033  // The index of the most recent statistics.
034  protected int mostRecent;
035  // If it travels a round.
036  protected boolean oneRound;
037
038  public WindowMovingAverage(String label) {
039    this(label, DEFAULT_SIZE);
040  }
041
042  public WindowMovingAverage(String label, int size) {
043    super(label);
044    this.lastN = new long[size <= 0 ? DEFAULT_SIZE : size];
045    this.mostRecent = -1;
046    this.oneRound = false;
047  }
048
049  @Override
050  protected void updateMostRecentTime(long elapsed) {
051    int index = moveForwardMostRecentPosistion();
052    lastN[index] = elapsed;
053  }
054
055  @Override
056  public double getAverageTime() {
057    return enoughStatistics() ?
058      (double) sum(getNumberOfStatistics()) / getNumberOfStatistics() :
059      (double) sum(getMostRecentPosistion() + 1) / (getMostRecentPosistion() + 1);
060  }
061
062  /**
063   * Check if there are enough statistics.
064   * @return true if lastN is full
065   */
066  protected boolean enoughStatistics() {
067    return oneRound;
068  }
069
070  /**
071   * @return number of statistics
072   */
073  protected int getNumberOfStatistics() {
074    return lastN.length;
075  }
076
077  /**
078   * Get statistics at index.
079   * @param index index of bar
080   * @return statistics
081   */
082  protected long getStatisticsAtIndex(int index) {
083    if (index < 0 || index >= getNumberOfStatistics()) {
084      // This case should not happen, but a prudent check.
085      throw new IndexOutOfBoundsException();
086    }
087    return lastN[index];
088  }
089
090  /**
091   * @return index of most recent
092   */
093  protected int getMostRecentPosistion() {
094    return mostRecent;
095  }
096
097  /**
098   * Move forward the most recent index.
099   * @return the most recent index
100   */
101  protected int moveForwardMostRecentPosistion() {
102    int index = ++mostRecent;
103    if (!oneRound && index == getNumberOfStatistics()) {
104      // Back to the head of the lastN, from now on will
105      // start to evict oldest value.
106      oneRound = true;
107    }
108    mostRecent = index % getNumberOfStatistics();
109    return mostRecent;
110  }
111
112  private long sum(int bound) {
113    long sum = 0;
114    for (int i = 0; i < bound; i++) {
115      sum += getStatisticsAtIndex(i);
116    }
117    return sum;
118  }
119}