Repository navigation
perf(net): resolve the paired network microbenchmark regressions - #7139
Conversation
Quest-Claim: 5ba8752a8eb79b0c
write_frames cloned every payload into its prepared entries and then dropped the originals, two atomic refcount writes per frame that only a canonical conflict needed, and only shared canonical content can refuse a frame. Path::parts flattened an optional split, which the route table walks on every lookup. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 35 minutes. View limit details
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed commit: d817d7d
No actionable bugs found. Batch validation still precedes draining, shared canonical content still prepares the full batch before storing it, and conflict-driven consumption remains within the documented failed-group exception. split_terminator preserves empty-root and segment behavior under Path's validation invariant.
Direction: targeted removal of payload refcount churn and iterator overhead is preferable to weakening canonical consistency or budget correctness for benchmark numbers. Keeping layout/inlining evidence separate from production-work changes is sensible.
Verification: static review of both changed code paths, surrounding invariants and existing tests, including an independent review of the batch-write lifecycle. No tests or benchmarks run; the reported performance measurements were not independently reproduced.
|
Merge summary: head
(Written by Claude Opus 5.5) |
Main's #7139 already moves batched payloads into group storage and skips canonicalization for groups without shared content, so keep its write_frames. This branch keeps its tests and 32/33-frame bench sweep. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Problem
The 2026-10-28 Nightly Rust bench targets job compared
270395a337against22808bbb2fand confirmed 14 regressions inmoq-net. This PR finishesquest/m0/nightly/paired-net.md: work out what caused each one, and fix the ones that are real extra work.Method
This host is shared with about 25 agents, so wall-clock comparisons here are noisy. Every claim below rests on one of two measures that load doesn't skew much:
write_frames,poll_read_frame,poll_requested_broadcast,requests_innerplus its drop, andBudgetWidening::widenwith collection toggled to it).To find which commit caused each step, I built all 14
moq-netcommits in the range and ran every flagged case across that chain.Attribution
codec_messages/decode/lite(+31%),encode/lite(+68%)setup_parametersgroup tobenches/codec.rs. It links the wholelite_wirefuzz target into the binary, so LTO stops inlining the message codecs intoMessages::encode_liteanddecode_lite.197bdae8eewith its parent'scodec.rs: decode +3.6% and encode −0.4% against the parent. With the new group: +42% and +45%.group_relay_frames/plain/512/1,8192/64(+23%)poll_read_frameon base, 239.1 on head. On x86 the chain jumps in +25% steps at commits that don't touch the read path, e.g.38bc872f1d+25%, thenfbeac8a9fd+1%.group_write_frames/batch32/{4,512,2048,8192}(+17% to +45%)write_framesclone each payload into a prepared entry and then drop the original. That is two atomic refcount RMWs per frame, plus a two-phase prepare/commit that only canonical content needs.write_frames, Ir per frame: base 380, main 536 (plus 2 refcount RMWs), this PR 459 (no refcount ops). Wall-clock A/B against base, main → this PR: 2048 +55.5% → +11.7%, 512 +52.1% → +11.4%, 8192 +48.4% → +6.3%, 4 +11.0% → +7.7%.origin/route_churn/10000b_1s(+14%)Path::partsinto aFlatten<Option<Split>>, which the route table walks on every lookup.requests_innerplus drop): base 19,293, main 20,658, this PR 19,848. Wall clock: +22.1% → +6.0%.origin/serve_idle/3000r(+46%)poll_requested_broadcaston both base and head. On x86 the wall-clock delta is within noise (+3.1% on main).track_budget_widening/{1r/16384c,64r/1024c,64r/16384c,64r/64c}/1(+13% to +25%)Misses::judge, its range sets, and 3 more clock reads). moq-dev/moq#6922 addedserves_endandarrival_grace.widen: 10,218 → 12,918 at64r/64c/1, and the same +2.7k in all four cases. The extra cost doesn't grow with cache depth (64 to 16,384) or with readers (1 to 64), so there is no new slope.Changes
group::Producer::write_framesnow moves each frame out of the drained buffer instead of cloning its payload. This keeps the documented contract: the buffer survives a refusal unless the refusal fails the group, and a canonical conflict does fail it. Only a group that shares canonical content prepares the whole batch before storing any of it. Any other group stores each frame as it goes, which skips the per-framecanonicalizecall and the extra move through the prepared array.GroupState::commitis a new helper that holds the store-and-charge stepswrite_frameandwrite_framesused to repeat inline.Path::partsis nowsplit_terminator('/'). A valid path has no empty segments, so only the empty path produces an empty substring, and a terminator split skips it.quest/m1/bench-ci.md, because those cases set its calibration problem: a paired round reruns the same two binaries, so a layout shift reads as a stable regression that extra rounds can't filter out.Public API: none. Wire: none.
Validation
just check origin/mainpassed: 9020 tests, plus clippy, fmt, doc, shear, and sort.quest check --base origin/mainpassed.group_write_frames), table size × cursors (route_churn), routes (serve_idle), readers × cached × runs (track_budget_widening), and frames × subscribers (group_relay_frames).Choices made without asking
track_budget_wideningcost. It is new correctness work with no slope, and a budget widen is a rare control-path operation.write_framesstays about 20% above the old base in instructions per frame (+6% to +12% wall clock). That comes fromEntry's canonical owner slot and helpers LLVM no longer inlines into the bigger function. Both are part of moq-dev/moq#6922's design.Follow-ups
quest/m1/bench-ci.mdshould weigh instruction counts (CodSpeed, or callgrind in the paired gate) for the cases above, because their wall-clock rounds agree on a layout shift.(Written by Claude Opus 5.5)
🤖 Generated with Claude Code