PaperMC/Spigot-Server-Patches/0468-Stop-copy-on-write-operations-for-updating-light-dat.patch

320 lines
16 KiB
Diff
Raw Normal View History

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <spottedleaf@spottedleaf.dev>
Date: Mon, 27 Apr 2020 04:05:38 -0700
Subject: [PATCH] Stop copy-on-write operations for updating light data
Causes huge memory allocations + gc issues
diff --git a/src/main/java/net/minecraft/world/level/lighting/LightEngineStorage.java b/src/main/java/net/minecraft/world/level/lighting/LightEngineStorage.java
index 5f3d2c090d098834e38e447d93f1ea8184c8fb3e..5b1ff4ff87591dd4ff0b79e4ac6ff0494fc3d0f8 100644
--- a/src/main/java/net/minecraft/world/level/lighting/LightEngineStorage.java
+++ b/src/main/java/net/minecraft/world/level/lighting/LightEngineStorage.java
@@ -26,8 +26,8 @@ public abstract class LightEngineStorage<M extends LightEngineStorageArray<M>> e
protected final LongSet b = new LongOpenHashSet();
protected final LongSet c = new LongOpenHashSet();
protected final LongSet d = new LongOpenHashSet();
- protected volatile M e;
- protected final M f;
+ protected volatile M e_visible; protected final Object visibleUpdateLock = new Object(); // Paper - diff on change, should be "visible" - force compile fail on usage change
+ protected final M f; // Paper - diff on change, should be "updating"
protected final LongSet g = new LongOpenHashSet();
protected final LongSet h = new LongOpenHashSet();
protected final Long2ObjectMap<NibbleArray> i = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap());
@@ -41,8 +41,8 @@ public abstract class LightEngineStorage<M extends LightEngineStorageArray<M>> e
this.l = enumskyblock;
this.m = ilightaccess;
this.f = m0;
- this.e = m0.b();
- this.e.d();
+ this.e_visible = m0.b(); // Paper - avoid copying light data
+ this.e_visible.d(); // Paper - avoid copying light data
}
protected boolean g(long i) {
@@ -51,7 +51,15 @@ public abstract class LightEngineStorage<M extends LightEngineStorageArray<M>> e
@Nullable
protected NibbleArray a(long i, boolean flag) {
- return this.a(flag ? this.f : this.e, i);
+ // Paper start - avoid copying light data
+ if (flag) {
+ return this.a(this.f, i);
+ } else {
+ synchronized (this.visibleUpdateLock) {
+ return this.a(this.e_visible, i);
+ }
+ }
+ // Paper end - avoid copying light data
}
@Nullable
@@ -364,10 +372,12 @@ public abstract class LightEngineStorage<M extends LightEngineStorageArray<M>> e
protected void e() {
if (!this.g.isEmpty()) {
+ synchronized (this.visibleUpdateLock) { // Paper - avoid copying light data
M m0 = this.f.b();
m0.d();
- this.e = m0;
+ this.e_visible = m0; // Paper - avoid copying light data
+ } // Paper - avoid copying light data
this.g.clear();
}
diff --git a/src/main/java/net/minecraft/world/level/lighting/LightEngineStorageArray.java b/src/main/java/net/minecraft/world/level/lighting/LightEngineStorageArray.java
index 242a2c5dea1241b515b9eee7c334ab3c31ad9d12..ed7864c552054fc47c6010a094230ce4aebf1c54 100644
--- a/src/main/java/net/minecraft/world/level/lighting/LightEngineStorageArray.java
+++ b/src/main/java/net/minecraft/world/level/lighting/LightEngineStorageArray.java
@@ -9,10 +9,23 @@ public abstract class LightEngineStorageArray<M extends LightEngineStorageArray<
private final long[] b = new long[2];
private final NibbleArray[] c = new NibbleArray[2];
private boolean d;
- protected final Long2ObjectOpenHashMap<NibbleArray> a;
-
- protected LightEngineStorageArray(Long2ObjectOpenHashMap<NibbleArray> long2objectopenhashmap) {
- this.a = long2objectopenhashmap;
+ protected final com.destroystokyo.paper.util.map.QueuedChangesMapLong2Object<NibbleArray> data; // Paper - avoid copying light data
+ protected final boolean isVisible; // Paper - avoid copying light data
+ java.util.function.Function<Long, NibbleArray> lookup; // Paper - faster branchless lookup
+
+ // Paper start - avoid copying light data
+ protected LightEngineStorageArray(com.destroystokyo.paper.util.map.QueuedChangesMapLong2Object<NibbleArray> data, boolean isVisible) {
+ if (isVisible) {
+ data.performUpdatesLockMap();
+ }
+ this.data = data;
+ this.isVisible = isVisible;
+ if (isVisible) {
+ lookup = data::getVisibleAsync;
+ } else {
+ lookup = data::getUpdating;
+ }
+ // Paper end - avoid copying light data
this.c();
this.d = true;
}
@@ -20,16 +33,17 @@ public abstract class LightEngineStorageArray<M extends LightEngineStorageArray<
public abstract M b();
public void a(long i) {
- this.a.put(i, ((NibbleArray) this.a.get(i)).b());
+ if (this.isVisible) { throw new IllegalStateException("writing to visible data"); } // Paper - avoid copying light data
+ this.data.queueUpdate(i, ((NibbleArray) this.data.getUpdating(i)).b()); // Paper - avoid copying light data
this.c();
}
public boolean b(long i) {
- return this.a.containsKey(i);
+ return lookup.apply(i) != null; // Paper - avoid copying light data
}
@Nullable
- public NibbleArray c(long i) {
+ public final NibbleArray c(long i) { // Paper - final
if (this.d) {
for (int j = 0; j < 2; ++j) {
if (i == this.b[j]) {
@@ -38,7 +52,7 @@ public abstract class LightEngineStorageArray<M extends LightEngineStorageArray<
}
Optimize Light Engine Massive update to light to improve performance and chunk loading/generation. 1) Massive bit packing/unpacking optimizations and inlining. A lot of performance has to do with constant packing and unpacking of bits. We now inline a most bit operations, and re-use base x/y/z bits in many places. This helps with cpu level processing to just do all the math at once instead of having to jump in and out of function calls. This much logic also is likely over the JVM Inline limit for JIT too. 2) Applied a few of JellySquid's Phosphor mod optimizations such as - ensuring we don't notify neighbor chunks when neighbor chunk doesn't need to be notified - reduce hasLight checks in initializing light, and prob some more, they are tagged JellySquid where phosphor influence was used. 3) Optimize hot path accesses to getting updating chunk to have less branching 4) Optimize getBlock accesses to have less branching, and less unpacking 5) Have a separate urgent bucket for chunk light tasks. These tasks will always cut in line over non blocking light tasks. 6) Retain chunk priority while light tasks are enqueued. So if a task comes in at high priority but the queue is full of tasks already at a lower priority, before the task was simply added to the end. Now it can cut in line to the front. this applies for both urgent and non urgent tasks. 7) Buffer non urgent tasks even if queueUpdate is called multiple times to improve efficiency. 8) Fix NPE risk that crashes server in getting nibble data Fixes #3489 Fixes #3363
2020-06-05 07:25:11 +02:00
}
- NibbleArray nibblearray = (NibbleArray) this.a.get(i);
Optimize Light Engine Massive update to light to improve performance and chunk loading/generation. 1) Massive bit packing/unpacking optimizations and inlining. A lot of performance has to do with constant packing and unpacking of bits. We now inline a most bit operations, and re-use base x/y/z bits in many places. This helps with cpu level processing to just do all the math at once instead of having to jump in and out of function calls. This much logic also is likely over the JVM Inline limit for JIT too. 2) Applied a few of JellySquid's Phosphor mod optimizations such as - ensuring we don't notify neighbor chunks when neighbor chunk doesn't need to be notified - reduce hasLight checks in initializing light, and prob some more, they are tagged JellySquid where phosphor influence was used. 3) Optimize hot path accesses to getting updating chunk to have less branching 4) Optimize getBlock accesses to have less branching, and less unpacking 5) Have a separate urgent bucket for chunk light tasks. These tasks will always cut in line over non blocking light tasks. 6) Retain chunk priority while light tasks are enqueued. So if a task comes in at high priority but the queue is full of tasks already at a lower priority, before the task was simply added to the end. Now it can cut in line to the front. this applies for both urgent and non urgent tasks. 7) Buffer non urgent tasks even if queueUpdate is called multiple times to improve efficiency. 8) Fix NPE risk that crashes server in getting nibble data Fixes #3489 Fixes #3363
2020-06-05 07:25:11 +02:00
+ NibbleArray nibblearray = lookup.apply(i); // Paper - avoid copying light data
if (nibblearray == null) {
return null;
@@ -59,11 +73,13 @@ public abstract class LightEngineStorageArray<M extends LightEngineStorageArray<
@Nullable
public NibbleArray d(long i) {
- return (NibbleArray) this.a.remove(i);
+ if (this.isVisible) { throw new IllegalStateException("writing to visible data"); } // Paper - avoid copying light data
+ return (NibbleArray) this.data.queueRemove(i); // Paper - avoid copying light data
}
public void a(long i, NibbleArray nibblearray) {
- this.a.put(i, nibblearray);
+ if (this.isVisible) { throw new IllegalStateException("writing to visible data"); } // Paper - avoid copying light data
+ this.data.queueUpdate(i, nibblearray); // Paper - avoid copying light data
}
public void c() {
@@ -71,7 +87,6 @@ public abstract class LightEngineStorageArray<M extends LightEngineStorageArray<
this.b[i] = Long.MAX_VALUE;
this.c[i] = null;
}
-
}
public void d() {
diff --git a/src/main/java/net/minecraft/world/level/lighting/LightEngineStorageBlock.java b/src/main/java/net/minecraft/world/level/lighting/LightEngineStorageBlock.java
index 06c0635cb40114b078dace69bb5f987e24d25b0f..bba0dda3b8a4740bb18b9a1fc1821dc4e5890fb7 100644
--- a/src/main/java/net/minecraft/world/level/lighting/LightEngineStorageBlock.java
+++ b/src/main/java/net/minecraft/world/level/lighting/LightEngineStorageBlock.java
@@ -10,7 +10,7 @@ import net.minecraft.world.level.chunk.NibbleArray;
public class LightEngineStorageBlock extends LightEngineStorage<LightEngineStorageBlock.a> {
protected LightEngineStorageBlock(ILightAccess ilightaccess) {
- super(EnumSkyBlock.BLOCK, ilightaccess, new LightEngineStorageBlock.a(new Long2ObjectOpenHashMap()));
+ super(EnumSkyBlock.BLOCK, ilightaccess, new LightEngineStorageBlock.a(new com.destroystokyo.paper.util.map.QueuedChangesMapLong2Object<>(), false)); // Paper - avoid copying light data
}
@Override
@@ -23,13 +23,13 @@ public class LightEngineStorageBlock extends LightEngineStorage<LightEngineStora
public static final class a extends LightEngineStorageArray<LightEngineStorageBlock.a> {
- public a(Long2ObjectOpenHashMap<NibbleArray> long2objectopenhashmap) {
- super(long2objectopenhashmap);
+ public a(com.destroystokyo.paper.util.map.QueuedChangesMapLong2Object<NibbleArray> long2objectopenhashmap, boolean isVisible) { // Paper - avoid copying light data
+ super(long2objectopenhashmap, isVisible); // Paper - avoid copying light data
}
@Override
public LightEngineStorageBlock.a b() {
- return new LightEngineStorageBlock.a(this.a.clone());
+ return new a(this.data, true); // Paper - avoid copying light data
}
}
}
diff --git a/src/main/java/net/minecraft/world/level/lighting/LightEngineStorageSky.java b/src/main/java/net/minecraft/world/level/lighting/LightEngineStorageSky.java
index befc8f846c772d58ee687ad427bb71206b4dc43e..64d37f4c6a8167f47c80953a388ea6635490563a 100644
--- a/src/main/java/net/minecraft/world/level/lighting/LightEngineStorageSky.java
+++ b/src/main/java/net/minecraft/world/level/lighting/LightEngineStorageSky.java
@@ -23,15 +23,16 @@ public class LightEngineStorageSky extends LightEngineStorage<LightEngineStorage
private volatile boolean p;
protected LightEngineStorageSky(ILightAccess ilightaccess) {
- super(EnumSkyBlock.SKY, ilightaccess, new LightEngineStorageSky.a(new Long2ObjectOpenHashMap(), new Long2IntOpenHashMap(), Integer.MAX_VALUE));
+ super(EnumSkyBlock.SKY, ilightaccess, new LightEngineStorageSky.a(new com.destroystokyo.paper.util.map.QueuedChangesMapLong2Object<>(), new com.destroystokyo.paper.util.map.QueuedChangesMapLong2Int(), Integer.MAX_VALUE, false)); // Paper - avoid copying light data
}
@Override
protected int d(long i) {
long j = SectionPosition.e(i);
int k = SectionPosition.c(j);
- LightEngineStorageSky.a lightenginestoragesky_a = (LightEngineStorageSky.a) this.e;
- int l = lightenginestoragesky_a.c.get(SectionPosition.f(j));
+ synchronized (this.visibleUpdateLock) { // Paper - avoid copying light data
+ LightEngineStorageSky.a lightenginestoragesky_a = (LightEngineStorageSky.a) this.e_visible; // Paper - avoid copying light data - must be after lock acquire
+ int l = lightenginestoragesky_a.otherData.getVisibleAsync(SectionPosition.f(j)); // Paper - avoid copying light data
if (l != lightenginestoragesky_a.b && k < l) {
NibbleArray nibblearray = this.a(lightenginestoragesky_a, j); // Paper - decompile fix
@@ -52,6 +53,7 @@ public class LightEngineStorageSky extends LightEngineStorage<LightEngineStorage
} else {
return 15;
}
+ } // Paper - avoid copying light data
}
@Override
@@ -60,14 +62,14 @@ public class LightEngineStorageSky extends LightEngineStorage<LightEngineStorage
if (((LightEngineStorageSky.a) this.f).b > j) {
((LightEngineStorageSky.a) this.f).b = j;
- ((LightEngineStorageSky.a) this.f).c.defaultReturnValue(((LightEngineStorageSky.a) this.f).b);
+ ((LightEngineStorageSky.a) this.f).otherData.queueDefaultReturnValue(((LightEngineStorageSky.a) this.f).b); // Paper - avoid copying light data
}
long k = SectionPosition.f(i);
- int l = ((LightEngineStorageSky.a) this.f).c.get(k);
+ int l = ((LightEngineStorageSky.a) this.f).otherData.getUpdating(k); // Paper - avoid copying light data
if (l < j + 1) {
- ((LightEngineStorageSky.a) this.f).c.put(k, j + 1);
+ ((LightEngineStorageSky.a) this.f).otherData.queueUpdate(k, j + 1); // Paper - avoid copying light data
if (this.o.contains(k)) {
this.q(i);
if (l > ((LightEngineStorageSky.a) this.f).b) {
@@ -107,7 +109,7 @@ public class LightEngineStorageSky extends LightEngineStorage<LightEngineStorage
int k = SectionPosition.c(i);
- if (((LightEngineStorageSky.a) this.f).c.get(j) == k + 1) {
+ if (((LightEngineStorageSky.a) this.f).otherData.getUpdating(j) == k + 1) { // Paper - avoid copying light data
long l;
for (l = i; !this.g(l) && this.a(k); l = SectionPosition.a(l, EnumDirection.DOWN)) {
@@ -115,12 +117,12 @@ public class LightEngineStorageSky extends LightEngineStorage<LightEngineStorage
}
if (this.g(l)) {
- ((LightEngineStorageSky.a) this.f).c.put(j, k + 1);
+ ((LightEngineStorageSky.a) this.f).otherData.queueUpdate(j, k + 1); // Paper - avoid copying light data
if (flag) {
this.q(l);
}
} else {
- ((LightEngineStorageSky.a) this.f).c.remove(j);
+ ((LightEngineStorageSky.a) this.f).otherData.queueRemove(j); // Paper - avoid copying light data
}
}
@@ -134,7 +136,7 @@ public class LightEngineStorageSky extends LightEngineStorage<LightEngineStorage
protected void b(long i, boolean flag) {
this.d();
if (flag && this.o.add(i)) {
- int j = ((LightEngineStorageSky.a) this.f).c.get(i);
+ int j = ((LightEngineStorageSky.a) this.f).otherData.getUpdating(i); // Paper - avoid copying light data
if (j != ((LightEngineStorageSky.a) this.f).b) {
long k = SectionPosition.b(SectionPosition.b(i), j - 1, SectionPosition.d(i));
@@ -161,7 +163,7 @@ public class LightEngineStorageSky extends LightEngineStorage<LightEngineStorage
return nibblearray;
} else {
long j = SectionPosition.a(i, EnumDirection.UP);
- int k = ((LightEngineStorageSky.a) this.f).c.get(SectionPosition.f(i));
+ int k = ((LightEngineStorageSky.a) this.f).otherData.getUpdating(SectionPosition.f(i)); // Paper - avoid copying light data
if (k != ((LightEngineStorageSky.a) this.f).b && SectionPosition.c(j) < k) {
NibbleArray nibblearray1;
@@ -304,7 +306,7 @@ public class LightEngineStorageSky extends LightEngineStorage<LightEngineStorage
if (!this.o.contains(l)) {
return false;
} else {
- int i1 = ((LightEngineStorageSky.a) this.f).c.get(l);
+ int i1 = ((LightEngineStorageSky.a) this.f).otherData.getUpdating(l); // Paper - avoid copying light data
return SectionPosition.c(i1) == j + 16;
}
@@ -313,7 +315,7 @@ public class LightEngineStorageSky extends LightEngineStorage<LightEngineStorage
protected boolean n(long i) {
long j = SectionPosition.f(i);
- int k = ((LightEngineStorageSky.a) this.f).c.get(j);
+ int k = ((LightEngineStorageSky.a) this.f).otherData.getUpdating(j); // Paper - avoid copying light data
return k == ((LightEngineStorageSky.a) this.f).b || SectionPosition.c(i) >= k;
}
@@ -327,18 +329,21 @@ public class LightEngineStorageSky extends LightEngineStorage<LightEngineStorage
public static final class a extends LightEngineStorageArray<LightEngineStorageSky.a> {
private int b;
- private final Long2IntOpenHashMap c;
-
- public a(Long2ObjectOpenHashMap<NibbleArray> long2objectopenhashmap, Long2IntOpenHashMap long2intopenhashmap, int i) {
- super(long2objectopenhashmap);
- this.c = long2intopenhashmap;
- long2intopenhashmap.defaultReturnValue(i);
+ private final com.destroystokyo.paper.util.map.QueuedChangesMapLong2Int otherData; // Paper - avoid copying light data
+
+ // Paper start - avoid copying light data
+ public a(com.destroystokyo.paper.util.map.QueuedChangesMapLong2Object<NibbleArray> data, com.destroystokyo.paper.util.map.QueuedChangesMapLong2Int otherData, int i, boolean isVisible) {
+ super(data, isVisible);
+ this.otherData = otherData;
+ otherData.queueDefaultReturnValue(i);
+ // Paper end - avoid copying light data
this.b = i;
}
@Override
public LightEngineStorageSky.a b() {
- return new LightEngineStorageSky.a(this.a.clone(), this.c.clone(), this.b);
+ this.otherData.performUpdatesLockMap(); // Paper - avoid copying light data
+ return new LightEngineStorageSky.a(this.data, this.otherData, this.b, true); // Paper - avoid copying light data
}
}
}