d07f7197f2
this should provide a pretty good across-the-board performance improvement for the entire server, as it's very common for code to check if (isLoaded(x, z)) { then do something on that chunk } Previously, containsKey would not read or set the last access cache By making it do get() != null, we gain the benefits of last access and also improves thread safey for async isLoaded checks This exact usage scenario is used in Entity movement, so that alone saves us up to 5%~ of CPU time for Entity movement.
125 lines
5.4 KiB
Diff
125 lines
5.4 KiB
Diff
From 7ac07ac07ac07ac07ac07ac07ac07ac07ac07ac0 Mon Sep 17 00:00:00 2001
|
|
From: Aikar <aikar@aikar.co>
|
|
Date: Thu, 27 Aug 2015 01:15:02 -0400
|
|
Subject: [PATCH] Optimize Chunk Access
|
|
|
|
getting a loaded chunk is one of the most hottest pieces of code in the game.
|
|
getChunkAt is called for the same chunk multiple times in a row, often from getType();
|
|
|
|
Optimize this look up by using a Last Access cache.
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/ChunkMap.java b/src/main/java/net/minecraft/server/ChunkMap.java
|
|
index 7ac07ac07ac0..7ac07ac07ac0 100644
|
|
--- a/src/main/java/net/minecraft/server/ChunkMap.java
|
|
+++ b/src/main/java/net/minecraft/server/ChunkMap.java
|
|
@@ -15,6 +15,7 @@ public class ChunkMap extends Long2ObjectOpenHashMap<Chunk> {
|
|
|
|
public Chunk a(long i, Chunk chunk) {
|
|
chunk.world.timings.syncChunkLoadPostTimer.startTiming(); // Paper
|
|
+ lastChunkByPos = chunk; // Paper
|
|
Chunk chunk1 = (Chunk) super.put(i, chunk);
|
|
ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(i);
|
|
|
|
@@ -22,7 +23,7 @@ public class ChunkMap extends Long2ObjectOpenHashMap<Chunk> {
|
|
for (int k = chunkcoordintpair.z - 1; k <= chunkcoordintpair.z + 1; ++k) {
|
|
if (j != chunkcoordintpair.x || k != chunkcoordintpair.z) {
|
|
long l = ChunkCoordIntPair.a(j, k);
|
|
- Chunk chunk2 = (Chunk) this.get(l);
|
|
+ Chunk chunk2 = (Chunk) super.get(l); // Paper - use super to avoid polluting last access cache
|
|
|
|
if (chunk2 != null) {
|
|
chunk.H();
|
|
@@ -40,7 +41,7 @@ public class ChunkMap extends Long2ObjectOpenHashMap<Chunk> {
|
|
continue;
|
|
}
|
|
|
|
- Chunk neighbor = this.get(ChunkCoordIntPair.a(chunkcoordintpair.x + x, chunkcoordintpair.z + z));
|
|
+ Chunk neighbor = super.get(ChunkCoordIntPair.a(chunkcoordintpair.x + x, chunkcoordintpair.z + z)); // Paper - use super to avoid polluting last access cache
|
|
if (neighbor != null) {
|
|
neighbor.setNeighborLoaded(-x, -z);
|
|
chunk.setNeighborLoaded(x, z);
|
|
@@ -64,7 +65,7 @@ public class ChunkMap extends Long2ObjectOpenHashMap<Chunk> {
|
|
for (int j = chunkcoordintpair.x - 1; j <= chunkcoordintpair.x + 1; ++j) {
|
|
for (int k = chunkcoordintpair.z - 1; k <= chunkcoordintpair.z + 1; ++k) {
|
|
if (j != chunkcoordintpair.x || k != chunkcoordintpair.z) {
|
|
- Chunk chunk1 = (Chunk) this.get(ChunkCoordIntPair.a(j, k));
|
|
+ Chunk chunk1 = (Chunk) super.get(ChunkCoordIntPair.a(j, k)); // Paper - use super to avoid polluting last access cache
|
|
|
|
if (chunk1 != null) {
|
|
chunk1.I();
|
|
@@ -73,8 +74,22 @@ public class ChunkMap extends Long2ObjectOpenHashMap<Chunk> {
|
|
}
|
|
}
|
|
|
|
+ // Paper start
|
|
+ if (lastChunkByPos != null && i == lastChunkByPos.chunkKey) {
|
|
+ lastChunkByPos = null;
|
|
+ }
|
|
return chunk;
|
|
}
|
|
+ private Chunk lastChunkByPos = null;
|
|
+
|
|
+ @Override
|
|
+ public Chunk get(long l) {
|
|
+ if (lastChunkByPos != null && l == lastChunkByPos.chunkKey) {
|
|
+ return lastChunkByPos;
|
|
+ }
|
|
+ return lastChunkByPos = super.get(l);
|
|
+ }
|
|
+ // Paper end
|
|
|
|
public Chunk a(Object object) {
|
|
return this.a(((Long) object).longValue());
|
|
diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java
|
|
index 7ac07ac07ac0..7ac07ac07ac0 100644
|
|
--- a/src/main/java/net/minecraft/server/ChunkProviderServer.java
|
|
+++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java
|
|
@@ -78,15 +78,16 @@ public class ChunkProviderServer implements IChunkProvider {
|
|
Chunk chunk;
|
|
|
|
synchronized (this.chunkLoader) {
|
|
- if (this.lastChunk != null && this.lastChunk.getPos().x == i && this.lastChunk.getPos().z == j) {
|
|
+ // Paper start - remove vanilla lastChunk, we do it more accurately
|
|
+ /* if (this.lastChunk != null && this.lastChunk.locX == i && this.lastChunk.locZ == j) {
|
|
return this.lastChunk;
|
|
- }
|
|
+ }*/ // Paper end
|
|
|
|
long k = ChunkCoordIntPair.a(i, j);
|
|
|
|
chunk = (Chunk) this.chunks.get(k);
|
|
if (chunk != null) {
|
|
- this.lastChunk = chunk;
|
|
+ //this.lastChunk = chunk; // Paper remove vanilla lastChunk
|
|
return chunk;
|
|
}
|
|
|
|
@@ -198,7 +199,7 @@ public class ChunkProviderServer implements IChunkProvider {
|
|
}
|
|
|
|
this.chunks.put(k, chunk);
|
|
- this.lastChunk = chunk;
|
|
+ //this.lastChunk = chunk; // Paper
|
|
}
|
|
|
|
this.asyncTaskHandler.postToMainThread(chunk::addEntities);
|
|
@@ -343,7 +344,7 @@ public class ChunkProviderServer implements IChunkProvider {
|
|
this.saveChunk(chunk, true); // Spigot
|
|
}
|
|
this.chunks.remove(chunk.chunkKey);
|
|
- this.lastChunk = null;
|
|
+ // this.lastChunk = null; // Paper
|
|
}
|
|
return true;
|
|
}
|
|
@@ -379,6 +380,6 @@ public class ChunkProviderServer implements IChunkProvider {
|
|
}
|
|
|
|
public boolean isLoaded(int i, int j) {
|
|
- return this.chunks.containsKey(ChunkCoordIntPair.a(i, j));
|
|
+ return this.chunks.get(ChunkCoordIntPair.asLong(i, j)) != null; // Paper - use get for last access
|
|
}
|
|
}
|
|
--
|
|
2.19.1
|
|
|