Skip to content

Commit

Permalink
More accurately track tick times
Browse files Browse the repository at this point in the history
I had a quick look at doing this per-world too, but I think doing this
in a way which is compatible with Forge and Fabric is tricky.
  • Loading branch information
SquidDev committed Nov 21, 2022
1 parent 3c432e4 commit 4b2e832
Show file tree
Hide file tree
Showing 14 changed files with 131 additions and 17 deletions.
1 change: 1 addition & 0 deletions common/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ minecraft {

dependencies {
compileOnlyApi(libs.jsr305)
compileOnly(libs.mixin)

// Core libraries
implementation(libs.bundles.prometheus)
Expand Down
6 changes: 5 additions & 1 deletion common/src/main/java/cc/tweaked/prometheus/Constants.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package cc.tweaked.prometheus;

public final class Constants {
public static final String NAMESPACE = "computercraft";
public static final String MOD_ID = "ccprometheus";

/**
* The Prometheus namespace for the ComputerCraft-specific metrics.
*/
public static final String COMPUTERCRAFT_NAMESPACE = "computercraft";

private Constants() {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,21 @@
import dan200.computercraft.shared.computer.core.ServerContext;
import io.prometheus.client.Gauge;

import static cc.tweaked.prometheus.Constants.NAMESPACE;
import static cc.tweaked.prometheus.Constants.COMPUTERCRAFT_NAMESPACE;

/**
* Counts the number of computers registered in the {@link ServerComputerRegistry}.
*/
public class ComputerCollector {
public static void register(MetricContext context) {
var totalComputers = Gauge.build()
.namespace(NAMESPACE)
.namespace(COMPUTERCRAFT_NAMESPACE)
.name("computers_total")
.help("Total number of loaded computers")
.register(context.registry());

var onComputers = Gauge.build()
.namespace(NAMESPACE)
.namespace(COMPUTERCRAFT_NAMESPACE)
.name("computers_on")
.help("Total number of computers which are running")
.register(context.registry());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import java.util.Map;
import java.util.Objects;

import static cc.tweaked.prometheus.Constants.NAMESPACE;
import static cc.tweaked.prometheus.Constants.COMPUTERCRAFT_NAMESPACE;

/**
* Reports the values for all {@link Metrics}s.
Expand Down Expand Up @@ -69,7 +69,7 @@ public void observe(ServerComputer computer, Metric.Event event, long value) {
@SuppressWarnings("unchecked")
private static <B extends SimpleCollector.Builder<?, ?>> B buildField(Metric field, B builder) {
return (B) builder
.namespace(NAMESPACE)
.namespace(COMPUTERCRAFT_NAMESPACE)
.name(field.name())
.help(Objects.toString(new AggregatedMetric(field, Aggregate.NONE).displayName()))
.labelNames("computer_id");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@
import java.util.ArrayList;
import java.util.List;

import static cc.tweaked.prometheus.Constants.NAMESPACE;
import static cc.tweaked.prometheus.Constants.COMPUTERCRAFT_NAMESPACE;

/**
* Counts the number of threads active in each of CC:T's thread groups.
*/
public class ThreadGroupCollector extends Collector implements Collector.Describable {
private static final String FULL_NAME = NAMESPACE + "_thread_count";
private static final String FULL_NAME = COMPUTERCRAFT_NAMESPACE + "_thread_count";
private static final String HELP = "Number of currently live threads";
private static final List<String> LABEL_NAMES = List.of("group");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ public static void export(MetricContext context) {
.help("The average tick time as defined by the MC server")
.register(context.registry());

var dimensionTickTime = Histogram.build()
.namespace(NAMESPACE).name("tick_time").unit("s")
.buckets(0.005, 0.01, 0.025, 0.05, 0.10, 0.25, 0.5, 1.0)
.labelNames("dimension")
.help("The average tick time for each dimension")
.register(context.registry());

var playerCount = Gauge.build()
.namespace(NAMESPACE).name("players").unit("count")
.help("The number of players in each dimension.")
Expand All @@ -49,10 +56,30 @@ public static void export(MetricContext context) {
}

totalPlayerCount.set(server.getPlayerCount());
});

// TODO: This doesn't include the time to run Forge/Fabric hooks! Not sure how to handle that in a generic way.
averageTickTime.set(server.getAverageTickTime() * 1e-3); // ms to s
tickTime.observe(server.tickTimes[server.getTickCount() % 100] * 1e-9); // ns to s.
((MinecraftServerTimings) server).prometheus$setTimingObserver((time, averageTime) -> {
averageTickTime.set(averageTime * 1e-3); // ms to s
tickTime.observe(time * 1e-9); // ns to s.
});
}

public interface MinecraftServerTimings {
/**
* Set the object which monitors this server's timings.
*
* @param observer The timings observer.
*/
void prometheus$setTimingObserver(TimingObserver observer);
}

public interface TimingObserver {
/**
* Called when a server finishes its tick.
*
* @param tickTime The time taken for the last tick measured in nanoseconds.
* @param averageTime A smoothed version of tick times, measured in milliseconds.
*/
void onServerTick(long tickTime, float averageTime);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package cc.tweaked.prometheus.mixin;

import cc.tweaked.prometheus.collectors.VanillaCollector;
import net.minecraft.Util;
import net.minecraft.server.MinecraftServer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

/**
* {@link MinecraftServer#tickTimes}} (and the derived calculations) do not include main-thread tick tasks (those queued
* by {@link MinecraftServer#tell(Runnable)}) or from Fabric/Forge's tick events. We use our own mixins to more
* accurately capture this information.
*/
@Mixin(MinecraftServer.class)
class MinecraftServerMixin implements VanillaCollector.MinecraftServerTimings {
@Unique
private VanillaCollector.TimingObserver observer;

@Unique
private float averageTickTime;

@Unique
private long tickStart;

@Inject(at = @At("HEAD"), method = "startMetricsRecordingTick")
private void beforeTick(CallbackInfo ci) {
tickStart = Util.getNanos();
}

@Inject(
method = "waitUntilNextTick",
at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;managedBlock(Ljava/util/function/BooleanSupplier;)V")
)
private void afterTick(CallbackInfo ci) {
// We want to inject here rather than endMetricsRecordingTick, as that also counts the sleep until the next tick.

long time = Util.getNanos() - tickStart;
averageTickTime = averageTickTime * 0.8F + (float) time / 1000000.0F * 0.19999999F;
if (observer != null) observer.onServerTick(time, averageTickTime);
}

@Override
public void prometheus$setTimingObserver(VanillaCollector.TimingObserver observer) {
this.observer = observer;
}
}
12 changes: 12 additions & 0 deletions common/src/main/resources/ccprometheus.mixins.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"required": true,
"package": "cc.tweaked.prometheus.mixin",
"minVersion": "0.8",
"compatibilityLevel": "JAVA_17",
"injectors": {
"defaultRequire": 1
},
"client": [
"MinecraftServerMixin"
]
}
5 changes: 4 additions & 1 deletion fabric/src/main/resources/fabric.mod.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,8 @@
},
"recommends": {
"computercraft": "*"
}
},
"mixins": [
"ccprometheus.mixins.json"
]
}
6 changes: 6 additions & 0 deletions forge/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ plugins {
alias(libs.plugins.shadow)
alias(libs.plugins.forgeGradle)
alias(libs.plugins.librarian)
alias(libs.plugins.mixinGradle)
id("java-convention")
}

Expand Down Expand Up @@ -59,6 +60,11 @@ tasks.jar {
archiveClassifier.set("slim")
}

mixin {
add(sourceSets.main.get(), "ccprometheus.mixins.refmap.json")
config("ccprometheus.mixins.json")
}

reobf {
create("shadowJar")
}
Expand Down
12 changes: 8 additions & 4 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ fabric-loader = "0.14.10"
forge = "43.1.1"
parchment = "2022.10.16"
parchmentMc = "1.19.2"
mixin = "0.8.5"

# Normal dependencies
jsr305 = "3.0.2"
Expand All @@ -23,6 +24,7 @@ forgeGradle = "5.1.+"
librarian = "1.+"
vanillaGradle = "0.2.1-SNAPSHOT"
shadow = "7.1.2"
mixinGradle = "0.7.+"

[libraries]
# Normal dependencies
Expand All @@ -40,18 +42,20 @@ prometheus-tracerOtel = { module = "io.prometheus:simpleclient_tracer_otel", ver
prometheus-tracerOtelAgent = { module = "io.prometheus:simpleclient_tracer_otel_agent", version.ref = "prometheus" }

# Minecraft mods
cct-fabric = { module = "cc.tweaked:cc-tweaked-1.19.2-fabric", version.ref = "cct" }
cct-forge = { module = "cc.tweaked:cc-tweaked-1.19.2-forge", version.ref = "cct" }
fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fabric-api" }
fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-loader" }
forgeConfig = { module = "net.minecraftforge:forgeconfigapiport-fabric", version.ref = "forgeConfig" }
cct-forge = { module = "cc.tweaked:cc-tweaked-1.19.2-forge", version.ref = "cct" }
cct-fabric = { module = "cc.tweaked:cc-tweaked-1.19.2-fabric", version.ref = "cct" }
mixin = { module = "org.spongepowered:mixin", version.ref = "mixin" }

[plugins]
forgeGradle = { id = "net.minecraftforge.gradle", version.ref = "forgeGradle" }
fabric-loom = { id = "fabric-loom", version.ref = "fabric-loom" }
forgeGradle = { id = "net.minecraftforge.gradle", version.ref = "forgeGradle" }
librarian = { id = "org.parchmentmc.librarian.forgegradle", version.ref = "librarian" }
vanillaGradle = { id = "org.spongepowered.gradle.vanilla", version.ref = "vanillaGradle" }
mixinGradle = { id = "org.spongepowered.mixin", version.ref = "mixinGradle" }
shadow = { id = "com.github.johnrengelman.shadow", version.ref = "shadow" }
vanillaGradle = { id = "org.spongepowered.gradle.vanilla", version.ref = "vanillaGradle" }

[bundles]
prometheus = [
Expand Down
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
8 changes: 8 additions & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ pluginManagement {
}
}
}

resolutionStrategy {
eachPlugin {
if (requested.id.id == "org.spongepowered.mixin") {
useModule("org.spongepowered:mixingradle:${requested.version}")
}
}
}
}

rootProject.name = "cc-prometheus"
Expand Down

0 comments on commit 4b2e832

Please sign in to comment.