Networking pipeline optimizations: reduce allocations and lock contention#395
Open
HanielCota wants to merge 6 commits into
Open
Networking pipeline optimizations: reduce allocations and lock contention#395HanielCota wants to merge 6 commits into
HanielCota wants to merge 6 commits into
Conversation
…ion per compressed packet
There was a problem hiding this comment.
Pull request overview
This PR introduces a micro-optimization to the server networking compression path by reusing a heap input buffer in PacketCompressor, aiming to reduce per-packet allocations and GC pressure during high-throughput packet bursts.
Changes:
- Add a reusable
inputBufferfield to avoid allocating a newbyte[]for each compressed packet. - Grow the reusable buffer only when a packet exceeds the current capacity, and reuse it across encodes.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+15
to
+17
| private final byte[] a = new byte[8192]; | ||
| + private byte[] inputBuffer = new byte[8192]; // PandaSpigot - reusable input buffer to avoid allocation per compressed packet | ||
| private final Deflater b; |
Comment on lines
+29
to
+35
| + // PandaSpigot start - grow reusable input buffer instead of allocating | ||
| + if (i > this.inputBuffer.length) { | ||
| + this.inputBuffer = new byte[i]; | ||
| + } | ||
| + bytebuf.readBytes(this.inputBuffer, 0, i); | ||
| + packetdataserializer.b(i); | ||
| + this.b.setInput(this.inputBuffer, 0, i); |
…icIntervalledCounter
…to avoid allocation per packet
…rd without HashSet, cache spawn position
|
Did you actually reason, test and profile the patches yourself or is it just, pardon my French, AI talking? |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR contains networking and login pipeline optimizations focused on reducing allocation pressure, eliminating lock contention, and cutting synchronous overhead in hot I/O and player-join paths.
Patch 1 — Reuse input buffer in PacketCompressor (lazy-init, 64KB cap)
File:
PacketCompressor.javaPacketCompressorpreviously allocated a newbyte[]for every packet exceeding the compression threshold.Changes:
inputBufferfield that is lazily initialized on first compressed packet (idle connections pay zero overhead)MAX_REUSABLE_SIZE)Impact: Reduces GC pressure during chunk sending and large packet bursts; idle connections retain no compressor buffer
Patch 2 — Replace synchronized packet limiter with lock-free AtomicIntervalledCounter
Files:
NetworkManager.java,AtomicIntervalledCounter.java(new)Every inbound packet acquired
synchronized(PACKET_LIMIT_LOCK)when packet limiting was enabled, creating unnecessary contention on a per-connection hot path.Changes:
AtomicIntervalledCounter, a lock-free counter usingAtomicLong+ dualLongAdderrotationHashMap<IntervalledCounter>withConcurrentHashMap<AtomicIntervalledCounter>PACKET_LIMIT_LOCKentirely;stopReadingPacketschanged tovolatileImpact: Eliminates monitor enter/exit overhead on every inbound packet when limiter is active
Patch 3 — Reuse PacketDataSerializer instances in Netty handlers
Files:
PacketDataSerializer.java,PacketEncoder.java,PacketDecoder.java,PacketCompressor.java,PacketDecompressor.javaEvery packet passing through the Netty pipeline triggered
new PacketDataSerializer(bytebuf)— a pure wrapper allocation with a singleByteBuffield.Changes:
PacketDataSerializernow uses aThreadLocalsingleton per thread, swapping the underlyingByteBufviaforBuffer(ByteBuf)PacketEncoder,PacketDecoder,PacketCompressor,PacketDecompressor) use the reused instanceImpact: Eliminates 1
PacketDataSerializerallocation per packet in encode/decode/compress/decompress pathsPatch 4 — Pool QueuedPacket objects
File:
NetworkManager.javaEvery packet that couldn't be sent immediately allocated a new
QueuedPacketwrapper for the outbound queue.Changes:
ArrayDeque<QueuedPacket>pool perNetworkManagerobtainQueuedPacket()reuses from pool or allocates only when emptyrecycleQueuedPacket()returns processed packets to the poolclearPacketQueue()also recycles on connection closeImpact: Eliminates
QueuedPacketallocations for the common case of queue-then-send immediately afterPatch 5 — Login pipeline micro-optimizations
Files:
PlayerList.java,World.javaThe login path had several small but measurable overheads that added up during mass joins.
Changes:
MC|Brandpacket once inPlayerListconstructor and reuse for every login (eliminatesPacketDataSerializer+Unpooled.buffer()alloc per player)HashSetinsendScoreboard(): uses a fixedScoreboardObjective[19]array with reference-equality checks instead ofSets.newHashSet()per loginWorld.getSpawn()result and invalidate only onsetSpawn()or world-border resize/center changes (eliminates repeatednew BlockPosition+ world-border checks)Impact: Reduces allocation churn and redundant computation during player join
Testing
./panda jarcompiles successfully./panda rbrebuilds patches cleanlypanda setupCompliance
// PandaSpigot start/endcommentsPatch files:
patches/server/0124-PandaSpigot-Reuse-input-buffer-in-PacketCompressor-t.patchpatches/server/0125-PandaSpigot-Replace-synchronized-packet-limiter-with.patchpatches/server/0126-PandaSpigot-Reuse-PacketDataSerializer-instances-in-.patchpatches/server/0127-PandaSpigot-Pool-QueuedPacket-objects-to-avoid-alloc.patchpatches/server/0128-PandaSpigot-Login-optimizations-cache-brand-packet-d.patch