mirror of
https://github.com/PaperMC/Folia.git
synced 2024-11-19 11:35:18 +01:00
b42537ed02
The repeated I/O of creating the directory for the regionfile or for checking if the file exists can be heavy in when pushing chunk generation extremely hard - as each chunk gen request may effectively go through to the I/O thread.
120 lines
6.6 KiB
Diff
120 lines
6.6 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
|
Date: Thu, 2 Mar 2023 23:19:04 -0800
|
|
Subject: [PATCH] Cache whether region files do not exist
|
|
|
|
The repeated I/O of creating the directory for the regionfile
|
|
or for checking if the file exists can be heavy in
|
|
when pushing chunk generation extremely hard - as each chunk gen
|
|
request may effectively go through to the I/O thread.
|
|
|
|
diff --git a/src/main/java/io/papermc/paper/chunk/system/io/RegionFileIOThread.java b/src/main/java/io/papermc/paper/chunk/system/io/RegionFileIOThread.java
|
|
index a08cde4eefe879adcee7c4118bc38f98c5097ed0..8a11e10b01fa012b2f98b1c193c53251e848f909 100644
|
|
--- a/src/main/java/io/papermc/paper/chunk/system/io/RegionFileIOThread.java
|
|
+++ b/src/main/java/io/papermc/paper/chunk/system/io/RegionFileIOThread.java
|
|
@@ -819,8 +819,14 @@ public final class RegionFileIOThread extends PrioritisedQueueExecutorThread {
|
|
return file.hasChunk(chunkPos) ? Boolean.TRUE : Boolean.FALSE;
|
|
});
|
|
} else {
|
|
+ // first check if the region file for sure does not exist
|
|
+ if (taskController.doesRegionFileNotExist(chunkX, chunkZ)) {
|
|
+ return Boolean.FALSE;
|
|
+ } // else: it either exists or is not known, fall back to checking the loaded region file
|
|
+
|
|
return taskController.computeForRegionFileIfLoaded(chunkX, chunkZ, (final RegionFile file) -> {
|
|
if (file == null) { // null if not loaded
|
|
+ // not sure at this point, let the I/O thread figure it out
|
|
return Boolean.TRUE;
|
|
}
|
|
|
|
@@ -1116,6 +1122,10 @@ public final class RegionFileIOThread extends PrioritisedQueueExecutorThread {
|
|
return !this.tasks.isEmpty();
|
|
}
|
|
|
|
+ public boolean doesRegionFileNotExist(final int chunkX, final int chunkZ) {
|
|
+ return this.getCache().doesRegionFileNotExistNoIO(new ChunkPos(chunkX, chunkZ));
|
|
+ }
|
|
+
|
|
public <T> T computeForRegionFile(final int chunkX, final int chunkZ, final boolean existingOnly, final Function<RegionFile, T> function) {
|
|
final RegionFileStorage cache = this.getCache();
|
|
final RegionFile regionFile;
|
|
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
|
index bd502ca721de0cab438d995efa00ad0554c0d2fe..9633b01d2d961fd1403e353484d336376ef009eb 100644
|
|
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
|
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
|
@@ -28,6 +28,35 @@ public class RegionFileStorage implements AutoCloseable {
|
|
|
|
private final boolean isChunkData; // Paper
|
|
|
|
+ // Paper start - cache regionfile does not exist state
|
|
+ static final int MAX_NON_EXISTING_CACHE = 1024 * 64;
|
|
+ private final it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet nonExistingRegionFiles = new it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet();
|
|
+ private synchronized boolean doesRegionFilePossiblyExist(long position) {
|
|
+ if (this.nonExistingRegionFiles.contains(position)) {
|
|
+ this.nonExistingRegionFiles.addAndMoveToFirst(position);
|
|
+ return false;
|
|
+ }
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ private synchronized void createRegionFile(long position) {
|
|
+ this.nonExistingRegionFiles.remove(position);
|
|
+ }
|
|
+
|
|
+ private synchronized void markNonExisting(long position) {
|
|
+ if (this.nonExistingRegionFiles.addAndMoveToFirst(position)) {
|
|
+ while (this.nonExistingRegionFiles.size() >= MAX_NON_EXISTING_CACHE) {
|
|
+ this.nonExistingRegionFiles.removeLastLong();
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public synchronized boolean doesRegionFileNotExistNoIO(ChunkPos pos) {
|
|
+ long key = ChunkPos.asLong(pos.getRegionX(), pos.getRegionZ());
|
|
+ return !this.doesRegionFilePossiblyExist(key);
|
|
+ }
|
|
+ // Paper end - cache regionfile does not exist state
|
|
+
|
|
protected RegionFileStorage(Path directory, boolean dsync) { // Paper - protected constructor
|
|
// Paper start - add isChunkData param
|
|
this(directory, dsync, false);
|
|
@@ -77,7 +106,7 @@ public class RegionFileStorage implements AutoCloseable {
|
|
}
|
|
public synchronized RegionFile getRegionFile(ChunkPos chunkcoordintpair, boolean existingOnly, boolean lock) throws IOException {
|
|
// Paper end
|
|
- long i = ChunkPos.asLong(chunkcoordintpair.getRegionX(), chunkcoordintpair.getRegionZ());
|
|
+ long i = ChunkPos.asLong(chunkcoordintpair.getRegionX(), chunkcoordintpair.getRegionZ()); final long regionPos = i; // Paper - OBFHELPER
|
|
RegionFile regionfile = (RegionFile) this.regionCache.getAndMoveToFirst(i);
|
|
|
|
if (regionfile != null) {
|
|
@@ -89,15 +118,27 @@ public class RegionFileStorage implements AutoCloseable {
|
|
// Paper end
|
|
return regionfile;
|
|
} else {
|
|
+ // Paper start - cache regionfile does not exist state
|
|
+ if (existingOnly && !this.doesRegionFilePossiblyExist(regionPos)) {
|
|
+ return null;
|
|
+ }
|
|
+ // Paper end - cache regionfile does not exist state
|
|
if (this.regionCache.size() >= io.papermc.paper.configuration.GlobalConfiguration.get().misc.regionFileCacheSize) { // Paper - configurable
|
|
((RegionFile) this.regionCache.removeLast()).close();
|
|
}
|
|
|
|
- FileUtil.createDirectoriesSafe(this.folder);
|
|
+ // Paper - only create directory if not existing only - moved down
|
|
Path path = this.folder;
|
|
int j = chunkcoordintpair.getRegionX();
|
|
Path path1 = path.resolve("r." + j + "." + chunkcoordintpair.getRegionZ() + ".mca"); // Paper - diff on change
|
|
- if (existingOnly && !java.nio.file.Files.exists(path1)) return null; // CraftBukkit
|
|
+ if (existingOnly && !java.nio.file.Files.exists(path1)) { // Paper start - cache regionfile does not exist state
|
|
+ this.markNonExisting(regionPos);
|
|
+ return null; // CraftBukkit
|
|
+ } else {
|
|
+ this.createRegionFile(regionPos);
|
|
+ }
|
|
+ // Paper end - cache regionfile does not exist state
|
|
+ FileUtil.createDirectoriesSafe(this.folder); // Paper - only create directory if not existing only - moved from above
|
|
RegionFile regionfile1 = new RegionFile(path1, this.folder, this.sync, this.isChunkData); // Paper - allow for chunk regionfiles to regen header
|
|
|
|
this.regionCache.putAndMoveToFirst(i, regionfile1);
|