Paper/patches/removed/1.19.2-legacy-chunksystem/0482-Improve-Chunk-Status-Transition-Speed.patch
Spottedleaf 01a13871de
Rewrite chunk system (#8177)
Patch documentation to come

Issues with the old system that are fixed now:
- World generation does not scale with cpu cores effectively.
- Relies on the main thread for scheduling and maintaining chunk state, dropping chunk load/generate rates at lower tps.
- Unreliable prioritisation of chunk gen/load calls that block the main thread.
- Shutdown logic is utterly unreliable, as it has to wait for all chunks to unload - is it guaranteed that the chunk system is in a state on shutdown that it can reliably do this? Watchdog shutdown also typically failed due to thread checks, which is now resolved.
- Saving of data is not unified (i.e can save chunk data without saving entity data, poses problems for desync if shutdown is really abnormal.
- Entities are not loaded with chunks. This caused quite a bit of headache for Chunk#getEntities API, but now the new chunk system loads entities with chunks so that they are ready whenever the chunk loads in. Effectively brings the behavior back to 1.16 era, but still storing entities in their own separate regionfiles.

The above list is not complete. The patch documentation will complete it.

New chunk system hard relies on starlight and dataconverter, and most importantly the new concurrent utilities in ConcurrentUtil.

Some of the old async chunk i/o interface (i.e the old file io thread reroutes _some_ calls to the new file io thread) is kept for plugin compat reasons. It will be removed in the next major version of minecraft.

The old legacy chunk system patches have been moved to the removed folder in case we need them again.
2022-09-26 01:02:51 -07:00

82 lines
4.4 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/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java
index ac42029596ae0c824bf33a4058ac1009740e29ea..a699107b1afea1c52e5a7e93af8f39ae9e1955b3 100644
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
@@ -101,6 +101,13 @@ public class ChunkHolder {
// Paper end - optimise anyPlayerCloseEnoughForSpawning
long lastAutoSaveTime; // Paper - incremental autosave
long inactiveTimeStart; // Paper - incremental autosave
+ // Paper start - optimize chunk status progression without jumping through thread pool
+ public boolean canAdvanceStatus() {
+ ChunkStatus status = getChunkHolderStatus();
+ ChunkAccess chunk = getAvailableChunkNow();
+ return chunk != null && (status == null || chunk.getStatus().isOrAfter(getNextStatus(status)));
+ }
+ // Paper end
public ChunkHolder(ChunkPos pos, int level, LevelHeightAccessor world, LevelLightEngine lightingProvider, ChunkHolder.LevelChangeListener levelUpdateListener, ChunkHolder.PlayerProvider playersWatchingChunkProvider) {
this.futures = new AtomicReferenceArray(ChunkHolder.CHUNK_STATUSES.size());
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index 7493da0f1c3f8ab0ebc517347ef23fbe2747a306..5a78ee69748b2b7b57a9adcff0a4718b1cc0c4ea 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -726,7 +726,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
return either.mapLeft((list) -> {
return (LevelChunk) list.get(list.size() / 2);
});
- }, this.mainThreadExecutor);
+ }, this.mainInvokingExecutor); // Paper
}
@Nullable
@@ -1144,6 +1144,12 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
return "chunkGenerate " + requiredStatus.getName();
});
Executor executor = (runnable) -> {
+ // Paper start - optimize chunk status progression without jumping through thread pool
+ if (holder.canAdvanceStatus()) {
+ this.mainInvokingExecutor.execute(runnable);
+ return;
+ }
+ // Paper end
this.worldgenMailbox.tell(ChunkTaskPriorityQueueSorter.message(holder, runnable));
};