From 960d992fd2ed5e8c573ab3ac71c24a162ecec9d2 Mon Sep 17 00:00:00 2001 From: Aikar Date: Wed, 29 Jul 2020 01:51:57 -0400 Subject: [PATCH] Further improve Chunk Light Prioritization This is as fast as its going to get outside of making the light engine calculations it self faster. Fixes #4022 --- .../0505-Optimize-Light-Engine.patch | 122 +++++++++++------- 1 file changed, 72 insertions(+), 50 deletions(-) diff --git a/Spigot-Server-Patches/0505-Optimize-Light-Engine.patch b/Spigot-Server-Patches/0505-Optimize-Light-Engine.patch index 5ecf8c4b4b..f7bbb509b1 100644 --- a/Spigot-Server-Patches/0505-Optimize-Light-Engine.patch +++ b/Spigot-Server-Patches/0505-Optimize-Light-Engine.patch @@ -1027,10 +1027,10 @@ index a35e7b392c74fadf2760d1fc2021e98d33858cb5..944094e8e770cc8c0205ef2aa6c48fff lightenginelayer.a(Long.MAX_VALUE, l3, 15, false); } diff --git a/src/main/java/net/minecraft/server/LightEngineThreaded.java b/src/main/java/net/minecraft/server/LightEngineThreaded.java -index a9dc8466278f9ec2becbcb643e6e1c973df72b82..97c41fa1306798dad08ad9797c61f13d0dff6f52 100644 +index a9dc8466278f9ec2becbcb643e6e1c973df72b82..1e70754529847e077f88af01c087aba6a887c039 100644 --- a/src/main/java/net/minecraft/server/LightEngineThreaded.java +++ b/src/main/java/net/minecraft/server/LightEngineThreaded.java -@@ -15,15 +15,119 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { +@@ -15,15 +15,153 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { private static final Logger LOGGER = LogManager.getLogger(); private final ThreadedMailbox b; @@ -1038,20 +1038,6 @@ index a9dc8466278f9ec2becbcb643e6e1c973df72b82..97c41fa1306798dad08ad9797c61f13d - private final PlayerChunkMap d; + // Paper start + private static final int MAX_PRIORITIES = PlayerChunkMap.GOLDEN_TICKET + 2; -+ private final java.util.concurrent.ConcurrentLinkedQueue priorityChanges = new java.util.concurrent.ConcurrentLinkedQueue<>(); -+ -+ public void changePriority(long pair, int currentPriority, int priority) { -+ this.priorityChanges.add(() -> { -+ ChunkLightQueue remove = this.queue.buckets[currentPriority].remove(pair); -+ if (remove != null) { -+ ChunkLightQueue existing = this.queue.buckets[priority].put(pair, remove); -+ if (existing != null) { -+ remove.pre.addAll(existing.pre); -+ remove.post.addAll(existing.post); -+ } -+ } -+ }); -+ } + + private boolean isChunkLightStatus(long pair) { + PlayerChunk playerChunk = playerChunkMap.getUpdatingChunk(pair); @@ -1070,12 +1056,28 @@ index a9dc8466278f9ec2becbcb643e6e1c973df72b82..97c41fa1306798dad08ad9797c61f13d + ChunkLightQueue(long chunk) {} + } + ++ static class PendingChunkLight { ++ long chunkId; ++ int priority; ++ Runnable pre; ++ Runnable post; ++ ++ public PendingChunkLight(long chunkId, int priority, Runnable pre, Runnable post) { ++ this.chunkId = chunkId; ++ this.priority = priority; ++ this.pre = pre; ++ this.post = post; ++ } ++ } ++ + + // Retain the chunks priority level for queued light tasks -+ private class LightQueue { ++ class LightQueue { + private int size = 0; + private int lowestPriority = MAX_PRIORITIES; + private final it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap[] buckets = new it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap[MAX_PRIORITIES]; ++ private final java.util.concurrent.ConcurrentLinkedQueue pendingChunks = new java.util.concurrent.ConcurrentLinkedQueue<>(); ++ private final java.util.concurrent.ConcurrentLinkedQueue priorityChanges = new java.util.concurrent.ConcurrentLinkedQueue<>(); + + private LightQueue() { + for (int i = 0; i < buckets.length; i++) { @@ -1083,6 +1085,29 @@ index a9dc8466278f9ec2becbcb643e6e1c973df72b82..97c41fa1306798dad08ad9797c61f13d + } + } + ++ public void changePriority(long pair, int currentPriority, int priority) { ++ this.priorityChanges.add(() -> { ++ ChunkLightQueue remove = this.buckets[currentPriority].remove(pair); ++ if (remove != null) { ++ ChunkLightQueue existing = this.buckets[priority].put(pair, remove); ++ if (existing != null) { ++ remove.pre.addAll(existing.pre); ++ remove.post.addAll(existing.post); ++ } ++ } ++ if (this.buckets[priority].containsKey(pair)) { ++ if (lowestPriority > priority) { ++ lowestPriority = priority; ++ } ++ } ++ }); ++ } ++ ++ public final void addChunk(long chunkId, int priority, Runnable pre, Runnable post) { ++ pendingChunks.add(new PendingChunkLight(chunkId, priority, pre, post)); ++ queueUpdate(); ++ } ++ + public final void add(long chunkId, int priority, LightEngineThreaded.Update type, Runnable run) { + add(chunkId, priority, type, run, false); + } @@ -1104,7 +1129,7 @@ index a9dc8466278f9ec2becbcb643e6e1c973df72b82..97c41fa1306798dad08ad9797c61f13d + } + + public final boolean isEmpty() { -+ return this.size == 0; ++ return this.size == 0 && this.pendingChunks.isEmpty(); + } + + public final int size() { @@ -1112,6 +1137,11 @@ index a9dc8466278f9ec2becbcb643e6e1c973df72b82..97c41fa1306798dad08ad9797c61f13d + } + + public boolean poll(java.util.List pre, java.util.List post) { ++ PendingChunkLight chunk; ++ while ((chunk = pendingChunks.poll()) != null) { ++ add(chunk.chunkId, chunk.priority, Update.PRE_UPDATE, chunk.pre, true); ++ add(chunk.chunkId, chunk.priority, Update.POST_UPDATE, chunk.post, true); ++ } + Runnable run; + while ((run = priorityChanges.poll()) != null) { + run.run(); @@ -1122,7 +1152,11 @@ index a9dc8466278f9ec2becbcb643e6e1c973df72b82..97c41fa1306798dad08ad9797c61f13d + it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap bucket = buckets[lowestPriority]; + if (bucket.isEmpty()) { + lowestPriority++; -+ continue; ++ if (hasWork && lowestPriority <= 3) { ++ return true; ++ } else { ++ continue; ++ } + } + ChunkLightQueue queue = bucket.removeFirst(); + this.size -= queue.pre.size() + queue.post.size(); @@ -1139,7 +1173,7 @@ index a9dc8466278f9ec2becbcb643e6e1c973df72b82..97c41fa1306798dad08ad9797c61f13d + } + } + -+ private final LightQueue queue = new LightQueue(); ++ final LightQueue queue = new LightQueue(); + // Paper end + private final PlayerChunkMap d; private final PlayerChunkMap playerChunkMap; // Paper private final Mailbox> e; @@ -1153,7 +1187,7 @@ index a9dc8466278f9ec2becbcb643e6e1c973df72b82..97c41fa1306798dad08ad9797c61f13d this.e = mailbox; this.b = threadedmailbox; } -@@ -111,10 +215,10 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { +@@ -111,10 +249,10 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { private void a(int i, int j, IntSupplier intsupplier, LightEngineThreaded.Update lightenginethreaded_update, Runnable runnable) { this.e.a(ChunkTaskQueueSorter.a(() -> { @@ -1168,7 +1202,7 @@ index a9dc8466278f9ec2becbcb643e6e1c973df72b82..97c41fa1306798dad08ad9797c61f13d }, ChunkCoordIntPair.pair(i, j), intsupplier)); } -@@ -133,8 +237,27 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { +@@ -133,8 +271,21 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { public CompletableFuture a(IChunkAccess ichunkaccess, boolean flag) { ChunkCoordIntPair chunkcoordintpair = ichunkaccess.getPos(); @@ -1179,26 +1213,20 @@ index a9dc8466278f9ec2becbcb643e6e1c973df72b82..97c41fa1306798dad08ad9797c61f13d + long pair = chunkcoordintpair.pair(); + CompletableFuture future = new CompletableFuture<>(); + IntSupplier prioritySupplier = playerChunkMap.getPrioritySupplier(pair); -+ this.e.a(ChunkTaskQueueSorter.a(() -> { -+ // Chunk's no longer needed ++ boolean[] skippedPre = {false}; ++ int priority = prioritySupplier.getAsInt(); ++ this.queue.addChunk(pair, priority, SystemUtils.a(() -> { + if (!isChunkLightStatus(pair)) { + this.d.c(chunkcoordintpair); // copied from end of method to release light ticket + future.complete(ichunkaccess); ++ skippedPre[0] = true; + return; + } -+ boolean[] skippedPre = {false}; -+ this.queue.add(pair, prioritySupplier.getAsInt(), LightEngineThreaded.Update.PRE_UPDATE, SystemUtils.a(() -> { -+ if (!isChunkLightStatus(pair)) { -+ this.d.c(chunkcoordintpair); // copied from end of method to release light ticket -+ future.complete(ichunkaccess); -+ skippedPre[0] = true; -+ return; -+ } -+ // Paper end ++ // Paper end ChunkSection[] achunksection = ichunkaccess.getSections(); for (int i = 0; i < 16; ++i) { -@@ -152,55 +275,55 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { +@@ -152,55 +303,47 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { }); } @@ -1206,11 +1234,10 @@ index a9dc8466278f9ec2becbcb643e6e1c973df72b82..97c41fa1306798dad08ad9797c61f13d + this.d.c(chunkcoordintpair); // Paper - if change, copy into !isChunkLightStatus above }, () -> { return "lightChunk " + chunkcoordintpair + " " + flag; -+ // Paper start - merge the 2 together - })); +- })); - return CompletableFuture.supplyAsync(() -> { -+ -+ this.queue.add(pair, prioritySupplier.getAsInt(), LightEngineThreaded.Update.POST_UPDATE, () -> { ++ // Paper start - merge the 2 together ++ }), () -> { + if (skippedPre[0]) return; // Paper - future's already complete ichunkaccess.b(true); super.b(chunkcoordintpair, false); @@ -1220,8 +1247,6 @@ index a9dc8466278f9ec2becbcb643e6e1c973df72b82..97c41fa1306798dad08ad9797c61f13d + // Paper start + future.complete(ichunkaccess); }); -+ queueUpdate(); // run queue now -+ }, pair, prioritySupplier)); + return future; + // Paper end } @@ -1253,15 +1278,15 @@ index a9dc8466278f9ec2becbcb643e6e1c973df72b82..97c41fa1306798dad08ad9797c61f13d - if (pair.getFirst() == LightEngineThreaded.Update.PRE_UPDATE) { - ((Runnable) pair.getSecond()).run(); - } -+ int i = Math.min(queue.size(), 4); -+ boolean ran = false; -+ while (i-- > 0 && queue.poll(pre, post)) { ++ if (queue.poll(pre, post)) { + pre.forEach(Runnable::run); + pre.clear(); + super.a(Integer.MAX_VALUE, true, true); + post.forEach(Runnable::run); + post.clear(); -+ ran = true; ++ } else { ++ // might have level updates to go still ++ super.a(Integer.MAX_VALUE, true, true); } - - objectlistiterator.back(j); @@ -1274,10 +1299,7 @@ index a9dc8466278f9ec2becbcb643e6e1c973df72b82..97c41fa1306798dad08ad9797c61f13d - } - - objectlistiterator.remove(); -+ if (!ran) { -+ // might have level updates to go still -+ super.a(Integer.MAX_VALUE, true, true); - } +- } - + // Paper end } @@ -1302,14 +1324,14 @@ index 8cedfdd820cc02a76607b53e0b054fc74654f907..a9795394c9b17f9f0ce4c4f9c8f51a48 private static final int nibbleBucketSizeMultiplier = Integer.getInteger("Paper.nibbleBucketSize", 3072); private static final int maxPoolSize = Integer.getInteger("Paper.maxNibblePoolSize", (int) Math.min(6, Math.max(1, Runtime.getRuntime().maxMemory() / 1024 / 1024 / 1024)) * (nibbleBucketSizeMultiplier * 8)); diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java -index 446c401b3139f8c6c0e70d883340f0140d94b752..a3bce8f13bf278af2d6870891daa9bf692b4e267 100644 +index 446c401b3139f8c6c0e70d883340f0140d94b752..c14cdb60243e16810ad711d204678d51120c3403 100644 --- a/src/main/java/net/minecraft/server/PlayerChunk.java +++ b/src/main/java/net/minecraft/server/PlayerChunk.java @@ -716,6 +716,7 @@ public class PlayerChunk { ioPriority = com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGH_PRIORITY; } chunkMap.world.asyncChunkTaskManager.raisePriority(location.x, location.z, ioPriority); -+ chunkMap.world.getChunkProvider().getLightEngine().changePriority(location.pair(), getCurrentPriority(), priority); ++ chunkMap.world.getChunkProvider().getLightEngine().queue.changePriority(location.pair(), getCurrentPriority(), priority); } if (getCurrentPriority() != priority) { this.v.a(this.location, this::getCurrentPriority, priority, this::setPriority); // use preferred priority