/*
* Copyright (c) 2015 NOVA, All rights reserved.
* This library is free software, licensed under GNU Lesser General Public License version 3
*
* This file is part of NOVA.
*
* NOVA 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
* (at your option) any later version.
*
* NOVA 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 NOVA. If not, see <http://www.gnu.org/licenses/>.
*/package nova.core.util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* A simple stop watch profiler. All results are in microseconds.
*/
public final class Profiler {
/**
* Name of this profiler
*/
public final String name;
private final List<Double> lapped = new ArrayList<>();
private long time = -1;
private double lastTime = 0;
/**
* Creates new profiler.
*
* @param name of this profiler.
*/
public Profiler(String name) {
this.name = name;
}
/**
* Starts this profiler.
*
* @return {@code this} for chaining.
* @throws IllegalStateException if profiler is currently running. See {@link Profiler#isRunning()}
*/
public Profiler start() {
assureRightState("start", false);
time = System.nanoTime();
return this;
}
/**
* Ends current profiling session. Saves result for average calculations.
*
* @return time elapsed since last start or lap measured in microseconds.
* @throws IllegalStateException if profiler is not currently running. See {@link Profiler#isRunning()}
*/
public double end() {
assureRightState("end", true);
lastTime = elapsed();
lapped.add(lastTime);
time = -1;
return lastTime;
}
/**
* Helper method for loop time measurements.
* Class {@link Profiler#end()} and then {@link Profiler#start()}
*
* @return time in microseconds previous lap took.
* @throws IllegalStateException if profiler is not currently running. See {@link Profiler#isRunning()}
*/
public double lap() {
assureRightState("lap", true);
end();
start();
return lastTime;
}
/**
* States whether profiler is currently performing time measurements.
*
* @return {@code true} if profiler is running.
*/
public boolean isRunning() {
return time != -1;
}
/**
* Measures time since last (lap)start of this profiler.
*
* @return time elapsed since last start or lap measured in microseconds.
* @throws IllegalStateException if profiler is not currently running. See {@link Profiler#isRunning()}
*/
public double elapsed() {
assureRightState("elapsed", true);
return (System.nanoTime() - time) / 1e9d;
}
/**
* Getter for last cycle time.
*
* @return time in seconds between last start-lap/lap-lap/lap-end or start-end cycle.
*/
public double lastTime() {
return lastTime;
}
/**
* Calculates average time between cycles.
*
* @return average time in microseconds.
*/
public double average() {
return lapped.stream().mapToDouble(Double::doubleValue).sum() / lapped.size();
}
/**
* Getter for lap timing.
*
* @return unmodifiable list of elapsed times between cycles.
*/
public List<Double> results() {
return Collections.unmodifiableList(lapped);
}
/**
* Clears saved results for average computations.
*
* @return this for chaining.
*/
public Profiler clearResults() {
lapped.clear();
return this;
}
@Override
public String toString() {
return name + " took " + lastTime() + " seconds";
}
private void assureRightState(String method, boolean shouldBeRunning) {
if (shouldBeRunning ^ isRunning()) {
throw new IllegalStateException(String.format("The profiler's method: <%s> was called while it %s running.", method, isRunning() ? "was" : "wasn't"));
}
}
}