From 2e18115cc5205d7501d3cf894bcdb45f1f398f7f Mon Sep 17 00:00:00 2001 From: Geoff Crossland 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/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java index e6219e027..1db3cbbdd 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -185,6 +185,7 @@ import org.bukkit.event.server.ServerLoadEvent; // CraftBukkit end import org.bukkit.craftbukkit.SpigotTimings; // Spigot +import org.spigotmc.SlackActivityAccountant; // Spigot public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant implements IMojangStatistics, ICommandListener, AutoCloseable { @@ -295,6 +296,7 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant S a(Function function) { @@ -1208,6 +1210,7 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant 2000); longiterator.remove()) { + // Spigot start + org.spigotmc.SlackActivityAccountant activityAccountant = this.level.getMinecraftServer().slackActivityAccountant; + activityAccountant.startActivity(0.5); + int targetSize = (int) (this.toDrop.size() * UNLOAD_QUEUE_RESIZE_FACTOR); + // Spigot end + while (longiterator.hasNext()) { // Spigot long j = longiterator.nextLong(); + longiterator.remove(); // Spigot PlayerChunk playerchunk = (PlayerChunk) this.updatingChunkMap.remove(j); if (playerchunk != null) { this.pendingUnloads.put(j, playerchunk); this.modified = true; - ++i; + // Spigot start + if (!booleansupplier.getAsBoolean() && this.toDrop.size() <= targetSize && activityAccountant.activityTimeIsExhausted()) { + break; + } + // Spigot end this.a(j, playerchunk); } } + activityAccountant.endActivity(); // Spigot Runnable runnable; 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.25.1