TPS.java

/*
 *                      ..::jDrawingLib::..
 *
 * Copyright (C) Federico Vera 2012 - 2023 <[email protected]>
 *
 * This program 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 3 of the License, or any later
 * version.
 *
 * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.dkt.graphics.utils;

/**
 * This class assists in calculating how many times <i>something</i> happens
 * on a second or to calculate the mean time between calls.<br>
 * Please note that this class was designed to be used for debugging or counting
 * small things, when the action is executed more than 1e15 times the error in
 * the total average {@link TPS#tps()} is very big. Also, this class uses
 * {@link System#nanoTime()} so if the elapsed time between the calls to
 * {@link TPS#action()} is very big, it might yield wrong results
 *
 * @author Federico Vera {@literal<[email protected]>}
 */
public class TPS {
    private final int SIZE = 50;
    private final long[] times = new long[SIZE];
    private double average;
    private long lastTime;
    private int i = -1;

    /**
     * This method is the one that tells the {@code TPS} instance that an action
     * has been performed. It should be called every time the action is executed
     * in order for it to compute the times.
     */
    public void action(){
        final long curr = System.nanoTime();

        if (lastTime == 0){
            lastTime = System.nanoTime();
            return;
        }

        final long delta = curr - lastTime;
        final int idx = ++i;

        times[(idx % SIZE)] = delta;

        if (i == 0){
            average = delta;
        } else {
            average += (delta - average) / idx;
        }

        lastTime = System.nanoTime();
    }

    /**
     * Retrieves the number of times per second that the action method has
     * been called
     *
     * @return times per second
     * @see TPS#ctps()
     */
    public double tps(){
        return 1e9 / average;
    }

    /**
     * Retrieves the number of times per second that the action method has
     * been called, considering ONLY the last 50 times
     *
     * @return times per second (last 50 times)
     * @see TPS#tps()
     */
    public double ctps(){
        return 1e9 / cAvgNanos();
    }

    /**
     * Retrieves the average time in ms between calls to {@link TPS#action()}
     *
     * @return average time in ms
     */
    public double avgMilis(){
        return average / 1e6;
    }

    /**
     * Retrieves the average time in µs between calls to {@link TPS#action()}
     *
     * @return average time in µs
     */
    public double avgMicros(){
        return average / 1e3;
    }

    /**
     * Retrieves the average time in ns between calls to {@link TPS#action()}
     *
     * @return average time in ns
     */
    public double avgNanos(){
        return average;
    }

    /**
     * Retrieves the average time in ms between calls to {@link TPS#action()},
     * considering only the last 50 calls
     *
     * @return average time in ms
     */
    public double cAvgMilis(){
        return cAvgNanos() / 1e6;
    }

    /**
     * Retrieves the average time in µs between calls to {@link TPS#action()},
     * considering only the last 50 calls
     *
     * @return average time in µs
     */
    public double cAvgMicros(){
        return cAvgNanos() / 1e3;
    }

    /**
     * Retrieves the average time in ns between calls to {@link TPS#action()},
     * considering only the last 50 calls
     *
     * @return average time in ns
     */
    public long cAvgNanos(){
        return avg(times, Math.min(i, SIZE));
    }

    /**
     * Resets the current object to its original state
     */
    public void reset(){
        lastTime = 0;
        average = 0;
        i = -1;
    }

    private static long avg(long[] ary, int n) {
        long avg = 0;
        int t = 1;

        for (int i = 0; i < n; i++) {
            avg += (ary[i] - avg) / t;
            ++t;
        }

        return avg;
    }

    @Override
    public String toString(){
        return String.format("%d actions avg ~%6.3fms", i, avgMilis());
    }
}