Optimize NibbleArray to use pooled buffers

Massively reduces memory allocation of 2048 byte buffers by using
an object pool for these.
This commit is contained in:
Aikar 2020-05-07 01:32:02 -04:00
parent bb418b5532
commit 9204e8c641
3 changed files with 255 additions and 20 deletions

View file

@ -2090,18 +2090,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+
+public final class PooledObjects<E> {
+
+ public static final PooledObjects<MutableInt> POOLED_MUTABLE_INTEGERS = new PooledObjects<>(new PooledObjectHandler<MutableInt>() {
+ @Override
+ public MutableInt createNew() {
+ return new MutableInt();
+ }
+
+ @Override
+ public void onAcquire(final MutableInt value) {}
+
+ @Override
+ public void onRelease(final MutableInt value) {}
+ }, 200, -1);
+ public static final PooledObjects<MutableInt> POOLED_MUTABLE_INTEGERS = new PooledObjects<>(MutableInt::new, 200, -1);
+
+ private final PooledObjectHandler<E> handler;
+ private final int maxPoolSize;
@ -2171,16 +2160,16 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ }
+
+ /** This object is restricted from interacting with any pool */
+ static interface PooledObjectHandler<E> {
+ public static interface PooledObjectHandler<E> {
+
+ /**
+ * Must return a non-null object
+ */
+ E createNew();
+
+ void onAcquire(final E value);
+ default void onAcquire(final E value) {}
+
+ void onRelease(final E value);
+ default void onRelease(final E value) {}
+ }
+
+ protected static class IsolatedPool<E> {

View file

@ -4,7 +4,8 @@ Date: Wed, 6 May 2020 04:53:35 -0400
Subject: [PATCH] Optimize Network Manager and add advanced packet support
Adds ability for 1 packet to bundle other packets to follow it
adds ability for a packet to delay sending more packets until a state is ready.
Adds ability for a packet to delay sending more packets until a state is ready.
Adds ability to clean up a packet when it is finished (not sent, or finished encoding), such as freeing buffers
Removes synchronization from sending packets
Removes processing packet queue off of main thread
@ -93,7 +94,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
- this.packetQueue.add(new NetworkManager.QueuedPacket(packet, genericfuturelistener));
+ // Paper start - handle oversized packets better
+ boolean connected = this.isConnected();
+ if (!connected && !preparing) return; // Do nothing
+ if (!connected && !preparing) {
+ packet.onPacketDone();
+ return; // Do nothing
+ }
+ if (connected && (InnerUtil.canSendImmediate(this, packet) || (
+ MCUtil.isMainThread() && packet.isReady() && this.packetQueue.isEmpty() &&
+ (packet.getExtraPackets() == null || packet.getExtraPackets().isEmpty())
@ -109,13 +113,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ } else {
+ java.util.List<NetworkManager.QueuedPacket> packets = new java.util.ArrayList<>(1 + extraPackets.size());
+ packets.add(new NetworkManager.QueuedPacket(packet, null)); // delay the future listener until the end of the extra packets
+
+ for (int i = 0, len = extraPackets.size(); i < len;) {
+ Packet extra = extraPackets.get(i);
+ boolean end = ++i == len;
+ packets.add(new NetworkManager.QueuedPacket(extra, end ? genericfuturelistener : null)); // append listener to the end
+ }
+
+ this.packetQueue.addAll(packets); // atomic
+ }
+ this.sendPacketQueue();
@ -181,13 +185,26 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
public void a() {
this.o();
@@ -0,0 +0,0 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
return this.socketAddress;
}
+ public void clearPacketQueue() { QueuedPacket packet; while ((packet = packetQueue.poll()) != null) packet.getPacket().onPacketDone(); } // Paper
public void close(IChatBaseComponent ichatbasecomponent) {
// Spigot Start
this.preparing = false;
+ this.packetQueue.clear(); // Paper - just incase its closed before we ever get to the main thread to do this
+ clearPacketQueue(); // Paper
// Spigot End
if (this.channel.isOpen()) {
this.channel.close(); // We can't wait as this may be called from an event loop.
@@ -0,0 +0,0 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
} else if (this.i() != null) {
this.i().a(new ChatMessage("multiplayer.disconnect.generic", new Object[0]));
}
- this.packetQueue.clear(); // Free up packet queue.
+ clearPacketQueue(); // Paper
// Paper start - Add PlayerConnectionCloseEvent
final PacketListener packetListener = this.i();
if (packetListener instanceof PlayerConnection) {
diff --git a/src/main/java/net/minecraft/server/Packet.java b/src/main/java/net/minecraft/server/Packet.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/Packet.java
@ -196,11 +213,25 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
void a(T t0);
// Paper start
+ default void onPacketDone() {}
+ default boolean isReady() { return true; }
+ default java.util.List<Packet> getExtraPackets() { return null; }
default boolean packetTooLarge(NetworkManager manager) {
return false;
}
diff --git a/src/main/java/net/minecraft/server/PacketEncoder.java b/src/main/java/net/minecraft/server/PacketEncoder.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/PacketEncoder.java
+++ b/src/main/java/net/minecraft/server/PacketEncoder.java
@@ -0,0 +0,0 @@ public class PacketEncoder extends MessageToByteEncoder<Packet<?>> {
} else {
throw throwable;
}
- }
+ } finally { try { packet.onPacketDone(); } catch (Exception e) { e.printStackTrace(); } ; } // Paper
// Paper start
int packetLength = bytebuf.readableBytes();
diff --git a/src/main/java/net/minecraft/server/PlayerList.java b/src/main/java/net/minecraft/server/PlayerList.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/PlayerList.java

View file

@ -0,0 +1,215 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Wed, 6 May 2020 23:30:30 -0400
Subject: [PATCH] Optimize NibbleArray to use pooled buffers
Massively reduces memory allocation of 2048 byte buffers by using
an object pool for these.
diff --git a/src/main/java/net/minecraft/server/ChunkRegionLoader.java b/src/main/java/net/minecraft/server/ChunkRegionLoader.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/ChunkRegionLoader.java
+++ b/src/main/java/net/minecraft/server/ChunkRegionLoader.java
@@ -0,0 +0,0 @@ public class ChunkRegionLoader {
}
if (nibblearray != null && !nibblearray.c()) {
- nbttagcompound2.setByteArray("BlockLight", nibblearray.asBytes());
+ nbttagcompound2.setByteArray("BlockLight", nibblearray.getIfSet()); // Paper
}
if (nibblearray1 != null && !nibblearray1.c()) {
- nbttagcompound2.setByteArray("SkyLight", nibblearray1.asBytes());
+ nbttagcompound2.setByteArray("SkyLight", nibblearray1.getIfSet()); // Paper
}
nbttaglist.add(nbttagcompound2);
diff --git a/src/main/java/net/minecraft/server/LightEngineStorageSky.java b/src/main/java/net/minecraft/server/LightEngineStorageSky.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/LightEngineStorageSky.java
+++ b/src/main/java/net/minecraft/server/LightEngineStorageSky.java
@@ -0,0 +0,0 @@ public class LightEngineStorageSky extends LightEngineStorage<LightEngineStorage
j = SectionPosition.a(j, EnumDirection.UP);
}
- return new NibbleArray((new NibbleArrayFlat(nibblearray1, 0)).asBytes());
+ return new NibbleArray((new NibbleArrayFlat(nibblearray1, 0)).asBytes(), true); // Paper - mark buffer as safe
} else {
return new NibbleArray();
}
diff --git a/src/main/java/net/minecraft/server/NibbleArray.java b/src/main/java/net/minecraft/server/NibbleArray.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/NibbleArray.java
+++ b/src/main/java/net/minecraft/server/NibbleArray.java
@@ -0,0 +0,0 @@
package net.minecraft.server;
+import com.destroystokyo.paper.util.pooled.PooledObjects;
+
import javax.annotation.Nullable;
public class NibbleArray {
- @Nullable
- protected byte[] a;
+ // Paper start
+ public static byte[] EMPTY_NIBBLE = new byte[2048];
+ public static final PooledObjects<byte[]> BYTE_2048 = new PooledObjects<>(() -> new byte[2048], 16384, 1);
+ public static void releaseBytes(byte[] bytes) {
+ if (bytes != EMPTY_NIBBLE) {
+ System.arraycopy(EMPTY_NIBBLE, 0, bytes, 0, 2048);
+ BYTE_2048.release(bytes);
+ }
+ }
+
+ public byte[] getIfSet() {
+ return this.a != null ? this.a : EMPTY_NIBBLE;
+ }
+ public byte[] getCloneIfSet() {
+ if (a == null) {
+ return EMPTY_NIBBLE;
+ }
+ byte[] ret = BYTE_2048.acquire();
+ System.arraycopy(getIfSet(), 0, ret, 0, 2048);
+ return ret;
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ if (this.a != null) {
+ releaseBytes(this.a);
+ this.a = null;
+ }
+ } finally {
+ super.finalize();
+ }
+ }
+ // Paper end
+ @Nullable protected byte[] a;
+
public NibbleArray() {}
public NibbleArray(byte[] abyte) {
+ // Paper start
+ this(abyte, false);
+ }
+ public NibbleArray(byte[] abyte, boolean isSafe) {
this.a = abyte;
+ if (!isSafe) this.a = getCloneIfSet(); // Paper - clone for safety
+ // Paper end
if (abyte.length != 2048) {
throw (IllegalArgumentException) SystemUtils.c(new IllegalArgumentException("ChunkNibbleArrays should be 2048 bytes not: " + abyte.length));
}
@@ -0,0 +0,0 @@ public class NibbleArray {
public void a(int i, int j) { // PAIL: private -> public
if (this.a == null) {
- this.a = new byte[2048];
+ this.a = BYTE_2048.acquire(); // Paper
}
int k = this.d(i);
@@ -0,0 +0,0 @@ public class NibbleArray {
public byte[] asBytes() {
if (this.a == null) {
- this.a = new byte[2048];
+ this.a = BYTE_2048.acquire(); // Paper
}
return this.a;
@@ -0,0 +0,0 @@ public class NibbleArray {
public NibbleArray copy() { return this.b(); } // Paper - OBFHELPER
public NibbleArray b() {
- return this.a == null ? new NibbleArray() : new NibbleArray((byte[]) this.a.clone());
+ return this.a == null ? new NibbleArray() : new NibbleArray(this.a); // Paper - clone in ctor
}
public String toString() {
diff --git a/src/main/java/net/minecraft/server/NibbleArrayFlat.java b/src/main/java/net/minecraft/server/NibbleArrayFlat.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/NibbleArrayFlat.java
+++ b/src/main/java/net/minecraft/server/NibbleArrayFlat.java
@@ -0,0 +0,0 @@ public class NibbleArrayFlat extends NibbleArray {
@Override
public byte[] asBytes() {
- byte[] abyte = new byte[2048];
+ byte[] abyte = BYTE_2048.acquire(); // Paper
for (int i = 0; i < 16; ++i) {
System.arraycopy(this.a, 0, abyte, i * 128, 128);
diff --git a/src/main/java/net/minecraft/server/PacketPlayOutLightUpdate.java b/src/main/java/net/minecraft/server/PacketPlayOutLightUpdate.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/PacketPlayOutLightUpdate.java
+++ b/src/main/java/net/minecraft/server/PacketPlayOutLightUpdate.java
@@ -0,0 +0,0 @@ public class PacketPlayOutLightUpdate implements Packet<PacketListenerPlayOut> {
private List<byte[]> h;
public PacketPlayOutLightUpdate() {}
+ // Paper start
+ private final java.util.List<byte[]> usedBytes = new java.util.ArrayList<>();
+
+ @Override
+ public void onPacketDone() {
+ usedBytes.forEach(NibbleArray::releaseBytes);
+ usedBytes.clear();
+ }
+ // Paper end
public PacketPlayOutLightUpdate(ChunkCoordIntPair chunkcoordintpair, LightEngine lightengine) {
this.a = chunkcoordintpair.x;
@@ -0,0 +0,0 @@ public class PacketPlayOutLightUpdate implements Packet<PacketListenerPlayOut> {
this.g = Lists.newArrayList();
this.h = Lists.newArrayList();
+ byte[] lastBytes; // Paper
for (int i = 0; i < 18; ++i) {
NibbleArray nibblearray = lightengine.a(EnumSkyBlock.SKY).a(SectionPosition.a(chunkcoordintpair, -1 + i));
NibbleArray nibblearray1 = lightengine.a(EnumSkyBlock.BLOCK).a(SectionPosition.a(chunkcoordintpair, -1 + i));
@@ -0,0 +0,0 @@ public class PacketPlayOutLightUpdate implements Packet<PacketListenerPlayOut> {
this.e |= 1 << i;
} else {
this.c |= 1 << i;
- this.g.add(nibblearray.asBytes().clone());
+ this.g.add(lastBytes = nibblearray.getCloneIfSet()); usedBytes.add(lastBytes); // Paper
}
}
@@ -0,0 +0,0 @@ public class PacketPlayOutLightUpdate implements Packet<PacketListenerPlayOut> {
this.f |= 1 << i;
} else {
this.d |= 1 << i;
- this.h.add(nibblearray1.asBytes().clone());
+ this.h.add(lastBytes = nibblearray1.getCloneIfSet()); usedBytes.add(lastBytes); // Paper
}
}
}
@@ -0,0 +0,0 @@ public class PacketPlayOutLightUpdate implements Packet<PacketListenerPlayOut> {
this.g = Lists.newArrayList();
this.h = Lists.newArrayList();
+ byte[] lastBytes; // Paper
for (int k = 0; k < 18; ++k) {
NibbleArray nibblearray;
if ((this.c & 1 << k) != 0) {
nibblearray = lightengine.a(EnumSkyBlock.SKY).a(SectionPosition.a(chunkcoordintpair, -1 + k));
if (nibblearray != null && !nibblearray.c()) {
- this.g.add(nibblearray.asBytes().clone());
+ this.g.add(lastBytes = nibblearray.getCloneIfSet()); usedBytes.add(lastBytes); // Paper
} else {
this.c &= ~(1 << k);
if (nibblearray != null) {
@@ -0,0 +0,0 @@ public class PacketPlayOutLightUpdate implements Packet<PacketListenerPlayOut> {
if ((this.d & 1 << k) != 0) {
nibblearray = lightengine.a(EnumSkyBlock.BLOCK).a(SectionPosition.a(chunkcoordintpair, -1 + k));
if (nibblearray != null && !nibblearray.c()) {
- this.h.add(nibblearray.asBytes().clone());
+ this.h.add(lastBytes = nibblearray.getCloneIfSet()); usedBytes.add(lastBytes); // Paper
} else {
this.d &= ~(1 << k);
if (nibblearray != null) {