spigot/CraftBukkit-Patches/0140-Replace-chunk-loading-unloading-rate-throttling.patch

227 lines
9.4 KiB
Diff

From 8135f1abbc84ae533bfdc7cff553095dbe60dc5a Mon Sep 17 00:00:00 2001
From: Geoff Crossland <gcrossland+bukkit@gmail.com>
Date: Thu, 26 Jan 2017 20:54:55 +0000
Subject: [PATCH] Replace chunk loading / unloading rate throttling.
Uses a scheme based on estimated tick slack time.
See https://hub.spigotmc.org/stash/projects/SPIGOT/repos/spigot/pull-requests/71/overview
diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java
index f0ce5ba7f..9dcab42de 100644
--- a/src/main/java/net/minecraft/server/ChunkProviderServer.java
+++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java
@@ -245,13 +245,22 @@ public class ChunkProviderServer implements IChunkProvider {
this.chunkLoader.c();
}
+ private static final double UNLOAD_QUEUE_RESIZE_FACTOR = 0.96;
+
public boolean unloadChunks() {
if (!this.world.savingDisabled) {
if (!this.unloadQueue.isEmpty()) {
+ // Spigot start
+ org.spigotmc.SlackActivityAccountant activityAccountant = this.world.getMinecraftServer().slackActivityAccountant;
+ activityAccountant.startActivity(0.5);
+ int targetSize = (int) (this.unloadQueue.size() * UNLOAD_QUEUE_RESIZE_FACTOR);
+ // Spigot end
+
Iterator iterator = this.unloadQueue.iterator();
- for (int i = 0; i < 100 && iterator.hasNext(); iterator.remove()) {
+ while (iterator.hasNext()) { // Spigot
Long olong = (Long) iterator.next();
+ iterator.remove(); // Spigot
Chunk chunk = (Chunk) this.chunks.get(olong);
if (chunk != null && chunk.d) {
@@ -261,9 +270,15 @@ public class ChunkProviderServer implements IChunkProvider {
}
// CraftBukkit end
- ++i;
+ // Spigot start
+ if (this.unloadQueue.size() <= targetSize && activityAccountant.activityTimeIsExhausted()) {
+ break;
+ }
+ // Spigot end
}
}
+
+ activityAccountant.endActivity(); // Spigot
}
this.chunkLoader.b();
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index 989b4a175..e396b3822 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -48,6 +48,7 @@ import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.craftbukkit.Main;
// CraftBukkit end
import org.bukkit.craftbukkit.SpigotTimings; // Spigot
+import org.spigotmc.SlackActivityAccountant; // Spigot
public abstract class MinecraftServer implements ICommandListener, Runnable, IAsyncTaskHandler, IMojangStatistics {
@@ -122,6 +123,7 @@ public abstract class MinecraftServer implements ICommandListener, Runnable, IAs
public static final int TICK_TIME = 1000000000 / TPS;
private static final int SAMPLE_INTERVAL = 100;
public final double[] recentTps = new double[ 3 ];
+ public final SlackActivityAccountant slackActivityAccountant = new SlackActivityAccountant();
// Spigot end
public MinecraftServer(OptionSet options, Proxy proxy, DataConverterManager dataconvertermanager, YggdrasilAuthenticationService yggdrasilauthenticationservice, MinecraftSessionService minecraftsessionservice, GameProfileRepository gameprofilerepository, UserCache usercache) {
@@ -662,6 +664,7 @@ public abstract class MinecraftServer implements ICommandListener, Runnable, IAs
protected void C() throws ExceptionWorldConflict { // CraftBukkit - added throws
SpigotTimings.serverTickTimer.startTiming(); // Spigot
+ this.slackActivityAccountant.tickStarted(); // Spigot
long i = System.nanoTime();
++this.ticks;
@@ -707,7 +710,10 @@ public abstract class MinecraftServer implements ICommandListener, Runnable, IAs
}
this.methodProfiler.a("tallying");
- this.h[this.ticks % 100] = System.nanoTime() - i;
+ // Spigot start
+ long tickNanos;
+ this.h[this.ticks % 100] = tickNanos = System.nanoTime() - i;
+ // Spigot end
this.methodProfiler.b();
this.methodProfiler.a("snooper");
if (getSnooperEnabled() && !this.m.d() && this.ticks > 100) { // Spigot
@@ -720,7 +726,9 @@ public abstract class MinecraftServer implements ICommandListener, Runnable, IAs
this.methodProfiler.b();
this.methodProfiler.b();
+
org.spigotmc.WatchdogThread.tick(); // Spigot
+ this.slackActivityAccountant.tickEnded(tickNanos); // Spigot
SpigotTimings.serverTickTimer.stopTiming(); // Spigot
org.spigotmc.CustomTimingsHandler.tick(); // Spigot
}
diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java
index 7e9fc5145..eeac34998 100644
--- a/src/main/java/net/minecraft/server/PlayerChunkMap.java
+++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java
@@ -153,8 +153,11 @@ public class PlayerChunkMap {
}
if (!this.h.isEmpty()) {
- long k = System.nanoTime() + 50000000L;
- int l = 49;
+ // Spigot start
+ org.spigotmc.SlackActivityAccountant activityAccountant = this.world.getMinecraftServer().slackActivityAccountant;
+ activityAccountant.startActivity(0.5);
+ // Spigot end
+
Iterator iterator1 = this.h.iterator();
while (iterator1.hasNext()) {
@@ -169,8 +172,7 @@ public class PlayerChunkMap {
this.g.remove(playerchunk1);
}
- --l;
- if (l < 0 || System.nanoTime() > k) {
+ if (activityAccountant.activityTimeIsExhausted()) { // Spigot
break;
}
}
@@ -180,6 +182,8 @@ public class PlayerChunkMap {
}
// CraftBukkit end
}
+
+ activityAccountant.endActivity(); // Spigot
}
if (!this.g.isEmpty()) {
diff --git a/src/main/java/org/spigotmc/SlackActivityAccountant.java b/src/main/java/org/spigotmc/SlackActivityAccountant.java
new file mode 100644
index 000000000..aabc7ad20
--- /dev/null
+++ b/src/main/java/org/spigotmc/SlackActivityAccountant.java
@@ -0,0 +1,78 @@
+package org.spigotmc;
+
+/**
+ * Keeps track of the time spent doing main thread activities that can be spread across ticks,
+ * so that such work doesn't exceed the current tick's estimated available slack time. Each
+ * activity is allotted a proportion of the expected slack time according to its weight, versus the
+ * estimated total weight of all activities.
+ */
+public class SlackActivityAccountant {
+ private double prevTickSlackWeightReciprocal = 1 / MIN_SLACK_WEIGHT;
+ private static final double MIN_SLACK_WEIGHT = 1 / 65536.0;
+ private double averageTickNonSlackNanos = 0;
+ private static final double AVERAGING_FACTOR = 0.375;
+
+ private long currentActivityStartNanos;
+ private static final long OFF = -1;
+ private long currentActivityEndNanos;
+ private double tickSlackWeight;
+ private long tickSlackNanos;
+
+ private double getSlackFraction(double slackWeight) {
+ return Math.min(slackWeight * this.prevTickSlackWeightReciprocal, 1);
+ }
+
+ private int getEstimatedSlackNanos() {
+ return (int) Math.max(net.minecraft.server.MinecraftServer.TICK_TIME - (long) this.averageTickNonSlackNanos, 0);
+ }
+
+ public void tickStarted() {
+ this.currentActivityStartNanos = OFF;
+ this.tickSlackWeight = 0;
+ this.tickSlackNanos = 0;
+ }
+
+ public void startActivity(double slackWeight) {
+ double slackFraction0 = getSlackFraction(this.tickSlackWeight);
+ this.tickSlackWeight += slackWeight;
+ double slackFraction1 = getSlackFraction(this.tickSlackWeight);
+
+ long t = System.nanoTime();
+ this.currentActivityStartNanos = t;
+ this.currentActivityEndNanos = t + ((int) ((slackFraction1 - slackFraction0) * this.getEstimatedSlackNanos()));
+ }
+
+ private void endActivity(long endNanos) {
+ this.tickSlackNanos += endNanos - this.currentActivityStartNanos;
+ this.currentActivityStartNanos = OFF;
+ }
+
+ public boolean activityTimeIsExhausted() {
+ if (this.currentActivityStartNanos == OFF) {
+ return true;
+ }
+
+ long t = System.nanoTime();
+ if (t <= this.currentActivityEndNanos) {
+ return false;
+ } else {
+ this.endActivity(this.currentActivityEndNanos);
+ return true;
+ }
+ }
+
+ public void endActivity() {
+ if (this.currentActivityStartNanos == OFF) {
+ return;
+ }
+
+ this.endActivity(Math.min(System.nanoTime(), this.currentActivityEndNanos));
+ }
+
+ public void tickEnded(long tickNanos) {
+ this.prevTickSlackWeightReciprocal = 1 / Math.max(this.tickSlackWeight, MIN_SLACK_WEIGHT);
+
+ long tickNonSlackNanos = tickNanos - this.tickSlackNanos;
+ this.averageTickNonSlackNanos = this.averageTickNonSlackNanos * (1 - AVERAGING_FACTOR) + tickNonSlackNanos * AVERAGING_FACTOR;
+ }
+}
--
2.11.0