Skip to content

Commit b5dea46

Browse files
committed
Refactor FabricTPSRetriever
1 parent 01c6aa6 commit b5dea46

File tree

1 file changed

+59
-73
lines changed

1 file changed

+59
-73
lines changed
Lines changed: 59 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,106 +1,92 @@
11
package it.renvins.serverpulse.fabric.metrics;
22

3+
import java.util.LinkedList;
4+
import java.util.Queue;
5+
36
import it.renvins.serverpulse.api.metrics.ITPSRetriever;
4-
import lombok.RequiredArgsConstructor;
57
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
68

7-
@RequiredArgsConstructor
89
public class FabricTPSRetriever implements ITPSRetriever {
910

1011
private static final int TICKS_PER_SECOND = 20;
12+
private static final int ONE_MINUTE_TICKS = 60 * TICKS_PER_SECOND; // 1200
13+
private static final int FIVE_MINUTES_TICKS = 5 * ONE_MINUTE_TICKS; // 6000
14+
private static final int FIFTEEN_MINUTES_TICKS = 15 * ONE_MINUTE_TICKS; // 18000
1115

12-
// Time windows in seconds
13-
private static final int ONE_MINUTE = 60;
14-
private static final int FIVE_MINUTES = 300;
15-
private static final int FIFTEEN_MINUTES = 900;
16-
17-
// Maximum history to keep (15 minutes worth of ticks)
18-
private static final int MAX_SAMPLES = FIFTEEN_MINUTES * TICKS_PER_SECOND;
16+
private static final int MAX_HISTORY_SIZE = FIFTEEN_MINUTES_TICKS;
1917

20-
// Ring buffer for storing tick times
21-
private final long[] tickTimes = new long[MAX_SAMPLES];
22-
private int tickIndex = 0;
23-
private boolean bufferFilled = false;
18+
// Queue for FIFO
19+
private final Queue<Long> tickDurations = new LinkedList<>();
20+
private long lastTickTimeNano = -1;
2421

25-
// Last measurement time
26-
private long lastTickTime = -1;
27-
28-
// Cache for TPS values
2922
private double tps1m = 20.0;
3023
private double tps5m = 20.0;
3124
private double tps15m = 20.0;
3225

3326
@Override
3427
public double[] getTPS() {
35-
return new double[] { tps1m, tps5m, tps15m };
28+
calculateAverages();
29+
return new double[]{tps1m, tps5m, tps15m};
3630
}
3731

3832
public void startTickMonitor() {
39-
lastTickTime = System.nanoTime();
40-
41-
// Use ServerTickEvents instead of a scheduled task
42-
ServerTickEvents.END_SERVER_TICK.register(server -> {
43-
long now = System.nanoTime();
33+
lastTickTimeNano = System.nanoTime();
4434

45-
// Only record after first tick
46-
if (lastTickTime > 0) {
47-
// Record this tick's time in nanoseconds
48-
tickTimes[tickIndex] = now - lastTickTime;
35+
ServerTickEvents.END_SERVER_TICK.register(tick -> {
36+
long currentTimeNano = System.nanoTime();
37+
long elapsedNano = currentTimeNano - lastTickTimeNano;
38+
lastTickTimeNano = currentTimeNano;
4939

50-
// Update index in ring buffer
51-
tickIndex = (tickIndex + 1) % MAX_SAMPLES;
52-
53-
// Mark buffer as filled once we wrap around
54-
if (tickIndex == 0) {
55-
bufferFilled = true;
40+
tickDurations.offer(elapsedNano);
41+
if (tickDurations.size() > MAX_HISTORY_SIZE) {
42+
tickDurations.poll();
5643
}
44+
});
45+
}
5746

58-
// Calculate TPS after recording the tick
59-
updateTPS();
60-
}
47+
private void calculateAverages() {
48+
double sum1m = 0, sum5m = 0, sum15m = 0;
49+
int count1m = 0, count5m = 0, count15m = 0;
6150

62-
lastTickTime = now;
63-
});
64-
}
51+
int i = 0;
52+
Object[] durationsArray = tickDurations.toArray();
6553

66-
private void updateTPS() {
67-
// Convert seconds to ticks
68-
int oneMinTicks = ONE_MINUTE * TICKS_PER_SECOND;
69-
int fiveMinTicks = FIVE_MINUTES * TICKS_PER_SECOND;
70-
int fifteenMinTicks = FIFTEEN_MINUTES * TICKS_PER_SECOND;
54+
for (int j = durationsArray.length - 1; j >= 0; j--) {
55+
if (!(durationsArray[j] instanceof Long)) continue;
56+
long durationNano = (Long) durationsArray[j];
7157

72-
// Calculate the actual number of samples we have
73-
int sampleCount = bufferFilled ? MAX_SAMPLES : tickIndex;
58+
if (i < ONE_MINUTE_TICKS) {
59+
sum1m += durationNano;
60+
count1m++;
61+
}
62+
if (i < FIVE_MINUTES_TICKS) {
63+
sum5m += durationNano;
64+
count5m++;
65+
}
66+
if (i < FIFTEEN_MINUTES_TICKS) {
67+
sum15m += durationNano;
68+
count15m++;
69+
} else {
70+
break;
71+
}
72+
i++;
73+
}
7474

75-
// Calculate TPS for each time window
76-
tps1m = calculateTPS(Math.min(oneMinTicks, sampleCount));
77-
tps5m = calculateTPS(Math.min(fiveMinTicks, sampleCount));
78-
tps15m = calculateTPS(Math.min(fifteenMinTicks, sampleCount));
75+
tps1m = calculateTPSFromAvgNano(sum1m, count1m);
76+
tps5m = calculateTPSFromAvgNano(sum5m, count5m);
77+
tps15m = calculateTPSFromAvgNano(sum15m, count15m);
7978
}
8079

81-
private double calculateTPS(int samples) {
82-
if (samples <= 0) {
83-
return 20.0; // Default to 20 TPS if we have no samples
80+
private double calculateTPSFromAvgNano(double totalNano, int count) {
81+
if (count == 0) {
82+
return 20.0;
8483
}
85-
86-
// Calculate total time for the samples
87-
long totalTime = 0;
88-
89-
// Start from the most recent sample and go backwards
90-
for (int i = 0; i < samples; i++) {
91-
// Calculate index in the circular buffer, accounting for wrap-around
92-
int idx = (tickIndex - 1 - i + MAX_SAMPLES) % MAX_SAMPLES;
93-
totalTime += tickTimes[idx];
84+
double avgTickTimeMillis = (double) totalNano / count / 1_000_000.0;
85+
if (avgTickTimeMillis <= 0) {
86+
return 20.0;
9487
}
95-
96-
// Calculate TPS:
97-
// - totalTime is in nanoseconds for 'samples' ticks
98-
// - Convert to seconds to get time for 'samples' ticks
99-
// - Then calculate how many ticks would occur in 1 second
100-
double timeInSeconds = totalTime / 1_000_000_000.0;
101-
double ticksPerSecond = samples / timeInSeconds;
102-
103-
// Clamp to maximum of 20 TPS
104-
return Math.min(ticksPerSecond, 20.0);
88+
// TPS: 1 second (1000ms) / avg tick time (ms)
89+
double tps = 1000.0 / avgTickTimeMillis;
90+
return Math.min(tps, 20.0);
10591
}
106-
}
92+
}

0 commit comments

Comments
 (0)