Repository navigation
perf(net): a frame's single delivery writes without the publication lock - #7508
Conversation
Quest-Claim: 75fbe89eb49b15a9
… lock Until anything attaches to a frame's content, its creating delivery is the only writer, so it publishes without the publication lock or an overlap check. It takes its admission with the content and ends it without the lock too. Attaching waits out a write in flight (a SeqCst store-load pair on each side), after which every write locks as before. A group without shared content skips the canonicalize call per frame. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The maintainer picked (a) on 2026-11-01: accept the remaining residual of #6922's per-frame cost and delete the quest, rather than keep it for a smaller FrameContent or the batch path's layout cost. 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 13 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. CodeRabbit Verify
|
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed commit: 3c678f25dd5b184655e09a3abd906ead23e04f42.
No actionable bugs found in the changed publication, attachment, reservation-release, and group commit paths. The creator/attacher store-load handshake preserves exclusive publication before switching permanently to the existing mutex path, and producer lifetime still governs reservation release rather than cached readers.
Direction: optimizing the common single-delivery case while retaining the existing shared-content path is reasonable. Keeping the mutex for forks is simpler than introducing another lock-free shared-writer mechanism; the documented decision to accept the remaining measured cost avoids expanding this into a larger ownership redesign. No public API or wire change identified.
Verification limits: static GitHub source and test inspection only; I did not run tests, concurrency instrumentation, or reproduce the benchmark measurements.
(Written by OpenAI)
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
This finishes the single-delivery-write quest by letting a frame's creating delivery publish without the publication lock until another party attaches, then deleting the quest.
- Lock-free creator write:
write_chunknow guards a newwrite_alone(no mutex, no overlap check) with awriting/attachedSeqCst store-load pair while the creating delivery is the only writer. FrameContent::attach: the single choke point under the publication lock that waits out any in-flight creator write and folds the creator's admission intoPublication.producers, called fromShared::fork,identify,bill_content, andadmit.- Admission rework:
AdmissionbecomesCreator(Option<Reservation>) | Sharedwith an explicitFrameBuf::release, andFrameBuf::admittedtakes the creator's reservation with the content soProducer::dropreturns it without a refcount round trip. - Group fast path:
canonicalizeis skipped when the group has no shared content, in both the batch write and commit paths. - Quest removal: deletes
quest/m2/perf/single-delivery-write.mdand its README entry.
I traced the StoreLoad pairing (write_chunk/release versus attach) and the reservation handoff across admitted/attach/admit/release, including Shared::fork's fast path and the release-during-attach interleaving, and found no double-release, leak, or missed/spurious producer increment. Two independent reads reached the same conclusion. Public API and wire are unchanged.
deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed commit: 33d7e13
No new actionable issue found in the sampled attachment and storage paths. Including main's new frame-owner storage, the creator/attacher handshake remains intact and storage-specific locking remains in place.
Static GitHub review only; I did not run tests, Miri or benchmarks. The earlier benchmark predates the merged storage change.
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
This run re-reviewed the delta since the prior Pullfrog review, which is the merge of origin/main into the branch. The PR's own lock-free creator-write logic is unchanged.
- Merged
origin/main(2ca588bd7): integrated the per-storage reader-owner refactor (FrameStorage::Fixed { segment, shared },Open,share, andFilledoverMaybeUninitsegments) and the groupPacemachinery onto the branch. - No conflict-induced change to
FrameContent::attach,Admission::Creator | Shared,FrameBuf::admitted/release,write_alone, or the groupcanonicalizefast path.
The new integration surface the prior review could not see is main's reader owners. I verified they do not race the creator's unlocked write to a Fixed segment: share takes a Filled owner's length only from an Acquire load of written, and the creator publishes that with a Release store only after copying, so a reader never builds an owner covering uninitialized bytes. The missing shared lock on the writer is sound because writes are append-only and written is monotonic. An independent read reached the same conclusion. Public API and wire are unchanged.
deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏
#7812 installed rust-src for Nightly Miri and rewrote miri.md to wait on the next scheduled run; take main's text. Fold this PR's miri-frame.md into it: its rust-src half has landed, and confirming #7508's frame tests ran is read from that same scheduled log. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Finishes quest/m2/perf/single-delivery-write.md by accepting the measured residual, and deletes it with its link from quest/m2/perf/README.md.
Problem
moq-dev/moq#6922 made every streamed frame publish through a per-frame
Publicationmutex:admit, everywrite_chunk(plus an overlap check against bytes another delivery already published), and the admission's release. Nearly every frame has exactly one delivery, so that work protects nothing. moq-dev/moq#7141 recovered the allocations but left this.Approach
Until anything else attaches to a frame's content, its creating delivery is the only writer:
FrameBuf::admitted), before anything else can see it, and keeps its reservation itself. Releasing it alone takes no lock either.FrameContent::attachunder the publication lock:identify(the only way a fork can appear in production),Shared::fork,bill_content, andadmit. The race the quest names, a second copy attaching while the creator is mid-write, is ruled out with a store-load pair on each side. The creator storeswritingthen loadsattached, and the attacher storesattachedthen loadswriting, allSeqCst. At least one side sees the other: either the creator takes the locked path, or the attacher yields until that write ends. After that, every write locks exactly as before.Admissionno longer holds anArcof the content. Aframe::Producerreleases it through its own buffer, which saves a refcount round trip per frame.canonicalizecall inwrite_frameand on commit, instead of calling it to return at once.Measurements
These are user-space instruction counts (
cpu_core/instructions/uread per round throughperf_event_open), pinned to one P-core. The host is loaded, but these counts are deterministic: repeat runs agree to within 0.01%. Each revision was built from its own export with its own target directory.mainis14ed0f05dd, andbaseisc67c124b20, the parent of #6922.256-frame lite-07-wip FETCH round, 1 publisher, 1 viewer:
Against main, this PR lowers every
session_delivery_fetch_framescase (0.16% to 1.55%) and everyfetch_rangecase but two moq-transport-22 ones, which rise 0.24% and 0.27% (table below). About 87k of #6922's 163k residual (measured at #7141) remains. A symbol profile spreads it over building and dropping the largerFrameContent, the commit path, and reader-side copies, with no locking left. So base parity is not met; the maintainer accepted the residual, see Decisions.group_write_frames/batch32is unchanged (512 frames: 243.3k on main, 243.5k here, 216.1k on base). The batch path takes no frame lock, so it shares none of this cost. Two experiments for its residual, which I measured and dropped:Entryowner slot (anAtomicPtrcell in place ofMutex<Option<Shared>>) saves 2.6 instructions per frame and addsunsafe.GroupState::commitinline intowrite_framescosts 3% more.FETCH sweep, instructions per round (main, this PR)
fetch_frames/moq-lite-06/publishers=1/viewers=1 frames=4fetch_frames/moq-lite-06/publishers=1/viewers=1 frames=256fetch_frames/moq-lite-06/publishers=1/viewers=16 frames=4fetch_frames/moq-lite-06/publishers=1/viewers=16 frames=256fetch_frames/moq-lite-06/publishers=4/viewers=1 frames=4fetch_frames/moq-lite-06/publishers=4/viewers=1 frames=256fetch_frames/moq-lite-06/publishers=4/viewers=16 frames=4fetch_frames/moq-lite-06/publishers=4/viewers=16 frames=256fetch_frames/moq-lite-07-wip/publishers=1/viewers=1 frames=4fetch_frames/moq-lite-07-wip/publishers=1/viewers=1 frames=256fetch_frames/moq-lite-07-wip/publishers=1/viewers=16/watc... frames=4fetch_frames/moq-lite-07-wip/publishers=1/viewers=16/watc... frames=256fetch_frames/moq-lite-07-wip/publishers=4/viewers=1 frames=4fetch_frames/moq-lite-07-wip/publishers=4/viewers=1 frames=256fetch_frames/moq-lite-07-wip/publishers=4/viewers=16/watc... frames=4fetch_frames/moq-lite-07-wip/publishers=4/viewers=16/watc... frames=256fetch_frames/moq-transport-22/publishers=1/viewers=1/watc... frames=4fetch_frames/moq-transport-22/publishers=1/viewers=1/watc... frames=256fetch_frames/moq-transport-22/publishers=1/viewers=16/wat... frames=4fetch_frames/moq-transport-22/publishers=1/viewers=16/wat... frames=256fetch_frames/moq-transport-22/publishers=4/viewers=1/watc... frames=4fetch_frames/moq-transport-22/publishers=4/viewers=1/watc... frames=256fetch_frames/moq-transport-22/publishers=4/viewers=16/wat... frames=4fetch_frames/moq-transport-22/publishers=4/viewers=16/wat... frames=256fetch_range/moq-lite-06/publishers=1/viewers=1/burst=2/fetchfetch_range/moq-lite-06/publishers=1/viewers=1/burst=16/fetchfetch_range/moq-lite-06/publishers=1/viewers=16/burst=2/fetchfetch_range/moq-lite-06/publishers=1/viewers=16/burst=16/fetchfetch_range/moq-lite-06/publishers=16/viewers=1/burst=2/fetchfetch_range/moq-lite-06/publishers=16/viewers=1/burst=16/fetchfetch_range/moq-lite-06/publishers=16/viewers=16/burst=2/fetchfetch_range/moq-lite-06/publishers=16/viewers=16/burst=16/fetchfetch_range/moq-lite-07-wip/publishers=1/viewers=1/burst=2/fetchfetch_range/moq-lite-07-wip/publishers=1/viewers=1/burst=16/fetchfetch_range/moq-lite-07-wip/publishers=1/viewers=16/burst=2/fetchfetch_range/moq-lite-07-wip/publishers=1/viewers=16/burst=16/fetchfetch_range/moq-lite-07-wip/publishers=16/viewers=1/burst=2/fetchfetch_range/moq-lite-07-wip/publishers=16/viewers=1/burst=16/fetchfetch_range/moq-lite-07-wip/publishers=16/viewers=16/burst=2/fetchfetch_range/moq-lite-07-wip/publishers=16/viewers=16/burst=16/fetchfetch_range/moq-transport-22/publishers=1/viewers=1/burst=2/fetchfetch_range/moq-transport-22/publishers=1/viewers=1/burst=16/fetchfetch_range/moq-transport-22/publishers=1/viewers=16/burst=2/fetchfetch_range/moq-transport-22/publishers=1/viewers=16/burst=16/fetchfetch_range/moq-transport-22/publishers=16/viewers=1/burst=2/fetchfetch_range/moq-transport-22/publishers=16/viewers=1/burst=16/fetchfetch_range/moq-transport-22/publishers=16/viewers=16/burst=2/fetchfetch_range/moq-transport-22/publishers=16/viewers=16/burst=16/fetchDecisions
Settled 2026-11-01 by the maintainer.
What to do with the remaining residual. Lite FETCH keeps about 87k instructions per round (1.9%) of fix(net): preserve transport frame bounds #6922's cost, and batched writes about 53 instructions per frame. Neither has a single removable step.
FrameContent's size andwrite_frames' code layout. Inlining experiments move it by as much as they save.FrameContent, with the canonical-only fields behind a lazily allocated part. That costs one more allocation per frame on groups with shared content.So this PR deletes the quest (
quest rm) instead of rewriting it to the (b)/(c) work as the earlier head did, and drops its entry from the perf epic'sRequiredlist. The quest's goal allowed this outcome: "or the measured residual is accepted and this quest deleted".Choices made without asking
attachruns under the group's write lock, and waits for at most one in-flight write: a bounded copy that takes no lock the attacher holds. It loops onstd::thread::yield_now(). Attaching happens once per content, and only waits if it lands mid-write.Entryowner-slot change (2.6 instructions per frame, not worth newunsafe). Padding base'sEntryto main's size costs only 3.4 per frame, so the slot is not the batch residual.Tests
a_creator_alone_publishes_and_releases_without_attaching: the creator publishes and returns its reservation without attaching. Once attached, a fork shares the reservation, the creator keeps it until it ends, and a mismatching fork is still refused.a_delivery_attaching_mid_write_publishes_one_payload: a fork attaches on another thread while the creator streams, for fixed and growing storage. Both see one payload, stored once. It passed 300 repeated runs.admission_ends_with_active_producers_not_cached_viewsnow starts from a creator admission, so it covers the hand-off of the creator's reservation.Impact
Validation
just check: clippy, fmt, doc, shear, sort, and 8845 of 9240 tests passed. The one failure,moq-uring::integration mtu::discovery_finds_a_larger_mtu, is the host's sharedRLIMIT_MEMLOCKrefusing io_uring memory (Cannot allocate memory), not this change.cargo clippy -p moq-net --all-targets --all-features -D warningsandcargo nextest run -p moq-net --all-features(3222 passed).bf4e726f66):just rs check -p moq-netandcargo nextest run -p moq-net --all-features(3223 passed).2ca588bd7d, head33d7e13329): the merge was clean, since moq-dev/moq#7509's per-storage reader owners take their own locks and leave the publication alone.just rs check -p moq-net --all-featuresandcargo nextest run -p moq-net --all-features(3311 passed, 13 skipped) passed. Miri (just rs miri) did not run: the host has no nightly toolchain, and CI runs it only in Nightly.quest check --base origin/main: PASS (554 documents).Follow-ups
groupbench binary whose counts matched neither source state, and fresh export paths were consistent. A paired A/B harness should build each revision at its own path, not just with its own target dir, which extends perf(net): stream a frame's single delivery without extra allocations #7141's note.Review
Codex
gpt-6.1-solat medium effort (quest review --base origin/main), round 1 on head33d7e13329: no findings.The online reviews on
3c678f25dd(OpenAI and pullfrog) reported no issues, and no review threads are open.(Written by Claude Opus 5.5)
🤖 Generated with Claude Code