Serialize bulk payloads as bytes, not byte-by-byte - #3156
Merged
Conversation
Contributor
|
😎 Merged successfully - details. |
`DataMessage::Vec` and `InterDaemonEvent::Output.data` reached the
encoder through serde's default slice impl, which emits `serialize_seq`
plus one `serialize_u8` per element -- postcard then does a `try_push`
per byte. Routing them through `serialize_bytes`/`deserialize_bytes`
moves the whole slice at once.
The encoding does not change. postcard writes a varint length followed
by the raw bytes either way, so this needs no wire-format version bump;
`encoding_is_unchanged_from_the_seq_form` pins that against the default
seq encoding at the varint prefix boundaries (127/128) and either side.
The golden vectors in tests/uhlc_wire_format.rs are untouched and still
pass. JSON is unaffected too: serde_json renders both forms as an array
of numbers, and the visitor accepts that shape back via `visit_seq`.
`DaemonRequest::SendMessage` encode, presized:
payload before after
64 B 111 ns 56 ns
4 KB 3.71 us 141 ns 26x
64 KB 59.2 us 1.24 us 48x
1 MB 932 us 21.1 us 44x
Decode is the same shape: 4 KB 3.90 us -> 151 ns, 1 MB 1.15 ms ->
22.2 us. 4 KB matters most -- it is the largest message that stays on
the daemon<->node TCP path, since anything above ZERO_COPY_THRESHOLD
goes via shared memory -- so that hop drops from ~7.6 us of serde
overhead to ~0.29 us.
This is not a regression the postcard migration introduced; bincode was
equally slow on the same path. It became visible while benchmarking it.
One consequence worth recording: now that the payload is a bulk copy,
`encode_presized` only clearly wins on small messages (2.3x at 64 B,
1.4x at 4 KB). At 64 KB and above, growing from empty is marginally
faster, because `Vec::extend` reserves the payload exactly once and
pre-sizing only adds the envelope overshoot. The small sizes are the
ones on the TCP path, so the helper stays; the bench comment now says
so rather than claiming a win at every size.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
phil-opp
force-pushed
the
followup/datamessage-serialize-bytes
branch
from
August 13, 2026 14:12
c3a9064 to
b86c2f3
Compare
Collaborator
Author
|
🤖 Automated review by Claude — fully automated review; no human has verified these findings. No issues found. What I checked:
Generated by Claude Code |
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.
Stacked on #3153 (
migrate-bincode-to-postcard) — review that first. This PR is based on that branch, so its diff shows only the change described below; GitHub will retarget it tomainonce #3153 merges.DataMessage::VecandInterDaemonEvent::Output.datareach the encoder through serde's default slice impl, which emitsserialize_seqplus oneserialize_u8per element; postcard turns that into atry_pushper byte. This routes them throughserialize_bytes/deserialize_bytesso the whole slice moves at once.No wire change. postcard writes a varint length followed by the raw bytes either way, so this needs no version bump.
encoding_is_unchanged_from_the_seq_formpins it against the default sequence encoding at the varint length-prefix boundary (127/128/129) and either side of it, and the golden vectors inlibraries/message/tests/uhlc_wire_format.rsare untouched and still pass. JSON is unaffected too — serde_json renders both forms as an array of numbers, and the visitor takes that shape back viavisit_seq, whichsurvives_a_json_round_tripasserts byte-for-byte against the sequence form.DaemonRequest::SendMessage, pre-sized encode:Decode is the same shape: 4 KB 3.90 µs → 151 ns, 1 MB 1.15 ms → 22.2 µs.
4 KB is the case that matters most. It's the largest message that stays on the daemon↔node TCP path — anything above
ZERO_COPY_THRESHOLDgoes via shared memory — so that hop drops from ~7.6 µs of serde overhead to ~0.29 µs.This isn't a regression the postcard migration introduced; bincode was equally slow on the same path (~979 µs for 1 MB). It only became visible while benchmarking the migration, and I kept it out of that PR so a bisect couldn't confuse "postcard" with "custom serde impl".
One thing worth recording
Now that the payload is a bulk copy,
encode_presizedonly clearly wins on small messages — 2.3x at 64 B, 1.4x at 4 KB. At 64 KB and above, growing from empty is in fact marginally faster, becauseVec::extendreserves the payload exactly once and pre-sizing then only adds the envelope overshoot. The small sizes are exactly the ones on the TCP path, so the helper stays as-is; I've corrected the bench comment, which previously claimed a win at every size.Deserialization still produces a 128-byte-aligned
AVec, which the Arrow zero-copy decode path depends on — asserted inround_trips_and_preserves_alignmentas well as the pre-existingdaemon_path_ipc_roundtrip_preserves_payload_and_alignment.Verification
cargo fmt --all -- --check,cargo clippy --all -- -D warnings— cleancargo test --all— 125 test binaries, zero failurescargo bench -p dora-message --bench message_serde, before/after on the same machine