aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorPaulBGD <[email protected]>2021-11-05 10:52:32 -0400
committerGitHub <[email protected]>2021-11-05 15:52:32 +0100
commit06d82e0d6d062aae0c20f369a5ba5bade0c51d27 (patch)
tree4ff010d10de5edf65ebfcc0991b47482460b44f8
parentada930bf8d0edd4e87b6f1b3868e0605fabd8bae (diff)
downloadPaper-06d82e0d6d062aae0c20f369a5ba5bade0c51d27.tar.gz
Paper-06d82e0d6d062aae0c20f369a5ba5bade0c51d27.zip
Cache palette array (#6767)
Instead of allocating the 4KB for every chunk section, cache it locally and reuse it for other chunk sections to save on allocations. These allocations add up very quickly when saving chunks frequently. For example, with 30,000 chunks and 16 chunk sections per chunk (which will actually get larger in 1.18) it allocates nearly 2GB, which this patch eliminates entirely.
-rw-r--r--patches/server/0836-Cache-palette-array.patch61
1 files changed, 61 insertions, 0 deletions
diff --git a/patches/server/0836-Cache-palette-array.patch b/patches/server/0836-Cache-palette-array.patch
new file mode 100644
index 0000000000..2c787bbbc6
--- /dev/null
+++ b/patches/server/0836-Cache-palette-array.patch
@@ -0,0 +1,61 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Paul Sauve <[email protected]>
+Date: Thu, 4 Feb 2021 23:28:46 -0600
+Subject: [PATCH] Cache palette array
+
+Instead of allocating the 4KB for every chunk section, cache it locally and
+reuse it for other chunk sections to save on allocations. These allocations add
+up very quickly when saving chunks frequently.
+
+diff --git a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
+index c9e942669458668a184aaec3bc0a5509dd6ab5f0..ac84d49d7819666a89cacbe9ef1199cf22ac2ac3 100644
+--- a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
++++ b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
+@@ -263,13 +263,17 @@ public class PalettedContainer<T> implements PaletteResize<T> {
+
+ }
+
++ // Paper start - allow reusing int array
+ public synchronized void write(CompoundTag nbt, String paletteKey, String dataKey) { // Paper - synchronize
++ this.write(nbt, paletteKey, dataKey, new int[4096]);
++ }
++ public synchronized void write(CompoundTag nbt, String paletteKey, String dataKey, int[] is) { // Paper - synchronize // Paper end
+ try {
+ this.acquire();
+ HashMapPalette<T> hashMapPalette = new HashMapPalette<>(this.registry, this.bits, this.dummyPaletteResize, this.reader, this.writer);
+ T object = this.defaultValue;
+ int i = hashMapPalette.idFor(this.defaultValue);
+- int[] is = new int[4096];
++ //int[] is = new int[4096]; // Paper - will now be pulled from ThreadLocal
+
+ for(int j = 0; j < 4096; ++j) {
+ T object2 = this.get(j);
+diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
+index 7921ee2786d0d6a60d43786b20efc03a0f9178e3..10f15b8c7763a980aec47a4b4dda5686e60642ce 100644
+--- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
++++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
+@@ -500,6 +500,7 @@ public class ChunkSerializer {
+ return new AsyncSaveData(blockLight, skyLight, blockTickListSerialized, fluidTickListSerialized, blockEntitiesSerialized, world.getGameTime());
+ }
+
++ private static final ThreadLocal<int[]> PALETTE_ARRAY = ThreadLocal.withInitial(() -> new int[4096]); // Paper - maintain a per-thread cache for saving palettes
+ public static CompoundTag write(ServerLevel world, ChunkAccess chunk) {
+ return saveChunk(world, chunk, null);
+ }
+@@ -533,6 +534,7 @@ public class ChunkSerializer {
+ ThreadedLevelLightEngine lightenginethreaded = world.getChunkSource().getLightEngine();
+ boolean flag = chunk.isLightCorrect();
+
++ int[] is = PALETTE_ARRAY.get(); // Paper - use cached
+ for (int i = lightenginethreaded.getMinLightSection(); i < lightenginethreaded.getMaxLightSection(); ++i) {
+ int finalI = i; // CraftBukkit - decompile errors
+ LevelChunkSection chunksection = (LevelChunkSection) Arrays.stream(achunksection).filter((chunksection1) -> {
+@@ -547,7 +549,7 @@ public class ChunkSerializer {
+
+ nbttagcompound2.putByte("Y", (byte) (i & 255));
+ if (chunksection != LevelChunk.EMPTY_SECTION) {
+- chunksection.getStates().write(nbttagcompound2, "Palette", "BlockStates");
++ chunksection.getStates().write(nbttagcompound2, "Palette", "BlockStates", is); // Paper - reuse array
+ }
+
+ // Paper start - replace light engine