From 6b4c6a766fa1ee659f25f12ee3422adcd1d0ba0a Mon Sep 17 00:00:00 2001 From: MrPowerGamerBR Date: Thu, 23 Nov 2023 19:36:09 -0300 Subject: [PATCH] Cache the block entity's chunk coordinate key in the block entity itself Again, this is seems like a small optimization, but it is actually useful because we avoid doing calculations for each block entity, and if your server has a lot of them, it adds up --- README.md | 1 + ...dTickBlocksAt-result-when-ticking-bl.patch | 87 ++++++++++++++++++- .../server/0019-Parallel-world-ticking.patch | 6 +- 3 files changed, 88 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index bbac276..77a97a3 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,7 @@ SparklyPaper's config file is `sparklypaper.yml`, the file is, by default, place * We could use a map for caching, but here's why this is way better than using a map: The block entity ticking list is sorted by chunks! Well, sort of... It is sorted by chunk when the chunk has been loaded *because* all loaded block entities are appended to the list, however, this also means that newly placed blocks will be appended to the end of the list until the chunk unloads and loads again. * But here's the thing: We don't care if this is bad, the small performance hit of when a player placed new block entities is so small ('tis just a long comparsion after all), that the huge performance boost from already placed block entities is way bigger. * Most block entities are things that players placed to be there for a long time anyway (like hoppers, etc), and the block entities will be automatically sorted after the chunk is unloaded and loaded again, so it ain't that bad. + * We also cache the chunk's coordinate key when creating the block entity, which is actually "free" because we just reuse the already cached chunk coordinate key from the chunk! * Check how much MSPT (milliseconds per tick) each world is using in `/mspt` * Useful to figure out which worlds are lagging your server. ![Per World MSPT](docs/per-world-mspt.png) diff --git a/patches/server/0017-Cache-last-shouldTickBlocksAt-result-when-ticking-bl.patch b/patches/server/0017-Cache-last-shouldTickBlocksAt-result-when-ticking-bl.patch index ce36880..2613e9b 100644 --- a/patches/server/0017-Cache-last-shouldTickBlocksAt-result-when-ticking-bl.patch +++ b/patches/server/0017-Cache-last-shouldTickBlocksAt-result-when-ticking-bl.patch @@ -14,8 +14,10 @@ But here's the thing: We don't care if this is bad, the small performance hit of Most block entities are things that players placed to be there for a long time anyway (like hoppers, etc) +We also cache the chunk's coordinate key when creating the block entity, which is actually "free" because we just reuse the already cached chunk coordinate key from the chunk! + diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index b9e0822638a3979bd43392efdb595153e6f34675..95bc1fab201f5869bb811e25e51ad9c94e955e6e 100644 +index b9e0822638a3979bd43392efdb595153e6f34675..14936213cf2a9f0d7f4d0fbf7387e6d97c6cf717 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java @@ -1272,6 +1272,10 @@ public abstract class Level implements LevelAccessor, AutoCloseable { @@ -34,9 +36,9 @@ index b9e0822638a3979bd43392efdb595153e6f34675..95bc1fab201f5869bb811e25e51ad9c9 toRemove.add(tickingblockentity); // Paper - use removeAll // Spigot end - } else if (this.shouldTickBlocksAt(tickingblockentity.getPos())) { -+ // } else if (this.shouldTickBlocksAt(tickingblockentity.getPos())) { // SparklyPaper - cache last shouldTickBlocksAt result when ticking block entities ++ // } else if (this.shouldTickBlocksAt(tickingblockentity.getPos())) { // SparklyPaper start - cache last shouldTickBlocksAt result when ticking block entities + } else { -+ long chunkPos = ChunkPos.asLong(tickingblockentity.getPos()); ++ long chunkPos = tickingblockentity.getChunkCoordinateKey(); + boolean shouldTick; + if (shouldTickBlocksAtLastResult != -1 && shouldTickBlocksAtChunkPos == chunkPos) { + shouldTick = shouldTickBlocksAtLastResult == 1; @@ -56,3 +58,82 @@ index b9e0822638a3979bd43392efdb595153e6f34675..95bc1fab201f5869bb811e25e51ad9c9 } } this.blockEntityTickers.removeAll(toRemove); +diff --git a/src/main/java/net/minecraft/world/level/block/entity/TickingBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/TickingBlockEntity.java +index 28e3b73507b988f7234cbf29c4024c88180d0aef..68d96dd3e288346d8df49b52fa035e8154057065 100644 +--- a/src/main/java/net/minecraft/world/level/block/entity/TickingBlockEntity.java ++++ b/src/main/java/net/minecraft/world/level/block/entity/TickingBlockEntity.java +@@ -10,4 +10,6 @@ public interface TickingBlockEntity { + BlockPos getPos(); + + String getType(); ++ ++ long getChunkCoordinateKey(); // SparklyPaper - cache last shouldTickBlocksAt result when ticking block entities + } +diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +index fa170cc1ce7011d201295b89718292d696c7fc24..c6f62c56da6e74fbaa57300f8cc2718675d1681e 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java ++++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +@@ -73,6 +73,13 @@ public class LevelChunk extends ChunkAccess { + public String getType() { + return ""; + } ++ ++ // SparklyPaper start - cache last shouldTickBlocksAt result when ticking block entities ++ @Override ++ public long getChunkCoordinateKey() { ++ return 0; ++ } ++ // SparklyPaper end + }; + private final Map tickersInLevel; + public boolean loaded; +@@ -1090,7 +1097,7 @@ public class LevelChunk extends ChunkAccess { + } + + private TickingBlockEntity createTicker(T blockEntity, BlockEntityTicker blockEntityTicker) { +- return new LevelChunk.BoundTickingBlockEntity<>(blockEntity, blockEntityTicker); ++ return new LevelChunk.BoundTickingBlockEntity<>(blockEntity, blockEntityTicker, coordinateKey); // SparklyPaper - cache last shouldTickBlocksAt result when ticking block entities + } + + @FunctionalInterface +@@ -1141,6 +1148,13 @@ public class LevelChunk extends ChunkAccess { + public String toString() { + return this.ticker + " "; + } ++ ++ // SparklyPaper start - cache last shouldTickBlocksAt result when ticking block entities ++ @Override ++ public long getChunkCoordinateKey() { ++ return this.ticker.getChunkCoordinateKey(); ++ } ++ // SparklyPaper end + } + + private class BoundTickingBlockEntity implements TickingBlockEntity { +@@ -1148,10 +1162,12 @@ public class LevelChunk extends ChunkAccess { + private final T blockEntity; + private final BlockEntityTicker ticker; + private boolean loggedInvalidBlockState; ++ private final long chunkCoordinateKey; // SparklyPaper - cache last shouldTickBlocksAt result when ticking block entities + +- BoundTickingBlockEntity(BlockEntity tileentity, BlockEntityTicker blockentityticker) { ++ BoundTickingBlockEntity(BlockEntity tileentity, BlockEntityTicker blockentityticker, long chunkCoordinateKey) { // SparklyPaper - cache last shouldTickBlocksAt result when ticking block entities + this.blockEntity = (T) tileentity; // CraftBukkit - decompile error + this.ticker = blockentityticker; ++ this.chunkCoordinateKey = chunkCoordinateKey; // SparklyPaper - cache last shouldTickBlocksAt result when ticking block entities + } + + @Override +@@ -1214,5 +1230,12 @@ public class LevelChunk extends ChunkAccess { + + return "Level ticker for " + s + "@" + this.getPos(); + } ++ ++ // SparklyPaper start - cache last shouldTickBlocksAt result when ticking block entities ++ @Override ++ public long getChunkCoordinateKey() { ++ return this.chunkCoordinateKey; ++ } ++ // SparklyPaper end + } + } diff --git a/patches/server/0019-Parallel-world-ticking.patch b/patches/server/0019-Parallel-world-ticking.patch index 884fdff..9dc4571 100644 --- a/patches/server/0019-Parallel-world-ticking.patch +++ b/patches/server/0019-Parallel-world-ticking.patch @@ -1322,7 +1322,7 @@ index 45243249a561440512ef2a620c60b02e159c80e2..849b9b4336d2ac99324dacf6ad8a2e34 continue; } diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index 95bc1fab201f5869bb811e25e51ad9c94e955e6e..09ab461affbc20f0748dadd1c80552642201e9cf 100644 +index 14936213cf2a9f0d7f4d0fbf7387e6d97c6cf717..ef0776b6429f1e82c7bd29b19b0328722b426a18 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java @@ -15,6 +15,8 @@ import java.util.function.Consumer; @@ -1695,10 +1695,10 @@ index a743f36f2682a6b72ffa6644782fc081d1479eb7..85e7da9884f48989a62a789306b701a3 // CraftBukkit end } diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -index fa170cc1ce7011d201295b89718292d696c7fc24..59045493537533f60bb5e3b80633c71bafb25dc0 100644 +index c6f62c56da6e74fbaa57300f8cc2718675d1681e..0af3100fdb59028dd43bcfba50c36fca45778fd5 100644 --- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -@@ -412,6 +412,7 @@ public class LevelChunk extends ChunkAccess { +@@ -419,6 +419,7 @@ public class LevelChunk extends ChunkAccess { @Nullable public BlockState setBlockState(BlockPos blockposition, BlockState iblockdata, boolean flag, boolean doPlace) {