mirror of
https://github.com/PaperMC/Paper.git
synced 2024-11-29 22:13:42 +01:00
209 lines
11 KiB
Diff
209 lines
11 KiB
Diff
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||
|
From: Aikar <aikar@aikar.co>
|
||
|
Date: Tue, 1 Mar 2016 23:09:29 -0600
|
||
|
Subject: [PATCH] Further improve server tick loop
|
||
|
|
||
|
Improves how the catchup buffer is handled, allowing it to roll both ways
|
||
|
increasing the effeciency of the thread sleep so it only will sleep once.
|
||
|
|
||
|
Also increases the buffer of the catchup to ensure server stays at 20 TPS unless extreme conditions
|
||
|
|
||
|
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 fa29790600021809f31092a90e1a3a9b84d5e0c4..526d6c0fa45bfba92a3f964f72e4965fd5c841c1 100644
|
||
|
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
||
|
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
||
|
@@ -251,7 +251,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||
|
public org.bukkit.command.ConsoleCommandSender console;
|
||
|
public org.bukkit.command.RemoteConsoleCommandSender remoteConsole;
|
||
|
public ConsoleReader reader;
|
||
|
- public static int currentTick = (int) (System.currentTimeMillis() / 50);
|
||
|
+ public static int currentTick = 0; // Paper - Further improve tick loop
|
||
|
public java.util.Queue<Runnable> processQueue = new java.util.concurrent.ConcurrentLinkedQueue<Runnable>();
|
||
|
public int autosavePeriod;
|
||
|
public Commands vanillaCommandDispatcher;
|
||
|
@@ -260,7 +260,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||
|
// Spigot start
|
||
|
public static final int TPS = 20;
|
||
|
public static final int TICK_TIME = 1000000000 / TPS;
|
||
|
- private static final int SAMPLE_INTERVAL = 100;
|
||
|
+ private static final int SAMPLE_INTERVAL = 20; // Paper
|
||
|
public final double[] recentTps = new double[ 3 ];
|
||
|
public final SlackActivityAccountant slackActivityAccountant = new SlackActivityAccountant();
|
||
|
// Spigot end
|
||
|
@@ -923,6 +923,57 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||
|
{
|
||
|
return ( avg * exp ) + ( tps * ( 1 - exp ) );
|
||
|
}
|
||
|
+
|
||
|
+ // Paper start - Further improve server tick loop
|
||
|
+ private static final long SEC_IN_NANO = 1000000000;
|
||
|
+ private static final long MAX_CATCHUP_BUFFER = TICK_TIME * TPS * 60L;
|
||
|
+ private long lastTick = 0;
|
||
|
+ private long catchupTime = 0;
|
||
|
+ public final RollingAverage tps1 = new RollingAverage(60);
|
||
|
+ public final RollingAverage tps5 = new RollingAverage(60 * 5);
|
||
|
+ public final RollingAverage tps15 = new RollingAverage(60 * 15);
|
||
|
+
|
||
|
+ public static class RollingAverage {
|
||
|
+ private final int size;
|
||
|
+ private long time;
|
||
|
+ private java.math.BigDecimal total;
|
||
|
+ private int index = 0;
|
||
|
+ private final java.math.BigDecimal[] samples;
|
||
|
+ private final long[] times;
|
||
|
+
|
||
|
+ RollingAverage(int size) {
|
||
|
+ this.size = size;
|
||
|
+ this.time = size * SEC_IN_NANO;
|
||
|
+ 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] = dec(TPS);
|
||
|
+ this.times[i] = SEC_IN_NANO;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ 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 = total.subtract(samples[index].multiply(dec(times[index])));
|
||
|
+ samples[index] = x;
|
||
|
+ times[index] = t;
|
||
|
+ time += t;
|
||
|
+ total = total.add(x.multiply(dec(t)));
|
||
|
+ if (++index == size) {
|
||
|
+ index = 0;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ public double getAverage() {
|
||
|
+ 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
|
||
|
|
||
|
protected void runServer() {
|
||
|
@@ -935,30 +986,38 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||
|
|
||
|
// Spigot start
|
||
|
Arrays.fill( recentTps, 20 );
|
||
|
- long curTime, tickSection = Util.getMillis(), tickCount = 1;
|
||
|
+ long start = System.nanoTime(), curTime, tickSection = start; // Paper - Further improve server tick loop
|
||
|
+ lastTick = start - TICK_TIME; // Paper
|
||
|
while (this.running) {
|
||
|
- long i = (curTime = Util.getMillis()) - this.nextTickTime;
|
||
|
+ long i = ((curTime = System.nanoTime()) / (1000L * 1000L)) - this.nextTickTime; // Paper
|
||
|
|
||
|
if (i > 5000L && this.nextTickTime - this.lastOverloadWarning >= 30000L) { // CraftBukkit
|
||
|
long j = i / 50L;
|
||
|
|
||
|
if (server.getWarnOnOverload()) // CraftBukkit
|
||
|
- MinecraftServer.LOGGER.warn("Can't keep up! Is the server overloaded? Running {}ms or {} ticks behind", i, j);
|
||
|
+ MinecraftServer.LOGGER.warn("Can't keep up! Is the server overloaded? Running {}ms or {} ticks behind", i, j);
|
||
|
this.nextTickTime += j * 50L;
|
||
|
this.lastOverloadWarning = this.nextTickTime;
|
||
|
}
|
||
|
|
||
|
- if ( tickCount++ % SAMPLE_INTERVAL == 0 )
|
||
|
+ if ( ++MinecraftServer.currentTick % SAMPLE_INTERVAL == 0 )
|
||
|
{
|
||
|
- double currentTps = 1E3 / ( curTime - tickSection ) * SAMPLE_INTERVAL;
|
||
|
- recentTps[0] = calcTps( recentTps[0], 0.92, currentTps ); // 1/exp(5sec/1min)
|
||
|
- 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;
|
||
|
+ 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);
|
||
|
+ // Backwards compat with bad plugins
|
||
|
+ recentTps[0] = tps1.getAverage();
|
||
|
+ recentTps[1] = tps5.getAverage();
|
||
|
+ recentTps[2] = tps15.getAverage();
|
||
|
+ // Paper end
|
||
|
tickSection = curTime;
|
||
|
}
|
||
|
// Spigot end
|
||
|
|
||
|
- MinecraftServer.currentTick = (int) (System.currentTimeMillis() / 50); // CraftBukkit
|
||
|
+ //MinecraftServer.currentTick = (int) (System.currentTimeMillis() / 50); // CraftBukkit // Paper - don't overwrite current tick time
|
||
|
+ lastTick = curTime;
|
||
|
this.nextTickTime += 50L;
|
||
|
SingleTickProfiler gameprofilertick = SingleTickProfiler.createTickProfiler("Server");
|
||
|
|
||
|
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||
|
index f81def94a1a7ab3a24b74a8bbd5f3e8ebae2c0d5..6fa31ca31128b1094eebd5f848c5b506dfeedeeb 100644
|
||
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||
|
@@ -2121,6 +2121,17 @@ public final class CraftServer implements Server {
|
||
|
return CraftMagicNumbers.INSTANCE;
|
||
|
}
|
||
|
|
||
|
+ // Paper - Add getTPS API - Further improve tick loop
|
||
|
+ @Override
|
||
|
+ public double[] getTPS() {
|
||
|
+ return new double[] {
|
||
|
+ net.minecraft.server.MinecraftServer.getServer().tps1.getAverage(),
|
||
|
+ net.minecraft.server.MinecraftServer.getServer().tps5.getAverage(),
|
||
|
+ net.minecraft.server.MinecraftServer.getServer().tps15.getAverage()
|
||
|
+ };
|
||
|
+ }
|
||
|
+ // Paper end
|
||
|
+
|
||
|
// Spigot start
|
||
|
private final org.bukkit.Server.Spigot spigot = new org.bukkit.Server.Spigot()
|
||
|
{
|
||
|
diff --git a/src/main/java/org/spigotmc/TicksPerSecondCommand.java b/src/main/java/org/spigotmc/TicksPerSecondCommand.java
|
||
|
index f5b6dec1cbe7501ce2ee9125920e810bc94670cc..e62890433ffbe0b4e48942fe6c38b599a19e58fd 100644
|
||
|
--- a/src/main/java/org/spigotmc/TicksPerSecondCommand.java
|
||
|
+++ b/src/main/java/org/spigotmc/TicksPerSecondCommand.java
|
||
|
@@ -24,22 +24,30 @@ public class TicksPerSecondCommand extends Command
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
- StringBuilder sb = new StringBuilder( ChatColor.GOLD + "TPS from last 1m, 5m, 15m: " );
|
||
|
- for ( double tps : MinecraftServer.getServer().recentTps )
|
||
|
- {
|
||
|
- sb.append( format( tps ) );
|
||
|
- sb.append( ", " );
|
||
|
+ // Paper start - Further improve tick handling
|
||
|
+ double[] tps = org.bukkit.Bukkit.getTPS();
|
||
|
+ String[] tpsAvg = new String[tps.length];
|
||
|
+
|
||
|
+ for ( int i = 0; i < tps.length; i++) {
|
||
|
+ tpsAvg[i] = format( tps[i] );
|
||
|
+ }
|
||
|
+ sender.sendMessage(ChatColor.GOLD + "TPS from last 1m, 5m, 15m: " + org.apache.commons.lang.StringUtils.join(tpsAvg, ", "));
|
||
|
+ if (args.length > 0 && args[0].equals("mem") && sender.hasPermission("bukkit.command.tpsmemory")) {
|
||
|
+ sender.sendMessage(ChatColor.GOLD + "Current Memory Usage: " + ChatColor.GREEN + ((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / (1024 * 1024)) + "/" + (Runtime.getRuntime().totalMemory() / (1024 * 1024)) + " mb (Max: " + (Runtime.getRuntime().maxMemory() / (1024 * 1024)) + " mb)");
|
||
|
+ if (!hasShownMemoryWarning) {
|
||
|
+ sender.sendMessage(ChatColor.RED + "Warning: " + ChatColor.GOLD + " Memory usage on modern garbage collectors is not a stable value and it is perfectly normal to see it reach max. Please do not pay it much attention.");
|
||
|
+ hasShownMemoryWarning = true;
|
||
|
+ }
|
||
|
}
|
||
|
- sender.sendMessage( sb.substring( 0, sb.length() - 2 ) );
|
||
|
- sender.sendMessage(ChatColor.GOLD + "Current Memory Usage: " + ChatColor.GREEN + ((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / (1024 * 1024)) + "/" + (Runtime.getRuntime().totalMemory() / (1024 * 1024)) + " mb (Max: "
|
||
|
- + (Runtime.getRuntime().maxMemory() / (1024 * 1024)) + " mb)");
|
||
|
+ // Paper end
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
- private String format(double tps)
|
||
|
+ private boolean hasShownMemoryWarning; // Paper
|
||
|
+ private static String format(double tps) // Paper - Made static
|
||
|
{
|
||
|
return ( ( tps > 18.0 ) ? ChatColor.GREEN : ( tps > 16.0 ) ? ChatColor.YELLOW : ChatColor.RED ).toString()
|
||
|
- + ( ( tps > 20.0 ) ? "*" : "" ) + Math.min( Math.round( tps * 100.0 ) / 100.0, 20.0 );
|
||
|
+ + ( ( tps > 21.0 ) ? "*" : "" ) + Math.min( Math.round( tps * 100.0 ) / 100.0, 20.0 ); // Paper - only print * at 21, we commonly peak to 20.02 as the tick sleep is not accurate enough, stop the noise
|
||
|
}
|
||
|
}
|