blob: f8c4c5a97a6f074b6d889ed72132fd041b313217 [file] [log] [blame]
/*-
* Copyright (C) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
*
* This file was distributed by Oracle as part of a version of Oracle Berkeley
* DB Java Edition made available at:
*
* http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html
*
* Please see the LICENSE file included in the top-level directory of the
* appropriate version of Oracle Berkeley DB Java Edition for a copy of the
* license and additional information.
*/
package com.sleepycat.je.utilint;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import com.sleepycat.utilint.FormatUtil;
/**
* A double JE stat component generated from an exponential moving average over
* a specified time period of values supplied with associated times, to support
* averaging values that are generated at irregular intervals.
*/
public class DoubleExpMovingAvg
extends MapStatComponent<Double, DoubleExpMovingAvg> {
private static final long serialVersionUID = 1L;
/** The name of this stat. */
private final String name;
/** The averaging period in milliseconds. */
private final long periodMillis;
/**
* The time in milliseconds specified with the previous value, or 0 if no
* values have been provided. Synchronize on this instance when accessing
* this field.
*/
private long prevTime;
/**
* The current average, or 0 if no values have been provided. Synchronize
* on this instance when accessing this field.
*/
private double avg;
/**
* Creates an instance of this class. The {@code periodMillis} represents
* the time period in milliseconds over which values will be averaged.
*
* @param name the name of this stat
* @param periodMillis the averaging period in milliseconds
*/
public DoubleExpMovingAvg(String name, long periodMillis) {
assert name != null;
assert periodMillis > 0;
this.name = name;
this.periodMillis = periodMillis;
}
/**
* Creates an instance of this class as a copy of another instance.
*
* @param other the other instance to copy
*/
DoubleExpMovingAvg(DoubleExpMovingAvg other) {
name = other.name;
periodMillis = other.periodMillis;
synchronized (this) {
synchronized (other) {
prevTime = other.prevTime;
avg = other.avg;
}
}
}
/**
* Returns the name of this stat.
*
* @return the name of this stat
*/
public String getName() {
return name;
}
/**
* Adds a new value to the average, ignoring values that are not newer than
* time of the previous call.
*
* @param value the new value
* @param time the current time in milliseconds
*/
public synchronized void add(double value, long time) {
assert time > 0;
if (time <= prevTime) {
return;
}
if (prevTime == 0) {
avg = value;
} else {
/*
* Compute the exponential moving average, as described in:
* http://en.wikipedia.org/wiki/
* Moving_average#Application_to_measuring_computer_performance
*/
double m = Math.exp(-((time - prevTime)/((double) periodMillis)));
avg = ((1-m) * value) + (m * avg);
}
prevTime = time;
}
/**
* Add the values from another average.
*
* @param other the other average
*/
public void add(DoubleExpMovingAvg other) {
final double otherValue;
final long otherTime;
synchronized (other) {
if (other.isNotSet()) {
return;
}
otherValue = other.avg;
otherTime = other.prevTime;
}
add(otherValue, otherTime);
}
/** Returns the current average as a primitive value. */
synchronized double getPrimitive() {
return avg;
}
/** Returns the current average, or 0 if no values have been added. */
@Override
public Double get() {
return getPrimitive();
}
@Override
public synchronized void clear() {
prevTime = 0;
avg = 0;
}
@Override
public DoubleExpMovingAvg copy() {
return new DoubleExpMovingAvg(this);
}
@Override
protected synchronized String getFormattedValue(boolean useCommas) {
if (isNotSet()) {
return "unknown";
} else if (Double.isNaN(avg)) {
return "NaN";
} else if (useCommas) {
return FormatUtil.decimalScale2().format(avg);
} else {
return String.format("%.2f", avg);
}
}
@Override
public synchronized boolean isNotSet() {
return prevTime == 0;
}
@Override
public synchronized String toString() {
return "DoubleExpMovingAvg[name=" + name + ", avg=" + avg +
", prevTime=" + prevTime + ", periodMillis=" + periodMillis + "]";
}
/** Synchronize access to fields. */
private synchronized void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException {
in.defaultReadObject();
}
/** Synchronize access to fields. */
private synchronized void writeObject(ObjectOutputStream out)
throws IOException {
out.defaultWriteObject();
}
}