Update Optimise chunk tick iteration (#6950)

This commit is contained in:
Jake Potrebic 2021-12-05 06:00:13 -08:00 committed by GitHub
parent c3ff7e0ff7
commit fd4f6c5e31
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 111 additions and 108 deletions

View File

@ -1,94 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <spottedleaf@spottedleaf.dev>
Date: Thu, 7 May 2020 05:48:54 -0700
Subject: [PATCH] Optimise chunk tick iteration
Use a dedicated list of entity ticking chunks to reduce the cost
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
index b5c8f3f57d09de4caffeb9f3e20e9bf4daba1cdd..d6981bbcf480c5856b51960013d144beba2361b3 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -1007,19 +1007,35 @@ public class ServerChunkCache extends ChunkSource {
this.lastSpawnState = spawnercreature_d;
this.level.getProfiler().pop();
- List<ChunkHolder> list = Lists.newArrayList(this.chunkMap.getChunks());
-
- Collections.shuffle(list);
+ // Paper - moved down, enabled if per-player = false
// Paper - moved natural spawn event up
this.level.timings.chunkTicks.startTiming(); // Paper
- list.forEach((playerchunk) -> {
- Optional<LevelChunk> optional = ((Either) playerchunk.getTickingChunkFuture().getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK)).left();
-
- if (optional.isPresent()) {
- LevelChunk chunk = (LevelChunk) optional.get();
+ // Paper start
+ java.util.Iterator<LevelChunk> iterator;
+ if (this.level.paperConfig.perPlayerMobSpawns) {
+ iterator = this.entityTickingChunks.iterator();
+ } else {
+ iterator = this.entityTickingChunks.unsafeIterator();
+ List<LevelChunk> shuffled = new java.util.ArrayList<>(this.entityTickingChunks.size());
+ while (iterator.hasNext()) {
+ shuffled.add(iterator.next());
+ }
+ Collections.shuffle(shuffled);
+ iterator = shuffled.iterator();
+ }
+ try { while (iterator.hasNext()) {
+ LevelChunk chunk = iterator.next();
+ ChunkHolder playerchunk = chunk.playerChunk;
+ if (playerchunk != null) {
+ this.level.getProfiler().push("broadcast");
+ this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timings
+ playerchunk.broadcastChanges(chunk);
+ this.level.timings.broadcastChunkUpdates.stopTiming(); // Paper - timings
+ this.level.getProfiler().pop();
+ // Paper end
ChunkPos chunkcoordintpair = chunk.getPos();
- if (this.level.isPositionEntityTicking(chunkcoordintpair) && !this.chunkMap.isOutsideOfRange(playerchunk, chunkcoordintpair, false)) { // Paper - optimise isOutsideOfRange
+ if ((true || this.level.isPositionEntityTicking(chunkcoordintpair)) && !this.chunkMap.isOutsideOfRange(playerchunk, chunkcoordintpair, false)) { // Paper - optimise isOutsideOfRange // Paper - we only iterate entity ticking chunks
chunk.setInhabitedTime(chunk.getInhabitedTime() + j);
if (flag1 && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunk.getPos()) && !this.chunkMap.isOutsideOfRange(playerchunk, chunkcoordintpair, true)) { // Spigot // Paper - optimise isOutsideOfRange
NaturalSpawner.spawnForChunk(this.level, chunk, spawnercreature_d, this.spawnFriendlies, this.spawnEnemies, flag2);
@@ -1030,7 +1046,13 @@ public class ServerChunkCache extends ChunkSource {
// this.level.timings.doTickTiles.stopTiming(); // Spigot // Paper
}
}
- });
+ } // Paper start - optimise chunk tick iteration
+ } finally {
+ if (iterator instanceof io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.Iterator) {
+ ((io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.Iterator<LevelChunk>)iterator).finishedIterating();
+ }
+ }
+ // Paper end - optimise chunk tick iteration
this.level.timings.chunkTicks.stopTiming(); // Paper
this.level.getProfiler().push("customSpawners");
if (flag1) {
@@ -1039,21 +1061,7 @@ public class ServerChunkCache extends ChunkSource {
} // Paper - timings
}
- this.level.getProfiler().popPush("broadcast");
- this.chunkMap.getChunks().forEach((playerchunk) -> { // Paper - no... just no...
- Optional<LevelChunk> optional = ((Either) playerchunk.getTickingChunkFuture().getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK)).left(); // CraftBukkit - decompile error
-
- Objects.requireNonNull(playerchunk);
-
- // Paper start - timings
- optional.ifPresent(chunk -> {
- this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timings
- playerchunk.broadcastChanges(chunk);
- this.level.timings.broadcastChunkUpdates.stopTiming(); // Paper - timings
- });
- // Paper end
- });
- this.level.getProfiler().pop();
+ // Paper - no, iterating just ONCE is expensive enough! Don't do it TWICE! Code moved up
this.level.getProfiler().pop();
}

View File

@ -5477,7 +5477,7 @@ index ef87c37633cee4ab438f1991144188ade1c4e65f..19d3802becd353e130b785f8286e595e
protected void purgeStaleTickets() {
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
index d672c467267ef4d96184e104c35d0379116880db..566c32a71054479257b7fba981507fa2559aa9e6 100644
index d672c467267ef4d96184e104c35d0379116880db..be11caf7c0dcdb11e24cfb053cda45ac1867da63 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -48,6 +48,8 @@ import net.minecraft.world.level.storage.LevelData;
@ -5874,15 +5874,6 @@ index d672c467267ef4d96184e104c35d0379116880db..566c32a71054479257b7fba981507fa2
@Nullable
@Override
public ChunkAccess getChunk(int x, int z, ChunkStatus leastStatus, boolean create) {
@@ -444,7 +817,7 @@ public class ServerChunkCache extends ChunkSource {
gameprofilerfiller.popPush("spawnAndTick");
boolean flag2 = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit
- Collections.shuffle(list);
+ //Collections.shuffle(list); // Paper - no... just no...
Iterator iterator1 = list.iterator();
while (iterator1.hasNext()) {
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index 9812116e2f28d01423373990ecce0e5ab6229ce2..e66b4837498a8bfefabc4dd95e91ccfba67843e4 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java

View File

@ -40,13 +40,13 @@ index e954365c9fe7a5f442c4ab31ab8c8db9684409e8..fb9a82b4fabdb73e9a44bcbc20b848d8
return true;
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
index 109d6638d8fe035498bd717c180c9143ff00e0ad..9090bc174c0ccb542616e756be66967f55f0a626 100644
index 8f5e9f1bdfd2e02512e1341f91b147b16e959e5b..87c9a7ffc69206554cf37c7d2c9939eb3cbea3a9 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -827,6 +827,15 @@ public class ServerChunkCache extends ChunkSource {
boolean flag2 = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit
//Collections.shuffle(list); // Paper - no... just no...
Collections.shuffle(list);
+ // Paper start - call player naturally spawn event
+ int chunkRange = level.spigotConfig.mobSpawnRange;
+ chunkRange = (chunkRange > level.spigotConfig.viewDistance) ? (byte) level.spigotConfig.viewDistance : chunkRange;

View File

@ -272,7 +272,7 @@ index 8868ffcda194e8c2300181a2cdda9337dbde6284..95f195980e28bb59f43e5ca1d5e79ebe
public String getDebugStatus() {
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
index 3c36dfa4f4c6025b02ec455d997f13226de75a64..76b56ea346d843aba482c52c4bfe877fdf0e9225 100644
index 41d2027cd4cf8f5de7bd59283361f7f1075356cb..24d0b02264e4cced08a60f36b5c41bb350a1dc60 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -881,6 +881,37 @@ public class ServerChunkCache extends ChunkSource {
@ -316,7 +316,7 @@ index 3c36dfa4f4c6025b02ec455d997f13226de75a64..76b56ea346d843aba482c52c4bfe877f
@@ -928,15 +959,7 @@ public class ServerChunkCache extends ChunkSource {
boolean flag2 = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit
//Collections.shuffle(list); // Paper - no... just no...
Collections.shuffle(list);
- // Paper start - call player naturally spawn event
- int chunkRange = level.spigotConfig.mobSpawnRange;
- chunkRange = (chunkRange > level.spigotConfig.viewDistance) ? (byte) level.spigotConfig.viewDistance : chunkRange;

View File

@ -0,0 +1,106 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <spottedleaf@spottedleaf.dev>
Date: Thu, 7 May 2020 05:48:54 -0700
Subject: [PATCH] Optimise chunk tick iteration
Use a dedicated list of entity ticking chunks to reduce the cost
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
index 51c4ca36221e9af074fa92f6ab94fa7ba5403080..ca16bde30da84e79fef856304ada9e3b8bfb9806 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -1001,34 +1001,46 @@ public class ServerChunkCache extends ChunkSource {
this.lastSpawnState = spawnercreature_d;
gameprofilerfiller.popPush("filteringLoadedChunks");
- List<ServerChunkCache.ChunkAndHolder> list = Lists.newArrayListWithCapacity(l);
- Iterator iterator = this.chunkMap.getChunks().iterator();
+ // Paper - moved down
this.level.timings.chunkTicks.startTiming(); // Paper
- while (iterator.hasNext()) {
- ChunkHolder playerchunk = (ChunkHolder) iterator.next();
- LevelChunk chunk = playerchunk.getTickingChunk();
-
- if (chunk != null) {
- list.add(new ServerChunkCache.ChunkAndHolder(chunk, playerchunk));
- }
- }
+ // Paper - moved down
gameprofilerfiller.popPush("spawnAndTick");
boolean flag2 = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit
- Collections.shuffle(list);
+ // Paper - only shuffle if per-player mob spawning is disabled
// Paper - moved natural spawn event up
- Iterator iterator1 = list.iterator();
+ // Paper start - optimise chunk tick iteration
+ Iterator<LevelChunk> iterator1;
+ if (this.level.paperConfig.perPlayerMobSpawns) {
+ iterator1 = this.entityTickingChunks.iterator();
+ } else {
+ iterator1 = this.entityTickingChunks.unsafeIterator();
+ List<LevelChunk> shuffled = Lists.newArrayListWithCapacity(this.entityTickingChunks.size());
+ while (iterator1.hasNext()) {
+ shuffled.add(iterator1.next());
+ }
+ Collections.shuffle(shuffled);
+ iterator1 = shuffled.iterator();
+ }
+ try {
while (iterator1.hasNext()) {
- ServerChunkCache.ChunkAndHolder chunkproviderserver_a = (ServerChunkCache.ChunkAndHolder) iterator1.next();
- LevelChunk chunk1 = chunkproviderserver_a.chunk;
+ LevelChunk chunk1 = iterator1.next();
+ ChunkHolder holder = chunk1.playerChunk;
+ if (holder != null) {
+ gameprofilerfiller.popPush("broadcast");
+ this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timing
+ holder.broadcastChanges(chunk1);
+ this.level.timings.broadcastChunkUpdates.stopTiming(); // Paper - timing
+ gameprofilerfiller.pop();
+ // Paper end - optimise chunk tick iteration
ChunkPos chunkcoordintpair = chunk1.getPos();
- if (this.level.isPositionEntityTicking(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(chunkproviderserver_a.holder, chunkcoordintpair, false)) { // Paper - optimise anyPlayerCloseEnoughForSpawning
+ if ((true || this.level.isPositionEntityTicking(chunkcoordintpair)) && this.chunkMap.anyPlayerCloseEnoughForSpawning(holder, chunkcoordintpair, false)) { // Paper - optimise anyPlayerCloseEnoughForSpawning & optimise chunk tick iteration
chunk1.incrementInhabitedTime(j);
- if (flag2 && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(chunkproviderserver_a.holder, chunkcoordintpair, true)) { // Spigot // Paper - optimise anyPlayerCloseEnoughForSpawning
+ if (flag2 && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(holder, chunkcoordintpair, true)) { // Spigot // Paper - optimise anyPlayerCloseEnoughForSpawning & optimise chunk tick iteration
NaturalSpawner.spawnForChunk(this.level, chunk1, spawnercreature_d, this.spawnFriendlies, this.spawnEnemies, flag1);
}
@@ -1036,7 +1048,16 @@ public class ServerChunkCache extends ChunkSource {
this.level.tickChunk(chunk1, k);
}
}
+ // Paper start - optimise chunk tick iteration
+ }
}
+
+ } finally {
+ if (iterator1 instanceof io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.Iterator safeIterator) {
+ safeIterator.finishedIterating();
+ }
+ }
+ // Paper end - optimise chunk tick iteration
this.level.timings.chunkTicks.stopTiming(); // Paper
gameprofilerfiller.popPush("customSpawners");
if (flag2) {
@@ -1045,13 +1066,7 @@ public class ServerChunkCache extends ChunkSource {
} // Paper - timings
}
- gameprofilerfiller.popPush("broadcast");
- list.forEach((chunkproviderserver_a1) -> {
- this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timing
- chunkproviderserver_a1.holder.broadcastChanges(chunkproviderserver_a1.chunk);
- this.level.timings.broadcastChunkUpdates.stopTiming(); // Paper - timing
- });
- gameprofilerfiller.pop();
+ // Paper - no, iterating just ONCE is expensive enough! Don't do it TWICE! Code moved up
gameprofilerfiller.pop();
// Paper start - controlled flush for entity tracker packets
List<net.minecraft.network.Connection> disabledFlushes = new java.util.ArrayList<>(this.level.players.size());