Use BigDecimal to improve accracy of TPS results - long live 20 TPS!

It's always been commonly said to 'ignore' that TPS was '19.X', that
it was fine.

I suspect that the inaccuracy of floating point math resulted in us
losing precision over time, making it difficult to actually get back to 20,
as you know the fun 0.1 + 0.1 ... 9 more times != 1 problem.

BigDecimal supports working with doubles with higher precision.

This change makes it so our RollingAverage class maintains all of the data
using BigDecimal and using BigDecimal arithematic operations.

This ensures we have extremely high precision, enabling us to
actually be able print '20 TPS' when TPS is perfect.
This commit is contained in:
Aikar
2018-09-17 22:32:37 -04:00
parent af737481c2
commit bf7742bf96
11 changed files with 31 additions and 27 deletions

View File

@@ -12,7 +12,7 @@ Previous implementation did not calculate TPS correctly.
Switch to a realistic rolling average and factor in std deviation as an extra reporting variable
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index 788f0519a8..13c6021ffa 100644
index 788f0519a8..d6ea4ae532 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -0,0 +0,0 @@ public abstract class MinecraftServer implements IAsyncTaskHandler, IMojangStati
@@ -59,39 +59,43 @@ index 788f0519a8..13c6021ffa 100644
+ public static class RollingAverage {
+ private final int size;
+ private long time;
+ private double total;
+ private java.math.BigDecimal total;
+ private int index = 0;
+ private final double[] samples;
+ private final java.math.BigDecimal[] samples;
+ private final long[] times;
+
+ RollingAverage(int size) {
+ this.size = size;
+ this.time = size * SEC_IN_NANO;
+ this.total = TPS * SEC_IN_NANO * size;
+ this.samples = new double[size];
+ this.total = dec(TPS).multiply(dec(SEC_IN_NANO)).multiply(dec(size));
+ this.samples = new java.math.BigDecimal[size];
+ this.times = new long[size];
+ for (int i = 0; i < size; i++) {
+ this.samples[i] = TPS;
+ this.samples[i] = dec(TPS);
+ this.times[i] = SEC_IN_NANO;
+ }
+ }
+
+ public void add(double x, long t) {
+ private static java.math.BigDecimal dec(long t) {
+ return new java.math.BigDecimal(t);
+ }
+ public void add(java.math.BigDecimal x, long t) {
+ time -= times[index];
+ total -= samples[index] * times[index];
+ total = total.subtract(samples[index].multiply(dec(times[index])));
+ samples[index] = x;
+ times[index] = t;
+ time += t;
+ total += x * t;
+ total = total.add(x.multiply(dec(t)));
+ if (++index == size) {
+ index = 0;
+ }
+ }
+
+ public double getAverage() {
+ return total / time;
+ return total.divide(dec(time), 30, java.math.RoundingMode.HALF_UP).doubleValue();
+ }
+ }
+ private static final java.math.BigDecimal TPS_BASE = new java.math.BigDecimal(1E9).multiply(new java.math.BigDecimal(SAMPLE_INTERVAL));
+ // Paper End
// Spigot End
@@ -138,7 +142,7 @@ index 788f0519a8..13c6021ffa 100644
- recentTps[1] = calcTps( recentTps[1], 0.9835, currentTps ); // 1/exp(5sec/5min)
- recentTps[2] = calcTps( recentTps[2], 0.9945, currentTps ); // 1/exp(5sec/15min)
+ final long diff = curTime - tickSection;
+ double currentTps = 1E9 / diff * SAMPLE_INTERVAL;
+ java.math.BigDecimal currentTps = TPS_BASE.divide(new java.math.BigDecimal(diff), 30, java.math.RoundingMode.HALF_UP);
+ tps1.add(currentTps, diff);
+ tps5.add(currentTps, diff);
+ tps15.add(currentTps, diff);