PaperMC/CraftBukkit-Patches/0035-Improve-NextTickList-Performance.patch

313 lines
13 KiB
Diff

From d608b1e2af6bc1d8e853aff5c466138653bb7ef2 Mon Sep 17 00:00:00 2001
From: Mike Primm <mike@primmhome.com>
Date: Wed, 24 Apr 2013 01:43:33 -0500
Subject: [PATCH] Improve NextTickList Performance
Improve next-tick-list performance on chunk unloads, large
queues
diff --git a/src/main/java/net/minecraft/server/NextTickListEntry.java b/src/main/java/net/minecraft/server/NextTickListEntry.java
index acf8838..1e3e0f8 100644
--- a/src/main/java/net/minecraft/server/NextTickListEntry.java
+++ b/src/main/java/net/minecraft/server/NextTickListEntry.java
@@ -30,7 +30,7 @@ public class NextTickListEntry implements Comparable {
}
public int hashCode() {
- return (this.a * 1024 * 1024 + this.c * 1024 + this.b) * 256;
+ return (this.a * 257) ^ this.b ^ (this.c * 60217); // Spigot - better hash
}
public NextTickListEntry a(long i) {
diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java
index db0345d..1f864a2 100644
--- a/src/main/java/net/minecraft/server/WorldServer.java
+++ b/src/main/java/net/minecraft/server/WorldServer.java
@@ -25,8 +25,8 @@ public class WorldServer extends World implements org.bukkit.BlockChangeDelegate
private final MinecraftServer server;
public EntityTracker tracker; // CraftBukkit - private final -> public
private final PlayerChunkMap manager;
- private Set L;
- private TreeSet M;
+ private org.bukkit.craftbukkit.util.LongObjectHashMap<Set<NextTickListEntry>> tickEntriesByChunk; // Spigot - switch to something better for chunk-wise access
+ private TreeSet<NextTickListEntry> tickEntryQueue; // Spigot
public ChunkProviderServer chunkProviderServer;
public boolean savingDisabled;
private boolean N;
@@ -36,7 +36,8 @@ public class WorldServer extends World implements org.bukkit.BlockChangeDelegate
private NoteDataList[] R = new NoteDataList[] { new NoteDataList((EmptyClass2) null), new NoteDataList((EmptyClass2) null)};
private int S;
private static final StructurePieceTreasure[] T = new StructurePieceTreasure[] { new StructurePieceTreasure(Item.STICK.id, 0, 1, 3, 10), new StructurePieceTreasure(Block.WOOD.id, 0, 1, 3, 10), new StructurePieceTreasure(Block.LOG.id, 0, 1, 3, 10), new StructurePieceTreasure(Item.STONE_AXE.id, 0, 1, 1, 3), new StructurePieceTreasure(Item.WOOD_AXE.id, 0, 1, 1, 5), new StructurePieceTreasure(Item.STONE_PICKAXE.id, 0, 1, 1, 3), new StructurePieceTreasure(Item.WOOD_PICKAXE.id, 0, 1, 1, 5), new StructurePieceTreasure(Item.APPLE.id, 0, 2, 3, 5), new StructurePieceTreasure(Item.BREAD.id, 0, 2, 3, 3)};
- private List U = new ArrayList();
+ private ArrayList<NextTickListEntry> pendingTickEntries = new ArrayList<NextTickListEntry>(); // Spigot
+ private int nextPendingTickEntry; // Spigot
private IntHashMap entitiesById;
// CraftBukkit start
@@ -54,13 +55,15 @@ public class WorldServer extends World implements org.bukkit.BlockChangeDelegate
this.entitiesById = new IntHashMap();
}
- if (this.L == null) {
- this.L = new HashSet();
+ // Spigot start
+ if (this.tickEntriesByChunk == null) {
+ this.tickEntriesByChunk = new org.bukkit.craftbukkit.util.LongObjectHashMap<Set<NextTickListEntry>>();
}
- if (this.M == null) {
- this.M = new TreeSet();
+ if (this.tickEntryQueue == null) {
+ this.tickEntryQueue = new TreeSet<NextTickListEntry>();
}
+ // Spigot end
this.P = new org.bukkit.craftbukkit.CraftTravelAgent(this); // CraftBukkit
this.scoreboard = new ScoreboardServer(minecraftserver);
@@ -440,9 +443,16 @@ public class WorldServer extends World implements org.bukkit.BlockChangeDelegate
}
public boolean a(int i, int j, int k, int l) {
- NextTickListEntry nextticklistentry = new NextTickListEntry(i, j, k, l);
-
- return this.U.contains(nextticklistentry);
+ // Spigot start
+ int te_cnt = this.pendingTickEntries.size();
+ for (int idx = this.nextPendingTickEntry; idx < te_cnt; idx++) {
+ NextTickListEntry ent = this.pendingTickEntries.get(idx);
+ if ((ent.a == i) && (ent.b == j) && (ent.c == k) && Block.b(ent.d, l)) {
+ return true;
+ }
+ }
+ return false;
+ // Spigot end
}
public void a(int i, int j, int k, int l, int i1) {
@@ -476,10 +486,9 @@ public class WorldServer extends World implements org.bukkit.BlockChangeDelegate
nextticklistentry.a(j1);
}
- if (!this.L.contains(nextticklistentry)) {
- this.L.add(nextticklistentry);
- this.M.add(nextticklistentry);
- }
+ // Spigot start
+ addNextTickIfNeeded(nextticklistentry);
+ // Spigot end
}
}
@@ -491,10 +500,9 @@ public class WorldServer extends World implements org.bukkit.BlockChangeDelegate
nextticklistentry.a((long) i1 + this.worldData.getTime());
}
- if (!this.L.contains(nextticklistentry)) {
- this.L.add(nextticklistentry);
- this.M.add(nextticklistentry);
- }
+ // Spigot start
+ addNextTickIfNeeded(nextticklistentry);
+ // Spigot end
}
public void tickEntities() {
@@ -514,11 +522,12 @@ public class WorldServer extends World implements org.bukkit.BlockChangeDelegate
}
public boolean a(boolean flag) {
- int i = this.M.size();
+ // Spigot start
+ int i = this.tickEntryQueue.size();
- if (i != this.L.size()) {
- throw new IllegalStateException("TickNextTick list out of synch");
- } else {
+ this.nextPendingTickEntry = 0;
+ {
+ // Spigot end
if (i > 1000) {
// CraftBukkit start - If the server has too much to process over time, try to alleviate that
if (i > 20 * 1000) {
@@ -534,23 +543,24 @@ public class WorldServer extends World implements org.bukkit.BlockChangeDelegate
NextTickListEntry nextticklistentry;
for (int j = 0; j < i; ++j) {
- nextticklistentry = (NextTickListEntry) this.M.first();
+ nextticklistentry = (NextTickListEntry) this.tickEntryQueue.first(); // Spigot
if (!flag && nextticklistentry.e > this.worldData.getTime()) {
break;
}
- this.M.remove(nextticklistentry);
- this.L.remove(nextticklistentry);
- this.U.add(nextticklistentry);
+ // Spigot start
+ this.removeNextTickIfNeeded(nextticklistentry);
+ this.pendingTickEntries.add(nextticklistentry);
+ // Spigot end
}
this.methodProfiler.b();
this.methodProfiler.a("ticking");
- Iterator iterator = this.U.iterator();
-
- while (iterator.hasNext()) {
- nextticklistentry = (NextTickListEntry) iterator.next();
- iterator.remove();
+ // Spigot start
+ for (int j = 0, te_cnt = this.pendingTickEntries.size(); j < te_cnt; j++) {
+ nextticklistentry = pendingTickEntries.get(j);
+ this.nextPendingTickEntry = j + 1; // treat this as dequeued
+ // Spigot end
byte b0 = 0;
if (this.e(nextticklistentry.a - b0, nextticklistentry.b - b0, nextticklistentry.c - b0, nextticklistentry.a + b0, nextticklistentry.b + b0, nextticklistentry.c + b0)) {
@@ -581,52 +591,18 @@ public class WorldServer extends World implements org.bukkit.BlockChangeDelegate
}
this.methodProfiler.b();
- this.U.clear();
- return !this.M.isEmpty();
- }
+ // Spigot start
+ this.pendingTickEntries.clear();
+ this.nextPendingTickEntry = 0;
+ return !this.tickEntryQueue.isEmpty();
+ // Spigot end
+ }
}
public List a(Chunk chunk, boolean flag) {
- ArrayList arraylist = null;
- ChunkCoordIntPair chunkcoordintpair = chunk.l();
- int i = (chunkcoordintpair.x << 4) - 2;
- int j = i + 16 + 2;
- int k = (chunkcoordintpair.z << 4) - 2;
- int l = k + 16 + 2;
-
- for (int i1 = 0; i1 < 2; ++i1) {
- Iterator iterator;
-
- if (i1 == 0) {
- iterator = this.M.iterator();
- } else {
- iterator = this.U.iterator();
- /* CraftBukkit start - Comment out debug spam
- if (!this.U.isEmpty()) {
- System.out.println(this.U.size());
- }
- // CraftBukkit end */
- }
-
- while (iterator.hasNext()) {
- NextTickListEntry nextticklistentry = (NextTickListEntry) iterator.next();
-
- if (nextticklistentry.a >= i && nextticklistentry.a < j && nextticklistentry.c >= k && nextticklistentry.c < l) {
- if (flag) {
- this.L.remove(nextticklistentry);
- iterator.remove();
- }
-
- if (arraylist == null) {
- arraylist = new ArrayList();
- }
-
- arraylist.add(nextticklistentry);
- }
- }
- }
-
- return arraylist;
+ // Spigot start
+ return this.getNextTickEntriesForChunk(chunk, flag);
+ // Spigot end
}
/* CraftBukkit start - We prevent spawning in general, so this butchering is not needed
@@ -698,13 +674,15 @@ public class WorldServer extends World implements org.bukkit.BlockChangeDelegate
this.entitiesById = new IntHashMap();
}
- if (this.L == null) {
- this.L = new HashSet();
+ // Spigot start
+ if (this.tickEntriesByChunk == null) {
+ this.tickEntriesByChunk = new org.bukkit.craftbukkit.util.LongObjectHashMap<Set<NextTickListEntry>>();
}
- if (this.M == null) {
- this.M = new TreeSet();
+ if (this.tickEntryQueue == null) {
+ this.tickEntryQueue = new TreeSet<NextTickListEntry>();
}
+ // Spigot end
this.b(worldsettings);
super.a(worldsettings);
@@ -991,4 +969,62 @@ public class WorldServer extends World implements org.bukkit.BlockChangeDelegate
return this.setTypeIdAndData(x, y, z, typeId, data, 3);
}
// CraftBukkit end
+ // Spigot start
+ private void addNextTickIfNeeded(NextTickListEntry ent) {
+ long coord = LongHash.toLong(ent.a >> 4, ent.c >> 4);
+ Set<NextTickListEntry> chunkset = this.tickEntriesByChunk.get(coord);
+ if (chunkset == null) {
+ chunkset = new HashSet<NextTickListEntry>();
+ this.tickEntriesByChunk.put(coord, chunkset);
+ } else if (chunkset.contains(ent)) {
+ return;
+ }
+ chunkset.add(ent);
+ this.tickEntryQueue.add(ent);
+ }
+
+ private void removeNextTickIfNeeded(NextTickListEntry ent) {
+ long coord = LongHash.toLong(ent.a >> 4, ent.c >> 4);
+ Set<NextTickListEntry> chunkset = this.tickEntriesByChunk.get(coord);
+ if (chunkset != null) {
+ chunkset.remove(ent);
+ if (chunkset.isEmpty()) {
+ this.tickEntriesByChunk.remove(coord);
+ }
+ }
+ this.tickEntryQueue.remove(ent);
+ }
+
+ private List<NextTickListEntry> getNextTickEntriesForChunk(Chunk chunk, boolean remove) {
+ long coord = LongHash.toLong(chunk.x, chunk.z);
+ Set<NextTickListEntry> chunkset = this.tickEntriesByChunk.get(coord);
+ List<NextTickListEntry> list = null;
+ if (chunkset != null) {
+ list = new ArrayList<NextTickListEntry>(chunkset);
+ if (remove) {
+ this.tickEntriesByChunk.remove(coord);
+ this.tickEntryQueue.removeAll(list);
+ chunkset.clear();
+ }
+ }
+ // See if any on list of ticks being processed now
+ if (this.nextPendingTickEntry < this.pendingTickEntries.size()) {
+ int xmin = (chunk.x << 4);
+ int xmax = xmin + 16;
+ int zmin = (chunk.z << 4);
+ int zmax = zmin + 16;
+ int te_cnt = this.pendingTickEntries.size();
+ for (int i = this.nextPendingTickEntry; i < te_cnt; i++) {
+ NextTickListEntry ent = this.pendingTickEntries.get(i);
+ if ((ent.a >= xmin) && (ent.a < xmax) && (ent.c >= zmin) && (ent.c < zmax)) {
+ if (list == null) {
+ list = new ArrayList<NextTickListEntry>();
+ }
+ list.add(ent);
+ }
+ }
+ }
+ return list;
+ }
+ // Spigot end
}
--
1.8.1.2