2019-04-23 06:47:07 +02:00
From 208077ba7ae7f41d3bdfcc4f5bc577faa8095905 Mon Sep 17 00:00:00 2001
2016-03-12 21:23:17 +01:00
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.
2016-03-18 18:40:46 +01:00
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.
2016-03-12 21:23:17 +01:00
2016-03-18 18:40:46 +01:00
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.
2016-03-12 21:23:17 +01:00
diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java
2019-04-23 06:47:07 +02:00
index cfcc244672..4e932ea235 100644
2016-03-12 21:23:17 +01:00
--- a/src/main/java/com/destroystokyo/paper/PaperConfig.java
+++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java
2018-10-19 02:44:59 +02:00
@@ -217,4 +217,10 @@ public class PaperConfig {
2018-09-29 01:31:59 +02:00
" - Interval: " + timeSummary(Timings.getHistoryInterval() / 20) +
" - Length: " + timeSummary(Timings.getHistoryLength() / 20));
2016-03-12 21:23:17 +01:00
}
+
+ 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");
+ }
}
2018-09-11 04:38:42 +02:00
diff --git a/src/main/java/net/minecraft/server/ChunkCoordIntPair.java b/src/main/java/net/minecraft/server/ChunkCoordIntPair.java
2019-04-23 06:47:07 +02:00
index b0c004b1f2..d2cece2651 100644
2018-09-11 04:38:42 +02:00
--- a/src/main/java/net/minecraft/server/ChunkCoordIntPair.java
+++ b/src/main/java/net/minecraft/server/ChunkCoordIntPair.java
2019-01-01 04:15:55 +01:00
@@ -20,6 +20,7 @@ public class ChunkCoordIntPair {
this.z = (int) (i >> 32);
2018-09-11 04:38:42 +02:00
}
+ public long asLong() { return a(); } // Paper
public long a() {
return a(this.x, this.z);
}
2016-03-12 21:23:17 +01:00
diff --git a/src/main/java/net/minecraft/server/ChunkRegionLoader.java b/src/main/java/net/minecraft/server/ChunkRegionLoader.java
2019-04-23 06:47:07 +02:00
index 35976a26f3..21ee154a57 100644
2016-03-12 21:23:17 +01:00
--- a/src/main/java/net/minecraft/server/ChunkRegionLoader.java
+++ b/src/main/java/net/minecraft/server/ChunkRegionLoader.java
2019-01-01 04:15:55 +01:00
@@ -20,6 +20,7 @@ import java.util.Map.Entry;
import java.util.function.Consumer;
2018-07-15 03:53:17 +02:00
import java.util.function.Function;
2016-05-12 04:07:46 +02:00
import javax.annotation.Nullable;
2016-03-12 21:23:17 +01:00
+import java.util.concurrent.ConcurrentLinkedQueue; // Paper
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
2017-08-12 23:32:01 +02:00
// Spigot start
2019-01-01 04:15:55 +01:00
@@ -29,8 +30,28 @@ import org.spigotmc.SupplierUtils;
2016-03-12 21:23:17 +01:00
public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
2018-07-15 03:53:17 +02:00
+ // Paper start - Chunk queue improvements
+ private static class QueuedChunk {
+ public ChunkCoordIntPair coords;
+ public Supplier<NBTTagCompound> compoundSupplier;
2018-09-09 21:29:22 +02:00
+ public Runnable onSave;
+
+ public QueuedChunk(Runnable run) {
+ this.coords = null;
+ this.compoundSupplier = null;
+ this.onSave = run;
+ }
2018-07-15 03:53:17 +02:00
+
+ public QueuedChunk(ChunkCoordIntPair coords, Supplier<NBTTagCompound> compoundSupplier) {
+ this.coords = coords;
+ this.compoundSupplier = compoundSupplier;
+ }
+ }
2018-08-26 20:11:49 +02:00
+ final private ConcurrentLinkedQueue<QueuedChunk> queue = new ConcurrentLinkedQueue<>();
2018-07-15 03:53:17 +02:00
+ // Paper end
+
2016-03-12 21:23:17 +01:00
private static final Logger a = LogManager.getLogger();
2018-09-11 04:38:42 +02:00
- private final Map<ChunkCoordIntPair, Supplier<NBTTagCompound>> b = Maps.newHashMap();
+ private final it.unimi.dsi.fastutil.longs.Long2ObjectMap<Supplier<NBTTagCompound>> saveMap = it.unimi.dsi.fastutil.longs.Long2ObjectMaps.synchronize(new it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<>()); // Paper
2018-07-15 03:53:17 +02:00
private final File c;
2018-09-11 04:38:42 +02:00
private final DataFixer d;
private PersistentStructureLegacy e;
2019-01-04 20:19:36 +01:00
@@ -86,7 +107,7 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
return null;
}
// CraftBukkit end
2018-09-11 04:38:42 +02:00
- NBTTagCompound nbttagcompound = SupplierUtils.getIfExists(this.b.get(new ChunkCoordIntPair(i, j))); // Spigot
+ NBTTagCompound nbttagcompound = SupplierUtils.getIfExists(this.saveMap.get(ChunkCoordIntPair.asLong(i, j))); // Spigot // Paper
if (nbttagcompound != null) {
return nbttagcompound;
2019-01-04 20:19:36 +01:00
@@ -314,7 +335,7 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
2018-07-15 03:53:17 +02:00
};
}
2018-09-11 04:38:42 +02:00
2018-07-15 03:53:17 +02:00
- 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
// Spigot end
} catch (Exception exception) {
ChunkRegionLoader.a.error("Failed to save chunk", exception);
2019-01-04 20:19:36 +01:00
@@ -323,7 +344,8 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
2018-07-15 03:53:17 +02:00
}
2018-08-26 20:11:49 +02:00
protected void a(ChunkCoordIntPair chunkcoordintpair, Supplier<NBTTagCompound> nbttagcompound) { // Spigot
- this.b.put(chunkcoordintpair, nbttagcompound);
2018-09-11 04:38:42 +02:00
+ this.saveMap.put(chunkcoordintpair.asLong(), nbttagcompound); // Paper
+ queue.add(new QueuedChunk(chunkcoordintpair, nbttagcompound)); // Paper - Chunk queue improvements
2016-03-12 21:23:17 +01:00
FileIOThread.a().a(this);
}
2018-07-15 03:53:17 +02:00
2019-01-04 20:19:36 +01:00
@@ -333,20 +355,24 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
2017-08-11 13:02:53 +02:00
}
2016-03-12 21:23:17 +01:00
2018-08-26 20:11:49 +02:00
private boolean processSaveQueueEntry(boolean logCompletion) {
2019-01-01 04:15:55 +01:00
- Iterator<Entry<ChunkCoordIntPair, Supplier<NBTTagCompound>>> iterator = this.b.entrySet().iterator(); // Spigot
-
2018-08-26 20:11:49 +02:00
- if (!iterator.hasNext()) {
2016-03-12 21:23:17 +01:00
+ // Paper start - Chunk queue improvements
+ QueuedChunk chunk = queue.poll();
+ if (chunk == null) {
2017-08-11 13:02:53 +02:00
+ // Paper - end
2018-08-26 20:11:49 +02:00
if (logCompletion) { // CraftBukkit
2018-07-15 03:53:17 +02:00
ChunkRegionLoader.a.info("ThreadedAnvilChunkStorage ({}): All chunks are saved", this.c.getName());
2018-08-26 20:11:49 +02:00
}
2016-03-12 21:23:17 +01:00
return false;
} else {
2019-01-01 04:15:55 +01:00
- Entry<ChunkCoordIntPair, NBTTagCompound> entry = (Entry) iterator.next();
2018-08-26 20:11:49 +02:00
-
- iterator.remove();
- ChunkCoordIntPair chunkcoordintpair = (ChunkCoordIntPair) entry.getKey();
- Supplier<NBTTagCompound> nbttagcompound = (Supplier<NBTTagCompound>) entry.getValue(); // Spigot
2018-09-09 21:29:22 +02:00
+ // Paper start
+ if (chunk.onSave != null) {
+ chunk.onSave.run();
+ return true;
+ }
+ // Paper end
2016-03-12 21:23:17 +01:00
+ ChunkCoordIntPair chunkcoordintpair = chunk.coords; // Paper - Chunk queue improvements
2018-08-26 20:11:49 +02:00
+ Supplier<NBTTagCompound> nbttagcompound = chunk.compoundSupplier; // Spigot // Paper
2016-03-12 21:23:17 +01:00
2018-08-26 20:11:49 +02:00
if (nbttagcompound == null) {
return true;
2019-01-04 20:19:36 +01:00
@@ -355,6 +381,15 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
2018-08-26 20:11:49 +02:00
// CraftBukkit start
RegionFileCache.write(this.c, chunkcoordintpair.x, chunkcoordintpair.z, SupplierUtils.getIfExists(nbttagcompound)); // Spigot
2016-03-12 21:23:17 +01:00
2018-08-26 20:11:49 +02:00
+ // Paper start remove from map only if this was the latest version of the chunk
2018-09-11 04:38:42 +02:00
+ synchronized (this.saveMap) {
+ long k = chunkcoordintpair.asLong();
2018-08-26 20:11:49 +02:00
+ // This will not equal if a newer version is still pending - wait until newest is saved to remove
2018-09-11 04:38:42 +02:00
+ if (this.saveMap.get(k) == chunk.compoundSupplier) {
+ this.saveMap.remove(k);
2018-08-26 20:11:49 +02:00
+ }
2018-07-15 03:53:17 +02:00
+ }
2018-08-26 20:11:49 +02:00
+ // Paper end
/*
NBTCompressedStreamTools.a(nbttagcompound, (DataOutput) dataoutputstream);
dataoutputstream.close();
2016-03-12 21:23:17 +01:00
diff --git a/src/main/java/net/minecraft/server/FileIOThread.java b/src/main/java/net/minecraft/server/FileIOThread.java
2019-04-23 06:47:07 +02:00
index 8c3537ab8d..3c688f546c 100644
2016-03-12 21:23:17 +01:00
--- a/src/main/java/net/minecraft/server/FileIOThread.java
+++ b/src/main/java/net/minecraft/server/FileIOThread.java
2019-01-01 04:15:55 +01:00
@@ -38,20 +38,21 @@ public class FileIOThread implements Runnable {
IAsyncChunkSaver iasyncchunksaver = (IAsyncChunkSaver) this.c.get(i);
2018-08-27 04:09:10 +02:00
boolean flag;
2019-01-01 04:15:55 +01:00
- synchronized (iasyncchunksaver) {
+ //synchronized (iasyncchunksaver) { // Paper - remove synchronized
2018-08-27 04:09:10 +02:00
flag = iasyncchunksaver.a();
- }
+ //} // Paper
if (!flag) {
this.c.remove(i--);
2018-07-15 03:53:17 +02:00
++this.e;
2016-03-12 21:23:17 +01:00
}
2018-07-15 03:53:17 +02:00
+ if (com.destroystokyo.paper.PaperConfig.enableFileIOThreadSleep) { // Paper
try {
- Thread.sleep(this.f ? 0L : 10L);
+ Thread.sleep(this.f ? 0L : 1L); // Paper
2019-01-01 04:15:55 +01:00
} catch (InterruptedException interruptedexception) {
interruptedexception.printStackTrace();
2018-07-15 03:53:17 +02:00
- }
+ }} // Paper
2016-03-12 21:23:17 +01:00
}
2018-07-15 03:53:17 +02:00
if (this.c.isEmpty()) {
2016-03-12 21:23:17 +01:00
--
2019-04-23 06:47:07 +02:00
2.21.0
2016-03-12 21:23:17 +01:00