All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.freeutils.util.Speedometer Maven / Gradle / Ivy

The newest version!
/*
 *  Copyright © 2003-2024 Amichai Rothman
 *
 *  This file is part of JElementary - the Java Elementary Utilities package.
 *
 *  JElementary is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  JElementary is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with JElementary.  If not, see .
 *
 *  For additional info see https://www.freeutils.net/source/jelementary/
 */

package net.freeutils.util;

import java.util.concurrent.atomic.*;

/**
 * The {@code Speedometer} class provides general purpose event firing speed statistics.
 * 

* The Speedometer counts the firing rate of events within consecutive time units, * each of a configurable interval, maintaining a bounded history of such units. * In addition, it maintains a counter for the current unit (which is still in progress). *

* Various speed calculations can then be performed on these statistics to * estimate current or average speed. */ public class Speedometer { private final int interval; private final int units; private final AtomicIntegerArray buckets; private final AtomicInteger current = new AtomicInteger(); private volatile long endTime; /** * Constructs a Speedometer. * * @param interval the duration of a single time unit in milliseconds * @param units the number of recent units to maintain */ public Speedometer(int interval, int units) { this.interval = interval; this.units = units; this.buckets = new AtomicIntegerArray(units); long now = System.currentTimeMillis(); endTime = (now / interval) * interval; // as if it just finished, and properly rounded to bucket boundary } /** * Fire an event. */ public void fire() { update(); current.incrementAndGet(); } /** * Fire multiple events. * * @param delta the number of events to fire */ public void fire(int delta) { update(); current.addAndGet(delta); } /** * Returns the approximate speed in the current time unit. * * @return the approximate speed in the current time unit */ public double getSpeed() { return sumLastUnit(); } /** * Returns the average speed over the specified number of previous units and the current unit. * * @param units the number of previous units to average * @return the average speed * @throws IllegalArgumentException if units is out of range */ public double getAverageSpeed(int units) { if (units < 0 || units > this.units) throw new IllegalArgumentException("units out of range: " + units); long now = update(); int curr = current.get(); int prev = units == this.units ? sumBuckets() : sumBuckets(getBucket(now) - units, units); double partialUnit = (now % interval) / (double) interval; return (prev + curr) / (units + partialUnit); } /** * Returns the average speed over all units. * * @return the average speed */ public double getAverageSpeed() { return getAverageSpeed(units); } /** * Updates the current counter and bucket values to account for elapsed time. * * @return the current time, as used by this method in its calculations */ private long update() { long now = System.currentTimeMillis(); if (now > endTime) { synchronized (this) { if (now > endTime) { // finalize last used bucket int val = current.get(); current.addAndGet(-val); buckets.set(getBucket(endTime), val); endTime += interval; // reset intermediate buckets while (now > endTime) { buckets.set(getBucket(endTime), 0); endTime += interval; } } } } return now; } /** * Returns the index of the bucket corresponding to the given time. * * @param time the time whose corresponding bucket is requested * @return the index of the bucket corresponding to the given time */ private int getBucket(long time) { return (int)((time / interval) % units); } /** * Returns the sum of the values in the given range of buckets. * * @param ind the first bucket index (taken modulo the number of buckets, can be negative) * @param len the number of buckets to include in the sum * @return the sum of the values in the given range of buckets */ private int sumBuckets(int ind, int len) { ind = units + (ind % units); // fix for negative values int sum = 0; for (int i = ind + len - 1; i >= ind; i--) sum += buckets.get(i % units); return sum; } /** * Returns the sum of the values in all buckets. * * @return the sum of the values in all buckets */ private int sumBuckets() { int sum = 0; for (int i = units - 1; i >= 0; i--) sum += buckets.get(i); return sum; } /** * Returns the (approximate) sum in the last unit of time, by adding the current bucket * and an appropriate fraction of the previous bucket. * * @return the sum in the last unit of time */ private double sumLastUnit() { // estimate the last unit by current units and corresponding part or the previous bucket long now = update(); int currEvents = current.get(); // current bucket value double prevEvents = buckets.get(getBucket(now - interval)); // previous bucket value double partialUnit = (now % interval) / (double) interval; // fraction of unit in current bucket return currEvents + (1 - partialUnit) * prevEvents; // current and partial previous buckets } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy