6d41af029c
Upstream has released updates that appears to apply and compile correctly. This update has not been tested by PaperMC and as with ANY update, please do your own testing Bukkit Changes: 3e2858f6 SPIGOT-4352: MoistureChangeEvent 5466e281 Add BlockDispenseArmorEvent CraftBukkit Changes: 3123a069 SPIGOT-4352: MoistureChangeEvent 226db0ea Add BlockDispenseArmorEvent cd367fa4 Fix bad thread safety in ChunkRegionLoader 3f5ca5f2 SPIGOT-4355: Improve cancelling VehicleEnterEvent Spigot Changes: 145a37ae Rebuild patches 3f2423cc Rebuild patches
172 lines
8.5 KiB
Diff
172 lines
8.5 KiB
Diff
From 47272a40d066580e0b7d14486215f020684e8bad Mon Sep 17 00:00:00 2001
|
|
From: Aikar <aikar@aikar.co>
|
|
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 0d68ffd75a..fd00c320ce 100644
|
|
--- a/src/main/java/com/destroystokyo/paper/PaperConfig.java
|
|
+++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java
|
|
@@ -208,4 +208,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 2e9bd0949a..3166d4b1ad 100644
|
|
--- a/src/main/java/net/minecraft/server/ChunkRegionLoader.java
|
|
+++ b/src/main/java/net/minecraft/server/ChunkRegionLoader.java
|
|
@@ -22,6 +22,7 @@ import java.util.function.Consumer;
|
|
import java.util.function.Function;
|
|
import java.util.function.Predicate;
|
|
import javax.annotation.Nullable;
|
|
+import java.util.concurrent.ConcurrentLinkedQueue; // Paper
|
|
import org.apache.logging.log4j.LogManager;
|
|
import org.apache.logging.log4j.Logger;
|
|
// Spigot start
|
|
@@ -31,6 +32,19 @@ import org.spigotmc.SupplierUtils;
|
|
|
|
public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
|
|
|
|
+ // Paper start - Chunk queue improvements
|
|
+ private static class QueuedChunk {
|
|
+ public ChunkCoordIntPair coords;
|
|
+ public Supplier<NBTTagCompound> compoundSupplier;
|
|
+
|
|
+ public QueuedChunk(ChunkCoordIntPair coords, Supplier<NBTTagCompound> compoundSupplier) {
|
|
+ this.coords = coords;
|
|
+ this.compoundSupplier = compoundSupplier;
|
|
+ }
|
|
+ }
|
|
+ final private ConcurrentLinkedQueue<QueuedChunk> queue = new ConcurrentLinkedQueue<>();
|
|
+ // Paper end
|
|
+
|
|
private static final Logger a = LogManager.getLogger();
|
|
private final Map<ChunkCoordIntPair, Supplier<NBTTagCompound>> b = java.util.Collections.synchronizedMap(Maps.newHashMap()); // CraftBukkit // Spigot
|
|
private final File c;
|
|
@@ -305,8 +319,8 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
|
|
}
|
|
};
|
|
}
|
|
-
|
|
- this.a(chunkcoordintpair, SupplierUtils.createUnivaluedSupplier(completion, unloaded && this.b.size() < SAVE_QUEUE_TARGET_SIZE));
|
|
+ this.a(chunkcoordintpair, SupplierUtils.createUnivaluedSupplier(completion, unloaded)); // Paper - Remove save queue target size
|
|
+ // Paper end
|
|
// Spigot end
|
|
} catch (Exception exception) {
|
|
ChunkRegionLoader.a.error("Failed to save chunk", exception);
|
|
@@ -315,7 +329,10 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
|
|
}
|
|
|
|
protected void a(ChunkCoordIntPair chunkcoordintpair, Supplier<NBTTagCompound> nbttagcompound) { // Spigot
|
|
- this.b.put(chunkcoordintpair, nbttagcompound);
|
|
+ synchronized (this.queue) { // Paper - synchronize while modifying the map
|
|
+ queue.add(new QueuedChunk(chunkcoordintpair, nbttagcompound)); // Paper - Chunk queue improvements
|
|
+ this.b.put(chunkcoordintpair, nbttagcompound);
|
|
+ }
|
|
FileIOThread.a().a(this);
|
|
}
|
|
|
|
@@ -325,20 +342,18 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
|
|
}
|
|
|
|
private boolean processSaveQueueEntry(boolean logCompletion) {
|
|
- synchronized (this.b) { // CraftBukkit
|
|
- Iterator iterator = this.b.entrySet().iterator();
|
|
- if (!iterator.hasNext()) {
|
|
+ // Paper start - Chunk queue improvements
|
|
+ QueuedChunk chunk = queue.poll();
|
|
+ if (chunk == null) {
|
|
+ // Paper - end
|
|
if (logCompletion) { // CraftBukkit
|
|
ChunkRegionLoader.a.info("ThreadedAnvilChunkStorage ({}): All chunks are saved", this.c.getName());
|
|
}
|
|
|
|
return false;
|
|
} else {
|
|
- Entry entry = (Entry) iterator.next();
|
|
-
|
|
- iterator.remove();
|
|
- ChunkCoordIntPair chunkcoordintpair = (ChunkCoordIntPair) entry.getKey();
|
|
- Supplier<NBTTagCompound> nbttagcompound = (Supplier<NBTTagCompound>) entry.getValue(); // Spigot
|
|
+ ChunkCoordIntPair chunkcoordintpair = chunk.coords; // Paper - Chunk queue improvements
|
|
+ Supplier<NBTTagCompound> nbttagcompound = chunk.compoundSupplier; // Spigot // Paper
|
|
|
|
if (nbttagcompound == null) {
|
|
return true;
|
|
@@ -347,6 +362,14 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
|
|
// CraftBukkit start
|
|
RegionFileCache.write(this.c, chunkcoordintpair.x, chunkcoordintpair.z, SupplierUtils.getIfExists(nbttagcompound)); // Spigot
|
|
|
|
+ // Paper start remove from map only if this was the latest version of the chunk
|
|
+ synchronized (this.queue) {
|
|
+ // This will not equal if a newer version is still pending - wait until newest is saved to remove
|
|
+ if (this.b.get(chunkcoordintpair) == chunk.compoundSupplier) {
|
|
+ this.b.remove(chunkcoordintpair);
|
|
+ }
|
|
+ }
|
|
+ // Paper end
|
|
/*
|
|
NBTCompressedStreamTools.a(nbttagcompound, (DataOutput) dataoutputstream);
|
|
dataoutputstream.close();
|
|
diff --git a/src/main/java/net/minecraft/server/FileIOThread.java b/src/main/java/net/minecraft/server/FileIOThread.java
|
|
index a3aba244af..97917551a4 100644
|
|
--- a/src/main/java/net/minecraft/server/FileIOThread.java
|
|
+++ b/src/main/java/net/minecraft/server/FileIOThread.java
|
|
@@ -35,20 +35,21 @@ public class FileIOThread implements Runnable {
|
|
for(int i = 0; i < this.c.size(); ++i) {
|
|
IAsyncChunkSaver iasyncchunksaver = (IAsyncChunkSaver)this.c.get(i);
|
|
boolean flag;
|
|
- synchronized(iasyncchunksaver) {
|
|
+ //synchronized(iasyncchunksaver) { // Paper - remove synchronized
|
|
flag = iasyncchunksaver.a();
|
|
- }
|
|
+ //} // Paper
|
|
|
|
if (!flag) {
|
|
this.c.remove(i--);
|
|
++this.e;
|
|
}
|
|
|
|
+ if (com.destroystokyo.paper.PaperConfig.enableFileIOThreadSleep) { // Paper
|
|
try {
|
|
- Thread.sleep(this.f ? 0L : 10L);
|
|
+ Thread.sleep(this.f ? 0L : 1L); // Paper
|
|
} catch (InterruptedException interruptedexception1) {
|
|
interruptedexception1.printStackTrace();
|
|
- }
|
|
+ }} // Paper
|
|
}
|
|
|
|
if (this.c.isEmpty()) {
|
|
--
|
|
2.18.0
|
|
|