Paper/patches/server/0626-Optimize-HashMapPalette.patch
Spottedleaf 8c5b837e05 Rework async chunk api implementation
Firstly, the old methods all routed to the CompletableFuture method.
However, the CF method could not guarantee that if the caller
was off-main that the future would be "completed" on-main. Since
the callback methods used the CF one, this meant that the callback
methods did not guarantee that the callbacks were to be called on
the main thread.

Now, all methods route to getChunkAtAsync(x, z, gen, urgent, cb)
so that the methods with the callback are guaranteed to invoke
the callback on the main thread. The CF behavior remains unchanged;
it may still appear to complete on main if invoked off-main.

Secondly, remove the scheduleOnMain invocation in the async
chunk completion. This unnecessarily delays the callback
by 1 tick.

Thirdly, add getChunksAtAsync(minX, minZ, maxX, maxZ, ...) which
will load chunks within an area. This method is provided as a helper
as keeping all chunks loaded within an area can be complicated to
implement for plugins (due to the lacking ticket API), and is
already implemented internally anyways.

Fourthly, remove the ticket addition that occured with getChunkAt
and getChunkAtAsync. The ticket addition may delay the unloading
of the chunk unnecessarily. It also fixes a very rare timing bug
where the future/callback would be completed after the chunk
unloads.
2024-11-18 23:00:59 -08:00

58 lines
3.3 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: stonar96 <minecraft.stonar96@gmail.com>
Date: Sun, 17 Jan 2021 01:11:36 +0100
Subject: [PATCH] Optimize HashMapPalette
HashMapPalette uses an instance of CrudeIncrementalIntIdentityHashBiMap
internally. A Palette has a preset maximum size = 1 << bits.
CrudeIncrementalIntIdentityHashBiMap has an initial size but is
automatically resized. The CrudeIncrementalIntIdentityHashBiMap is created
with the maximum size in the constructor of HashMapPalette, with the aim
that it doesn't need to be resized anymore. However, there are two things
that I think Mojang hasn't considered here:
1) The CrudeIncrementalIntIdentityHashBiMap is resized, when its initial
size is reached and not the next time, when a further object is added.
2) HashMapPalette adds objects (unnecessarily) before checking if the
initial size of CrudeIncrementalIntIdentityHashBiMap is reached.
This means to actually avoid resize operations in
CrudeIncrementalIntIdentityHashBiMap, one has to add 2 to the initial size
or add 1 and check the size before adding objects. This commit implements
the second approach. Note that this isn't only an optimization but also
makes async reads of Palettes fail-safe. An async read while the
CrudeIncrementalIntIdentityHashBiMap is resized is fatal and can even lead
to corrupted data. This is also something that Anti-Xray is currently
relying on.
diff --git a/src/main/java/net/minecraft/world/level/chunk/HashMapPalette.java b/src/main/java/net/minecraft/world/level/chunk/HashMapPalette.java
index ac673cb38755852eef37e915f157f6a702117306..98dbeaf8bde15940e5b5d5d1f13fd4bb32f0a10d 100644
--- a/src/main/java/net/minecraft/world/level/chunk/HashMapPalette.java
+++ b/src/main/java/net/minecraft/world/level/chunk/HashMapPalette.java
@@ -20,7 +20,7 @@ public class HashMapPalette<T> implements Palette<T> {
}
public HashMapPalette(IdMap<T> idList, int indexBits, PaletteResize<T> listener) {
- this(idList, indexBits, listener, CrudeIncrementalIntIdentityHashBiMap.create(1 << indexBits));
+ this(idList, indexBits, listener, CrudeIncrementalIntIdentityHashBiMap.create((1 << indexBits) + 1)); // Paper - Perf: Avoid unnecessary resize operation in CrudeIncrementalIntIdentityHashBiMap
}
private HashMapPalette(IdMap<T> idList, int indexBits, PaletteResize<T> listener, CrudeIncrementalIntIdentityHashBiMap<T> map) {
@@ -38,10 +38,16 @@ public class HashMapPalette<T> implements Palette<T> {
public int idFor(T object) {
int i = this.values.getId(object);
if (i == -1) {
- i = this.values.add(object);
- if (i >= 1 << this.bits) {
+ // Paper start - Perf: Avoid unnecessary resize operation in CrudeIncrementalIntIdentityHashBiMap and optimize
+ // We use size() instead of the result from add(K)
+ // This avoids adding another object unnecessarily
+ // Without this change, + 2 would be required in the constructor
+ if (this.values.size() >= 1 << this.bits) {
i = this.resizeHandler.onResize(this.bits + 1, object);
+ } else {
+ i = this.values.add(object);
}
+ // Paper end - Perf: Avoid unnecessary resize operation in CrudeIncrementalIntIdentityHashBiMap and optimize
}
return i;