perf: encode RESP replies straight into the output buffer (drop per-reply BytesMut)#505
Merged
Merged
Conversation
…eply BytesMut) Every reply was encoded into a fresh `BytesMut::with_capacity(64)` and then copied into the connection's `Vec<u8>` output buffer -- one heap allocation + one buffer-to-buffer copy per reply, on every command. `ironcache_protocol::encode` (and its seven internal helpers) are now generic over `&mut impl BufMut` instead of `&mut BytesMut`. `BytesMut` still satisfies `BufMut`, so the signature change is backward-compatible for any external caller; and `Vec<u8>` is also a `BufMut` sink, so the reply encoder can write DIRECTLY into the serve loop's output buffer. All six reply-encode wrappers (the serve hot path + the multikey / spanning-move / spanning-combine / whole-keyspace / coordinator cross-shard reply encoders) now call `encode(out, ..)` with no temp buffer; `encode_to_vec` likewise builds a `Vec` directly. Optimization #3 of the datapath hot-path analysis (per-op allocation removal), on the reply side; complements the #504 read-buffer O(P^2)->O(P) fix on the request side. Byte-identical output (verified: 79 protocol encode/decode tests, 571 ironcache-server tests incl. the RESP2/RESP3 reply-shaping suites, 272 ironcache lib tests all pass); fmt + clippy -D warnings (with --features io_uring) + invariant lints clean; 0 dashes. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Zeke <ezequiel.lares@outlook.com>
Owner
Author
Deep self-review: verdict byte-identical + correctThe adversarial review read the full diff, the complete encoder, all 6 wrapper sites, AND the
The differential-vs-real-redis CI check is the byte-for-byte gate. Merging on green. |
perf-gate (A5)Same-runner ratchet of HEAD against the merge-base (both rebuilt and measured in this job).
Overall: PASS
|
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.
What
Every RESP reply was encoded into a fresh
BytesMut::with_capacity(64)and then copied into the connection'sVec<u8>output buffer -- one heap allocation + one buffer-to-buffer copy per reply, on every command.ironcache_protocol::encode(and its 7 internal helpers) are now generic over&mut impl BufMutinstead of&mut BytesMut. SinceBytesMut: BufMutthe change is backward-compatible for any external caller, andVec<u8>is also aBufMutsink -- so the reply encoder writes directly into the serve loop's output buffer.Applied at all six reply-encode wrappers:
serve.rs(the hot single-key reply path)multikey.rs,spanning_move.rs,spanning_combine.rs,whole_keyspace.rs,coordinator.rs(the cross-shard fan-out reply encoders -- same wasteful pattern)encode_to_vecnow builds aVecdirectly (noBytesMutround-trip)Why
Optimization #3 of the datapath hot-path analysis (per-op allocation removal), on the reply side -- complements #504's O(P^2)->O(P) read-buffer fix on the request side. Both target the per-op allocation/copy asymmetry vs Redis identified while benchmarking against Redis 8 / Dragonfly.
Correctness
The encoded bytes are byte-identical -- this only changes where the encoder writes, not what. Verified:
ironcache-servertests (incl. the RESP2/RESP3 reply-shaping suites)ironcachelib tests (the cross-shard reply paths)fmt --all --check+clippy -D warnings --features io_uring+ invariant lints clean; 0 dashesThe differential-vs-real-redis CI check is the byte-for-byte gate.
Note on measured impact
Like #504, isolating a throughput delta needs a CPU-bound regime (the prior AWS runs were latency/loadgen-bound). This is a strict reduction in per-reply allocation + copy work; a consolidated CPU-bound re-bench of the accumulated datapath optimizations is a follow-up.
🤖 Generated with Claude Code