Skip to content

minecraft: cut per-batch decode allocations ~1500x with pooled decompression - #504

Merged
TwistedAsylumMC merged 3 commits into
Sandertv:masterfrom
HashimTheArab:perf/pooled-batch-decoding
Sep 26, 2026
Merged

TwistedAsylumMC merged 3 commits into
Sandertv:masterfrom
HashimTheArab:perf/pooled-batch-decoding

Conversation

@HashimTheArab

Copy link
Copy Markdown
Contributor

Reading a compressed batch used to allocate the full decompressed payload plus a packet collection on every read. The read loops now decompress into a pooled buffer and allocate ~50 bytes per batch instead of ~74 KB, and a single queued packet no longer pins the entire decompressed batch's backing array.

Benchmark (64 packets × 1 KiB in one flate batch, Apple M3 Pro, three runs):

Read path Time Allocated
flateCompression.Decompress (old) 19.3–21.0 µs/op ~74 KB/op, 2 allocs/op
Decoder.DecodeFunc (new) 16.7–19.7 µs/op 48–50 B/op, 1 alloc/op

Changes

  • Add Decoder.DecodeFunc, which calls a callback for each packet in a batch with slices borrowed from a pooled decompression buffer. The batch is validated in full before the first callback, so a malformed batch dispatches no packets, preserving the all-or-nothing behavior of Decode.
  • Switch the dialer and listener read loops to DecodeFunc. Packets are copied only at the two points where they can outlive the batch (the Conn.packets channel and deferPacket), via packetData.ensureOwned.
  • Add append-style decompression for flate and snappy behind an unexported appendDecompression interface, so batches decompress into an existing pooled buffer. Buffers above 1 MiB are dropped instead of returned to the pool.
  • Decoder.Decode keeps its API and behavior. Its returned packets are now backed by one caller-owned allocation, where they previously aliased the decoder's internal read buffer and were only valid until the next Decode call.
  • PacketFunc now receives a copy of the payload, so it can retain it safely.

Compression-limit handling is untouched; this is orthogonal to #503.

Validation

  • go build ./..., go vet ./..., go test -race -count=1 ./...

Decoding a compressed batch allocated the full decompressed payload and
a packet collection on every read. Decoder.DecodeFunc now decompresses
into a pooled buffer and passes borrowed packet slices to a callback,
copying only the packets that outlive the batch. The dialer and listener
read loops use it, so a queued packet no longer pins the entire
decompressed batch's backing array.
@HashimTheArab HashimTheArab changed the title minecraft: cut per-batch decode allocations with pooled decompression minecraft: cut per-batch decode allocations ~1500x with pooled decompression Aug 13, 2026
@HashimTheArab

Copy link
Copy Markdown
Contributor Author

ive been running this for a couple of days now and its running great, no issues

@NopeNotDark NopeNotDark left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, Thanks for the PR!

@TwistedAsylumMC
TwistedAsylumMC merged commit 80c61e3 into Sandertv:master Sep 26, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants