Skip to content

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

Open
HashimTheArab wants to merge 1 commit into
Sandertv:masterfrom
HashimTheArab:perf/pooled-batch-decoding
Open

minecraft: cut per-batch decode allocations ~1500x with pooled decompression#504
HashimTheArab wants to merge 1 commit 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

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.

1 participant