Add experimental local buffer API. Remove allocation when writing chunk packets (should help with startup time)

This commit is contained in:
TheMode 2021-08-13 01:08:19 +02:00
parent 82602a686e
commit 78126fad6c
2 changed files with 35 additions and 18 deletions

View File

@ -10,6 +10,7 @@ import net.minestom.server.instance.palette.Palette;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import net.minestom.server.tag.Tag;
import net.minestom.server.utils.PacketUtils;
import net.minestom.server.utils.Utils;
import net.minestom.server.utils.binary.BinaryReader;
import net.minestom.server.utils.binary.BinaryWriter;
@ -32,8 +33,7 @@ public class ChunkDataPacket implements ServerPacket {
public Map<Integer, Block> entries = new HashMap<>();
private static final byte CHUNK_SECTION_COUNT = 16;
private static final int MAX_BITS_PER_ENTRY = 16;
private static final int MAX_BUFFER_SIZE = (Short.BYTES + Byte.BYTES + 5 * Byte.BYTES + (4096 * MAX_BITS_PER_ENTRY / Long.SIZE * Long.BYTES)) * CHUNK_SECTION_COUNT + 256 * Integer.BYTES;
private static final PacketUtils.Cache BLOCK_CACHE = PacketUtils.Cache.get("chunk-block-cache", 262_144);
/**
* Heightmaps NBT, as read from raw packet data.
@ -49,7 +49,7 @@ public class ChunkDataPacket implements ServerPacket {
writer.writeInt(chunkX);
writer.writeInt(chunkZ);
ByteBuffer blocks = ByteBuffer.allocate(MAX_BUFFER_SIZE);
ByteBuffer blocks = BLOCK_CACHE.retrieveLocal();
Int2LongRBTreeMap maskMap = new Int2LongRBTreeMap();

View File

@ -21,6 +21,8 @@ import org.jetbrains.annotations.NotNull;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.zip.Deflater;
/**
@ -30,22 +32,12 @@ import java.util.zip.Deflater;
public final class PacketUtils {
private static final PacketListenerManager PACKET_LISTENER_MANAGER = MinecraftServer.getPacketListenerManager();
private static final ThreadLocal<Deflater> COMPRESSOR = ThreadLocal.withInitial(Deflater::new);
private static final ThreadLocal<ByteBuffer> PACKET_BUFFER = ThreadLocal.withInitial(() -> ByteBuffer.allocateDirect(Server.SOCKET_BUFFER_SIZE));
private static final ThreadLocal<ByteBuffer> COMPRESSION_CACHE = ThreadLocal.withInitial(() -> ByteBuffer.allocateDirect(Server.SOCKET_BUFFER_SIZE));
private static final Cache PACKET_BUFFER = Cache.get("packet-buffer", Server.SOCKET_BUFFER_SIZE);
private static final Cache COMPRESSION_CACHE = Cache.get("compression-buffer", Server.SOCKET_BUFFER_SIZE);
private PacketUtils() {
}
@ApiStatus.Internal
static ByteBuffer localBuffer() {
return PACKET_BUFFER.get().clear();
}
@ApiStatus.Internal
static ByteBuffer compressionCache() {
return COMPRESSION_CACHE.get().clear();
}
/**
* Sends a packet to an audience. This method performs the following steps in the
* following order:
@ -162,7 +154,7 @@ public final class PacketUtils {
// Packet large enough, compress
final int limitCache = buffer.limit();
buffer.position(contentStart).limit(contentStart + packetSize);
var uncompressedCopy = compressionCache().put(buffer).flip();
var uncompressedCopy = COMPRESSION_CACHE.retrieveLocal().put(buffer).flip();
buffer.position(contentStart).limit(limitCache);
var deflater = COMPRESSOR.get();
@ -198,15 +190,40 @@ public final class PacketUtils {
}
public static ByteBuffer createFramedPacket(@NotNull ServerPacket packet) {
return createFramedPacket(localBuffer(), packet);
return createFramedPacket(PACKET_BUFFER.retrieveLocal(), packet);
}
public static ByteBuffer createFramedPacket(@NotNull ServerPacket packet, boolean compression) {
return createFramedPacket(localBuffer(), packet, compression);
return createFramedPacket(PACKET_BUFFER.retrieveLocal(), packet, compression);
}
public static ByteBuffer allocateTrimmedPacket(@NotNull ServerPacket packet) {
final var temp = PacketUtils.createFramedPacket(packet);
return ByteBuffer.allocateDirect(temp.position()).put(temp.flip()).asReadOnlyBuffer();
}
@ApiStatus.Internal
public static final class Cache {
private static final Map<String, Cache> CACHES = new ConcurrentHashMap<>();
private final String name;
private final ThreadLocal<ByteBuffer> cache;
private Cache(String name, int size) {
this.name = name;
this.cache = ThreadLocal.withInitial(() -> ByteBuffer.allocateDirect(size));
}
public static Cache get(String name, int size) {
return CACHES.computeIfAbsent(name, s -> new Cache(name, size));
}
public String name() {
return name;
}
public ByteBuffer retrieveLocal() {
return cache.get().clear();
}
}
}