mysten_common/decay_moving_average.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0
/// A moving average that decays over time so that the average value
/// skews towards the newer values over time.
///
/// The decay factor is a value between 0 and 1 that determines how much of the previous value
/// is kept when updating the average.
///
/// A decay factor of 0 means that the average is completely replaced with the new value every time,
/// and a decay factor of 1 means that the average never changes (keeps the old value).
///
/// ## Choosing Decay Factors for Different Behaviors
///
/// **Lower decay factor (closer to 0.0):**
/// - Adapts quickly to new values, making the average more responsive to recent changes
/// - Outliers are "forgotten" faster, providing better tolerance to temporary spikes or anomalies
/// - Useful for tracking recent trends where you want to react quickly to changes
/// - Example: `0.1` - heavily weights recent values, good for responsive latency tracking
///
/// **Higher decay factor (closer to 1.0):**
/// - Changes slowly and retains more historical information
/// - Outliers have a longer-lasting impact on the average, making them more visible
/// - Provides more stable tracking that's less sensitive to temporary fluctuations
/// - Example: `0.9` - heavily weights historical values, good for stable baseline tracking
///
/// When using this to track moving average of latency, it is important that
/// there should be a cap on the maximum value that can be stored.
#[derive(Debug, Clone)]
pub struct DecayMovingAverage {
value: f64,
decay_factor: f64,
}
impl DecayMovingAverage {
/// Create a new DecayMovingAverage with an initial value and decay factor.
///
/// # Arguments
/// * `init_value` - The initial value for the moving average
/// * `decay_factor` - A value between 0.0 and 1.0 that controls how much historical data is retained.
/// Lower values (e.g., 0.1) make the average more responsive to recent values and better at
/// forgetting outliers. Higher values (e.g., 0.9) make the average more stable but outliers
/// will have a longer-lasting impact.
pub fn new(init_value: f64, decay_factor: f64) -> Self {
assert!(
decay_factor > 0.0 && decay_factor < 1.0,
"Decay factor must be between 0 and 1"
);
Self {
value: init_value,
decay_factor,
}
}
/// Update the moving average with a new value.
///
/// The new value is weighted by (1 - decay_factor), and the previous value
/// is weighted by decay_factor, so that the average value skews towards
/// the newer values over time.
pub fn update_moving_average(&mut self, value: f64) {
self.value = self.value * self.decay_factor + value * (1.0 - self.decay_factor);
}
/// Override the moving average with a new value, bypassing the decay calculation.
///
/// Unlike `update_moving_average()`, this method immediately sets the average to the new value
/// rather than blending it with the previous value using the decay factor.
///
/// This is particularly useful for implementing patterns like "decay moving max":
/// - Track the maximum value seen recently, but let it decay over time if no new maxima occur
/// - When a new maximum is encountered, immediately jump to that value using `override_moving_average()`
/// - For regular updates below the maximum, use `update_moving_average()` to let the value decay naturally
///
/// # Example: Decay Moving Max
/// ```
/// fn update_moving_max(new_value: f64) {
/// use mysten_common::decay_moving_average::DecayMovingAverage;
/// let mut decay_max = DecayMovingAverage::new(0.0, 0.9);
///
/// // New maximum encountered - immediately jump to it
/// if new_value > decay_max.get() {
/// decay_max.override_moving_average(new_value);
/// } else {
/// // Let the maximum decay naturally toward the current value
/// decay_max.update_moving_average(new_value);
/// }
/// }
/// ```
pub fn override_moving_average(&mut self, value: f64) {
self.value = value;
}
/// Get the current value of the moving average.
pub fn get(&self) -> f64 {
self.value
}
}