diff options
Diffstat (limited to 'Spigot-Server-Patches/0067-Chunk-save-queue-improvements.patch')
-rw-r--r-- | Spigot-Server-Patches/0067-Chunk-save-queue-improvements.patch | 181 |
1 files changed, 181 insertions, 0 deletions
diff --git a/Spigot-Server-Patches/0067-Chunk-save-queue-improvements.patch b/Spigot-Server-Patches/0067-Chunk-save-queue-improvements.patch new file mode 100644 index 0000000000..eb4d40fdd7 --- /dev/null +++ b/Spigot-Server-Patches/0067-Chunk-save-queue-improvements.patch @@ -0,0 +1,181 @@ +From 30f567605c740413aa96168ce4eb60ab22ee1180 Mon Sep 17 00:00:00 2001 +From: Aikar <[email protected]> +Date: Fri, 4 Mar 2016 18:18:37 -0600 +Subject: [PATCH] Chunk save queue improvements + +For some unknown reason, Minecraft is sleeping 10ms between every single chunk being saved to disk. +Under high chunk load/unload activity (lots of movement / teleporting), this causes the chunk unload queue +to build up in size. + +This has multiple impacts: +1) Performance of the unload queue itself - The save thread is pretty ineffecient for how it accesses it + By letting the queue get larger, checking and popping work off the queue can get less performant. +2) Performance of chunk loading - As with #1, chunk loads also have to check this queue when loading + chunk data so that it doesn't load stale data if new data is pending write to disk. +3) Memory Usage - The entire chunk has been serialized to NBT, and now sits in this queue. This leads to + elevated memory usage, and then the objects used in the serialization sit around longer than needed, + resulting in promotion to Old Generation instead of dying young. + +To optimize this, we change the entire unload queue to be a proper queue. This improves the behavior of popping +the first queued chunk off, instead of abusing iterators like Mojang was doing. + +This also improves reliability of chunk saving, as the previous hack job had a race condition that could +fail to save some chunks. + +Then finally, Sleeping will by default be removed, but due to known issues with 1.9, a config option was added. +But if sleeps are to remain enabled, we at least lower the sleep interval so it doesn't have as much negative impact. + +diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java +index 9a6f87e59..873ffa77d 100644 +--- a/src/main/java/com/destroystokyo/paper/PaperConfig.java ++++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java +@@ -204,4 +204,10 @@ public class PaperConfig { + private static void chunkLoadThreads() { + minChunkLoadThreads = Math.min(6, getInt("settings.min-chunk-load-threads", 2)); // Keep people from doing stupid things with max of 6 + } ++ ++ public static boolean enableFileIOThreadSleep; ++ private static void enableFileIOThreadSleep() { ++ enableFileIOThreadSleep = getBoolean("settings.sleep-between-chunk-saves", false); ++ if (enableFileIOThreadSleep) Bukkit.getLogger().info("Enabled sleeping between chunk saves, beware of memory issues"); ++ } + } +diff --git a/src/main/java/net/minecraft/server/ChunkRegionLoader.java b/src/main/java/net/minecraft/server/ChunkRegionLoader.java +index 79cb3953b..7f3e874ba 100644 +--- a/src/main/java/net/minecraft/server/ChunkRegionLoader.java ++++ b/src/main/java/net/minecraft/server/ChunkRegionLoader.java +@@ -12,14 +12,17 @@ import java.util.Map; + import java.util.Set; + import java.util.concurrent.ConcurrentHashMap; + import javax.annotation.Nullable; ++import java.util.concurrent.ConcurrentLinkedQueue; // Paper + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + + public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver { + ++ private ConcurrentLinkedQueue<QueuedChunk> queue = new ConcurrentLinkedQueue<>(); // Paper - Chunk queue improvements ++ private final Object lock = new Object(); // Paper - Chunk queue improvements + private static final Logger a = LogManager.getLogger(); + private final Map<ChunkCoordIntPair, NBTTagCompound> b = new ConcurrentHashMap(); +- private final Set<ChunkCoordIntPair> c = Collections.newSetFromMap(new ConcurrentHashMap()); ++ //private final Set<ChunkCoordIntPair> c = Collections.newSetFromMap(new ConcurrentHashMap()); // Paper - Chunk queue improvements + private final File d; + private final DataConverterManager e; + private boolean f; +@@ -33,11 +36,11 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver { + public boolean chunkExists(World world, int i, int j) { + ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(i, j); + +- if (this.c.contains(chunkcoordintpair)) { ++ //if (this.c.contains(chunkcoordintpair)) { // Paper - Chunk queue improvements + if (this.b.containsKey(chunkcoordintpair)) { + return true; + } +- } ++ //} // Paper - Chunk queue improvements + + // Paper start - Don't create region files when checking that they exist + final RegionFile region = RegionFileCache.a(this.d, i, j, false); +@@ -151,35 +154,32 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver { + } + + protected void a(ChunkCoordIntPair chunkcoordintpair, NBTTagCompound nbttagcompound) { +- if (!this.c.contains(chunkcoordintpair)) { ++ synchronized (lock) { // Paper - Chunk queue improvements + this.b.put(chunkcoordintpair, nbttagcompound); + } ++ queue.add(new QueuedChunk(chunkcoordintpair, nbttagcompound)); // Paper - Chunk queue improvements + + FileIOThread.a().a(this); + } + + public boolean c() { +- // CraftBukkit start +- Iterator<Map.Entry<ChunkCoordIntPair, NBTTagCompound>> iter = this.b.entrySet().iterator(); +- if (!iter.hasNext()) { +- // CraftBukkit end ++ // Paper start - Chunk queue improvements ++ QueuedChunk chunk = queue.poll(); ++ if (chunk == null) { ++ // Paper - end + if (this.f) { + ChunkRegionLoader.a.info("ThreadedAnvilChunkStorage ({}): All chunks are saved", new Object[] { this.d.getName()}); + } + + return false; + } else { +- // CraftBukkit start +- Map.Entry<ChunkCoordIntPair, NBTTagCompound> entry = iter.next(); +- iter.remove(); // Pop single entry +- ChunkCoordIntPair chunkcoordintpair = entry.getKey(); +- // CraftBukkit end ++ ChunkCoordIntPair chunkcoordintpair = chunk.coords; // Paper - Chunk queue improvements + + boolean flag; + + try { +- this.c.add(chunkcoordintpair); +- NBTTagCompound nbttagcompound = (NBTTagCompound) entry.getValue(); // CraftBukkit ++ //this.c.add(chunkcoordintpair); ++ NBTTagCompound nbttagcompound = chunk.compound; // Paper - Chunk queue improvements + + if (nbttagcompound != null) { + try { +@@ -188,10 +188,11 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver { + ChunkRegionLoader.a.error("Failed to save chunk", exception); + } + } ++ synchronized (lock) { if (this.b.get(chunkcoordintpair) == nbttagcompound) { this.b.remove(chunkcoordintpair); } }// Paper - This will not equal if a newer version is still pending + + flag = true; + } finally { +- this.c.remove(chunkcoordintpair); ++ //this.c.remove(chunkcoordintpair); // Paper + } + + return flag; +@@ -556,4 +557,16 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver { + return entity; + } + } ++ ++ // Paper start - Chunk queue improvements ++ private static class QueuedChunk { ++ public ChunkCoordIntPair coords; ++ public NBTTagCompound compound; ++ ++ public QueuedChunk(ChunkCoordIntPair coords, NBTTagCompound compound) { ++ this.coords = coords; ++ this.compound = compound; ++ } ++ } ++ // Paper end + } +diff --git a/src/main/java/net/minecraft/server/FileIOThread.java b/src/main/java/net/minecraft/server/FileIOThread.java +index acfdd52dc..fdbaf5fbd 100644 +--- a/src/main/java/net/minecraft/server/FileIOThread.java ++++ b/src/main/java/net/minecraft/server/FileIOThread.java +@@ -39,11 +39,15 @@ public class FileIOThread implements Runnable { + ++this.d; + } + +- try { +- Thread.sleep(this.e ? 0L : 10L); +- } catch (InterruptedException interruptedexception) { +- interruptedexception.printStackTrace(); ++ // Paper start - Add toggle ++ if (com.destroystokyo.paper.PaperConfig.enableFileIOThreadSleep) { ++ try { ++ Thread.sleep(this.e ? 0L : 2L); ++ } catch (InterruptedException interruptedexception) { ++ interruptedexception.printStackTrace(); ++ } + } ++ // Paper end + } + + if (this.b.isEmpty()) { +-- +2.12.2 + |