minecraft-src/net/minecraft/util/profiling/metrics/MetricSampler.java
2025-07-04 01:41:11 +03:00

211 lines
5.8 KiB
Java

package net.minecraft.util.profiling.metrics;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import it.unimi.dsi.fastutil.ints.Int2DoubleMap;
import it.unimi.dsi.fastutil.ints.Int2DoubleOpenHashMap;
import java.util.Locale;
import java.util.function.Consumer;
import java.util.function.DoubleSupplier;
import java.util.function.ToDoubleFunction;
import org.jetbrains.annotations.Nullable;
public class MetricSampler {
private final String name;
private final MetricCategory category;
private final DoubleSupplier sampler;
private final ByteBuf ticks;
private final ByteBuf values;
private volatile boolean isRunning;
@Nullable
private final Runnable beforeTick;
@Nullable
final MetricSampler.ThresholdTest thresholdTest;
private double currentValue;
protected MetricSampler(
String name, MetricCategory category, DoubleSupplier sampler, @Nullable Runnable beforeTick, @Nullable MetricSampler.ThresholdTest thresholdTest
) {
this.name = name;
this.category = category;
this.beforeTick = beforeTick;
this.sampler = sampler;
this.thresholdTest = thresholdTest;
this.values = ByteBufAllocator.DEFAULT.buffer();
this.ticks = ByteBufAllocator.DEFAULT.buffer();
this.isRunning = true;
}
public static MetricSampler create(String name, MetricCategory category, DoubleSupplier sampler) {
return new MetricSampler(name, category, sampler, null, null);
}
public static <T> MetricSampler create(String name, MetricCategory category, T context, ToDoubleFunction<T> sampler) {
return builder(name, category, sampler, context).build();
}
public static <T> MetricSampler.MetricSamplerBuilder<T> builder(String name, MetricCategory category, ToDoubleFunction<T> sampler, T context) {
return new MetricSampler.MetricSamplerBuilder<>(name, category, sampler, context);
}
public void onStartTick() {
if (!this.isRunning) {
throw new IllegalStateException("Not running");
} else {
if (this.beforeTick != null) {
this.beforeTick.run();
}
}
}
public void onEndTick(int tickTime) {
this.verifyRunning();
this.currentValue = this.sampler.getAsDouble();
this.values.writeDouble(this.currentValue);
this.ticks.writeInt(tickTime);
}
public void onFinished() {
this.verifyRunning();
this.values.release();
this.ticks.release();
this.isRunning = false;
}
private void verifyRunning() {
if (!this.isRunning) {
throw new IllegalStateException(String.format(Locale.ROOT, "Sampler for metric %s not started!", this.name));
}
}
DoubleSupplier getSampler() {
return this.sampler;
}
public String getName() {
return this.name;
}
public MetricCategory getCategory() {
return this.category;
}
public MetricSampler.SamplerResult result() {
Int2DoubleMap int2DoubleMap = new Int2DoubleOpenHashMap();
int i = Integer.MIN_VALUE;
int j = Integer.MIN_VALUE;
while (this.values.isReadable(8)) {
int k = this.ticks.readInt();
if (i == Integer.MIN_VALUE) {
i = k;
}
int2DoubleMap.put(k, this.values.readDouble());
j = k;
}
return new MetricSampler.SamplerResult(i, j, int2DoubleMap);
}
public boolean triggersThreshold() {
return this.thresholdTest != null && this.thresholdTest.test(this.currentValue);
}
public boolean equals(Object object) {
if (this == object) {
return true;
} else if (object != null && this.getClass() == object.getClass()) {
MetricSampler metricSampler = (MetricSampler)object;
return this.name.equals(metricSampler.name) && this.category.equals(metricSampler.category);
} else {
return false;
}
}
public int hashCode() {
return this.name.hashCode();
}
public static class MetricSamplerBuilder<T> {
private final String name;
private final MetricCategory category;
private final DoubleSupplier sampler;
private final T context;
@Nullable
private Runnable beforeTick;
@Nullable
private MetricSampler.ThresholdTest thresholdTest;
public MetricSamplerBuilder(String name, MetricCategory category, ToDoubleFunction<T> sampler, T context) {
this.name = name;
this.category = category;
this.sampler = () -> sampler.applyAsDouble(context);
this.context = context;
}
public MetricSampler.MetricSamplerBuilder<T> withBeforeTick(Consumer<T> beforeTick) {
this.beforeTick = () -> beforeTick.accept(this.context);
return this;
}
public MetricSampler.MetricSamplerBuilder<T> withThresholdAlert(MetricSampler.ThresholdTest thresholdTest) {
this.thresholdTest = thresholdTest;
return this;
}
public MetricSampler build() {
return new MetricSampler(this.name, this.category, this.sampler, this.beforeTick, this.thresholdTest);
}
}
public static class SamplerResult {
private final Int2DoubleMap recording;
private final int firstTick;
private final int lastTick;
public SamplerResult(int firstTick, int lastTick, Int2DoubleMap recording) {
this.firstTick = firstTick;
this.lastTick = lastTick;
this.recording = recording;
}
public double valueAtTick(int tick) {
return this.recording.get(tick);
}
public int getFirstTick() {
return this.firstTick;
}
public int getLastTick() {
return this.lastTick;
}
}
public interface ThresholdTest {
boolean test(double value);
}
public static class ValueIncreasedByPercentage implements MetricSampler.ThresholdTest {
private final float percentageIncreaseThreshold;
private double previousValue = Double.MIN_VALUE;
public ValueIncreasedByPercentage(float percentageIncreaseThreshold) {
this.percentageIncreaseThreshold = percentageIncreaseThreshold;
}
@Override
public boolean test(double d) {
boolean bl;
if (this.previousValue != Double.MIN_VALUE && !(d <= this.previousValue)) {
bl = (d - this.previousValue) / this.previousValue >= this.percentageIncreaseThreshold;
} else {
bl = false;
}
this.previousValue = d;
return bl;
}
}
}