From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: MrPowerGamerBR Date: Sun, 19 Nov 2023 12:35:16 -0300 Subject: [PATCH] Skip EntityScheduler's executeTick checks if there isn't any tasks to be run On each tick, Paper runs EntityScheduler's executeTick of each entity. This is a bit expensive, due to ArrayDeque's size() call because it ain't a simple "get the current queue size" function, due to the thread checks, and because it needs to iterate all entities in all worlds. To avoid the hefty ArrayDeque's size() call, we check if we *really* need to execute the executeTick, by adding all entities with scheduled tasks to a global set. Most entities won't have any scheduled tasks, so this is a nice performance bonus. These optimizations, however, wouldn't work in a Folia environment, but because in SparklyPaper executeTick is always executed on the main thread, it ain't an issue for us (yay). diff --git a/src/main/java/io/papermc/paper/threadedregions/EntityScheduler.java b/src/main/java/io/papermc/paper/threadedregions/EntityScheduler.java index 62484ebf4550b05182f693a3180bbac5d5fd906d..4c7aa86bd115c0a6dd6fc8fe20be5c7c48215ca2 100644 --- a/src/main/java/io/papermc/paper/threadedregions/EntityScheduler.java +++ b/src/main/java/io/papermc/paper/threadedregions/EntityScheduler.java @@ -36,6 +36,7 @@ public final class EntityScheduler { * The Entity. Note that it is the CraftEntity, since only that class properly tracks world transfers. */ public final CraftEntity entity; + public final net.minecraft.server.MinecraftServer server; // SparklyPaper - skip EntityScheduler's executeTick checks if there isn't any tasks to be run private static final record ScheduledTask(Consumer run, Consumer retired) {} @@ -46,7 +47,8 @@ public final class EntityScheduler { private final ArrayDeque currentlyExecuting = new ArrayDeque<>(); - public EntityScheduler(final CraftEntity entity) { + public EntityScheduler(final net.minecraft.server.MinecraftServer server, final CraftEntity entity) { // SparklyPaper - skip EntityScheduler's executeTick checks if there isn't any tasks to be run + this.server = Validate.notNull(server); this.entity = Validate.notNull(entity); } @@ -66,6 +68,7 @@ public final class EntityScheduler { throw new IllegalStateException("Already retired"); } this.tickCount = RETIRED_TICK_COUNT; + this.server.entitiesWithScheduledTasks.remove(entity); // SparklyPaper - skip EntityScheduler's executeTick checks if there isn't any tasks to be run } final Entity thisEntity = this.entity.getHandleRaw(); @@ -124,6 +127,7 @@ public final class EntityScheduler { if (this.tickCount == RETIRED_TICK_COUNT) { return false; } + this.server.entitiesWithScheduledTasks.add(entity); // SparklyPaper - skip EntityScheduler's executeTick checks if there isn't any tasks to be run this.oneTimeDelayed.computeIfAbsent(this.tickCount + Math.max(1L, delay), (final long keyInMap) -> { return new ArrayList<>(); }).add(task); @@ -143,6 +147,13 @@ public final class EntityScheduler { TickThread.ensureTickThread(thisEntity, "May not tick entity scheduler asynchronously"); final List toRun; synchronized (this.stateLock) { + // SparklyPaper start - skip EntityScheduler's executeTick checks if there isn't any tasks to be run + // Do we *really* have scheduled tasks tho? + if (this.currentlyExecuting.isEmpty() && this.oneTimeDelayed.isEmpty()) { // Check if we have any pending tasks and, if not, skip! + this.server.entitiesWithScheduledTasks.remove(entity); // We don't! Bye bye!! + return; + } + // SparklyPaper end if (this.tickCount == RETIRED_TICK_COUNT) { throw new IllegalStateException("Ticking retired scheduler"); } diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java index 25367df06a8a6e8b0b3a56652a5fb1c70a15632d..e01297d1269e55f4a4f6c43273d194972529645c 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -308,6 +308,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop entitiesWithScheduledTasks = java.util.concurrent.ConcurrentHashMap.newKeySet(); // SparklyPaper - skip EntityScheduler's executeTick checks if there isn't any tasks to be run (concurrent because plugins may schedule tasks async) public static S spin(Function serverFactory) { AtomicReference atomicreference = new AtomicReference(); @@ -1471,6 +1472,15 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { for (final Entity entity : level.getEntityLookup().getAllCopy()) { if (entity.isRemoved()) { @@ -1482,6 +1492,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop