Paper/Spigot-Server-Patches/0533-Improve-Chunk-Status-Transition-Speed.patch
Aikar 357b52fd98
Improve Chunk Prioritization / Load Order
1) Improve frustum to look more at the near chunks and frontal chunks only instead of 1 large single look up.
2) Delay adding 33 tickets based on view distance and lower their task priority. This will slower roll out the spiral
3) Chunks behind the player have additional delay on loading, favoring chunks in front of the player.

This has benefit that if faster traveling, some of the chunks will be cancelled / not loaded.

This should reduce pressure on chunk loading, as well as reduce loading/unloading unnecessary chunks while moving.
2020-05-30 03:51:07 -04:00

115 lines
5.8 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Fri, 29 May 2020 23:32:14 -0400
Subject: [PATCH] Improve Chunk Status Transition Speed
When a chunk is loaded from disk that has already been generated,
the server has to promote the chunk through the system to reach
it's current desired status level.
This results in every single status transition going from the main thread
to the world gen threads, only to discover it has no work it actually
needs to do.... and then it returns back to main.
This back and forth costs a lot of time and can really delay chunk loads
when the server is under high TPS due to their being a lot of time in
between chunk load times, as well as hogs up the chunk threads from doing
actual generation and light work.
Additionally, the whole task system uses a lot of CPU on the server threads anyways.
So by optimizing status transitions for status's that are already complete,
we can run them to the desired level while on main thread (where it has
to happen anyways) instead of ever jumping to world gen thread.
This will improve chunk loading effeciency to be reduced down to the following
scenario / path:
1) MAIN: Chunk Requested, Load Request sent to ChunkTaskManager / IO Queue
2) IO: Once position in queue comes, submit read IO data and schedule to chunk task thread
3) CHUNK: Once IO is loaded and position in queue comes, deserialize the chunk data, process conversions, submit to main queue
4) MAIN: next Chunk Task process (Mid Tick or End Of Tick), load chunk data into world (POI, main thread tasks)
5) MAIN: process status transitions all the way to LIGHT, light schedules Threaded task
6) SERVER: Light tasks register light enablement for chunk and any lighting needing to be done
7) MAIN: Task returns to main, finish processing to FULL/TICKING status
Previously would have hopped to SERVER around 12+ times there extra.
diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java
index 885992d5d3bbe175ad4a8d1c875c68a30fe4d315..01b7ac1b26461734d2b812a32fe95168bdc9d5c4 100644
--- a/src/main/java/net/minecraft/server/PlayerChunk.java
+++ b/src/main/java/net/minecraft/server/PlayerChunk.java
@@ -56,6 +56,13 @@ public class PlayerChunk {
this.playersInChunkTickRange = this.chunkMap.playerChunkTickRangeMap.getObjectsInRange(key);
}
// Paper end - optimise isOutsideOfRange
+ // Paper start - optimize chunk status progression without jumping through thread pool
+ public boolean canAdvanceStatus() {
+ ChunkStatus status = getChunkHolderStatus();
+ IChunkAccess chunk = getAvailableChunkNow();
+ return chunk != null && (status == null || chunk.getChunkStatus().isAtLeastStatus(getNextStatus(status)));
+ }
+ // Paper end
// Paper start - no-tick view distance
public final Chunk getSendingChunk() {
diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java
index 519ecdf76ba506f135aa668a3088a16fce9504fc..142d6fac11236e2cfb3730aedf65ae08ae248b25 100644
--- a/src/main/java/net/minecraft/server/PlayerChunkMap.java
+++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java
@@ -88,6 +88,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
public final WorldServer world;
private final LightEngineThreaded lightEngine;
private final IAsyncTaskHandler<Runnable> executor;
+ final java.util.concurrent.Executor mainInvokingExecutor; // Paper
public final ChunkGenerator<?> chunkGenerator;
private final Supplier<WorldPersistentData> l; public final Supplier<WorldPersistentData> getWorldPersistentDataSupplier() { return this.l; } // Paper - OBFHELPER
private final VillagePlace m;
@@ -285,6 +286,15 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
this.world = worldserver;
this.chunkGenerator = chunkgenerator;
this.executor = iasynctaskhandler;
+ // Paper start - optimize chunk status progression without jumping through thread pool
+ this.mainInvokingExecutor = (run) -> {
+ if (MCUtil.isMainThread()) {
+ run.run();
+ } else {
+ iasynctaskhandler.execute(run);
+ }
+ };
+ // Paper end
ThreadedMailbox<Runnable> threadedmailbox = ThreadedMailbox.a(executor, "worldgen");
iasynctaskhandler.getClass();
@@ -708,7 +718,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
return either.mapLeft((list) -> {
return (Chunk) list.get(list.size() / 2);
});
- }, this.executor);
+ }, this.mainInvokingExecutor); // Paper
}
@Nullable
@@ -1074,7 +1084,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
return this.b(playerchunk, chunkstatus);
}
}
- }, this.executor);
+ }, this.mainInvokingExecutor); // Paper - optimize chunk status progression without jumping through thread pool
}
}
@@ -1185,6 +1195,12 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
return CompletableFuture.completedFuture(Either.right(playerchunk_failure));
});
}, (runnable) -> {
+ // Paper start - optimize chunk status progression without jumping through thread pool
+ if (playerchunk.canAdvanceStatus()) {
+ this.mainInvokingExecutor.execute(runnable);
+ return;
+ }
+ // Paper end
this.mailboxWorldGen.a(ChunkTaskQueueSorter.a(playerchunk, runnable)); // CraftBukkit - decompile error
});
}