mirror of
https://github.com/PolyhedralDev/Terra.git
synced 2026-06-14 04:41:13 +00:00
refactor API
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
package com.dfsek.terra.profiler;
|
||||
|
||||
/**
|
||||
* Class to hold a profiler data value. Contains formatting method to highlight value based on desired range.
|
||||
*/
|
||||
public class DataHolder {
|
||||
private final long desired;
|
||||
private final DataType type;
|
||||
private final double desiredRangePercent;
|
||||
|
||||
/**
|
||||
* Constructs a DataHolder with a DataType and a desired value, including a percentage around the desired value considered acceptable
|
||||
*
|
||||
* @param type The type of data held in this instance.
|
||||
* @param desired The desired value. This should be the average value of whatever is being measured.
|
||||
* @param desiredRangePercent The percentage around the desired value to be considered acceptable.
|
||||
*/
|
||||
public DataHolder(DataType type, long desired, double desiredRangePercent) {
|
||||
this.desired = desired;
|
||||
this.type = type;
|
||||
this.desiredRangePercent = desiredRangePercent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a String, formatted with Bungee ChatColors.<br>
|
||||
* GREEN if the value is better than desired and outside of acceptable range.<br>
|
||||
* YELLOW if the value is better or worse than desired, and within acceptable range.<br>
|
||||
* RED if the value is worse than desired and outside of acceptable range.<br>
|
||||
*
|
||||
* @param data The data to format.
|
||||
* @return String - The formatted data.
|
||||
*/
|
||||
public String getFormattedData(long data) {
|
||||
return type.getFormatted(data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.dfsek.terra.profiler;
|
||||
|
||||
import net.jafama.FastMath;
|
||||
|
||||
public enum DataType {
|
||||
PERIOD_MILLISECONDS(Desire.LOW, 1000000, "ms"), PERIOD_NANOSECONDS(Desire.LOW, 1, "ns");
|
||||
private final Desire desire;
|
||||
private final long divisor;
|
||||
private final String unit;
|
||||
|
||||
DataType(Desire d, long divisor, String unit) {
|
||||
this.desire = d;
|
||||
this.divisor = divisor;
|
||||
this.unit = unit;
|
||||
}
|
||||
|
||||
public String getFormatted(long value) {
|
||||
return (double) FastMath.round(((double) value / divisor) * 100D) / 100D + unit;
|
||||
}
|
||||
|
||||
public Desire getDesire() {
|
||||
return desire;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.dfsek.terra.profiler;
|
||||
|
||||
|
||||
/**
|
||||
* Enum to represent the "goal" of a value, whether it is desirable for the value to be high (e.g. Frequency), or low (e.g. Period)
|
||||
*/
|
||||
public enum Desire {
|
||||
LOW, HIGH
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.dfsek.terra.profiler;
|
||||
|
||||
import com.dfsek.terra.api.math.MathUtil;
|
||||
import com.dfsek.terra.api.util.GlueList;
|
||||
import net.jafama.FastMath;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Class to record and hold all data for a single type of measurement performed by the profiler.
|
||||
*/
|
||||
public class Measurement {
|
||||
private final List<Long> measurements = new LinkedList<>();
|
||||
private final long desirable;
|
||||
private final DataType type;
|
||||
private long min = Long.MAX_VALUE;
|
||||
private long max = Long.MIN_VALUE;
|
||||
|
||||
/**
|
||||
* Constructs a new Measurement with a desired value and DataType.
|
||||
*
|
||||
* @param desirable The desired value of the measurement.
|
||||
* @param type The type of data the measurement is holding.
|
||||
*/
|
||||
public Measurement(long desirable, DataType type) {
|
||||
this.desirable = desirable;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public void record(long value) {
|
||||
max = FastMath.max(value, max);
|
||||
min = FastMath.min(value, min);
|
||||
measurements.add(value);
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return measurements.size();
|
||||
}
|
||||
|
||||
public ProfileFuture beginMeasurement() {
|
||||
ProfileFuture future = new ProfileFuture();
|
||||
long current = System.nanoTime();
|
||||
future.thenRun(() -> record(System.nanoTime() - current));
|
||||
return future;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
min = Long.MAX_VALUE;
|
||||
max = Long.MIN_VALUE;
|
||||
measurements.clear();
|
||||
}
|
||||
|
||||
public DataHolder getDataHolder() {
|
||||
return new DataHolder(type, desirable, 0.25);
|
||||
}
|
||||
|
||||
public long getMin() {
|
||||
if(min == Long.MAX_VALUE) return 0;
|
||||
return min;
|
||||
}
|
||||
|
||||
public long getMax() {
|
||||
if(max == Long.MIN_VALUE) return 0;
|
||||
return max;
|
||||
}
|
||||
|
||||
public long average() {
|
||||
BigInteger running = BigInteger.valueOf(0);
|
||||
List<Long> mTemp = new GlueList<>(measurements);
|
||||
for(Long l : mTemp) {
|
||||
running = running.add(BigInteger.valueOf(l));
|
||||
}
|
||||
if(measurements.size() == 0) return 0;
|
||||
return running.divide(BigInteger.valueOf(measurements.size())).longValue();
|
||||
}
|
||||
|
||||
public double getStdDev() {
|
||||
return MathUtil.standardDeviation(new GlueList<>(measurements));
|
||||
}
|
||||
|
||||
public int entries() {
|
||||
return measurements.size();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.dfsek.terra.profiler;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class ProfileFuture extends CompletableFuture<Boolean> implements AutoCloseable {
|
||||
public ProfileFuture() {
|
||||
super();
|
||||
}
|
||||
|
||||
public boolean complete() {
|
||||
return super.complete(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
this.complete();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.dfsek.terra.profiler;
|
||||
|
||||
import com.dfsek.terra.api.platform.world.World;
|
||||
import com.google.common.collect.BiMap;
|
||||
import com.google.common.collect.HashBiMap;
|
||||
import net.jafama.FastMath;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class WorldProfiler {
|
||||
private final BiMap<String, Measurement> measures = HashBiMap.create();
|
||||
private final World world;
|
||||
private boolean isProfiling;
|
||||
|
||||
public WorldProfiler(World w) {
|
||||
if(w.getGenerator().getTerraGenerator() == null)
|
||||
throw new IllegalArgumentException("Attempted to instantiate profiler on non-Terra managed world!");
|
||||
this.addMeasurement(new Measurement(2500000, DataType.PERIOD_MILLISECONDS), "TotalChunkGenTime")
|
||||
.addMeasurement(new Measurement(1500000, DataType.PERIOD_MILLISECONDS), "FloraTime")
|
||||
.addMeasurement(new Measurement(10000000, DataType.PERIOD_MILLISECONDS), "TreeTime")
|
||||
.addMeasurement(new Measurement(1500000, DataType.PERIOD_MILLISECONDS), "OreTime")
|
||||
.addMeasurement(new Measurement(5000000, DataType.PERIOD_MILLISECONDS), "CaveTime")
|
||||
.addMeasurement(new Measurement(1500000, DataType.PERIOD_MILLISECONDS), "StructureTime");
|
||||
;
|
||||
isProfiling = false;
|
||||
this.world = w;
|
||||
}
|
||||
|
||||
public String getResultsFormatted() {
|
||||
if(! isProfiling) return "Profiler is not currently running.";
|
||||
StringBuilder result = new StringBuilder("Gaea World Profiler Results (Min / Avg / Max / Std Dev): \n");
|
||||
for(Map.Entry<String, Measurement> e : measures.entrySet()) {
|
||||
result
|
||||
.append(e.getKey())
|
||||
.append(": ")
|
||||
.append(e.getValue().getDataHolder().getFormattedData(e.getValue().getMin()))
|
||||
.append(" / ")
|
||||
.append(e.getValue().getDataHolder().getFormattedData(e.getValue().average()))
|
||||
.append(" / ")
|
||||
.append(e.getValue().getDataHolder().getFormattedData(e.getValue().getMax()))
|
||||
.append(" / ")
|
||||
.append((double) FastMath.round((e.getValue().getStdDev() / 1000000) * 100D) / 100D)
|
||||
.append("ms")
|
||||
.append(" (x").append(e.getValue().size()).append(")\n");
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
for(Map.Entry<String, Measurement> e : measures.entrySet()) {
|
||||
e.getValue().reset();
|
||||
}
|
||||
}
|
||||
|
||||
public com.dfsek.terra.profiler.WorldProfiler addMeasurement(Measurement m, String name) {
|
||||
measures.put(name, m);
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setMeasurement(String id, long value) {
|
||||
if(isProfiling) measures.get(id).record(value);
|
||||
}
|
||||
|
||||
public ProfileFuture measure(String id) {
|
||||
if(isProfiling) return measures.get(id).beginMeasurement();
|
||||
else return null;
|
||||
}
|
||||
|
||||
public String getID(Measurement m) {
|
||||
return measures.inverse().get(m);
|
||||
}
|
||||
|
||||
public boolean isProfiling() {
|
||||
return isProfiling;
|
||||
}
|
||||
|
||||
public void setProfiling(boolean enabled) {
|
||||
this.isProfiling = enabled;
|
||||
}
|
||||
|
||||
public World getWorld() {
|
||||
return world;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user