Skip to content

perf(mux): fix export regressions found by the main against release comparison - #6784

Merged
kixelated merged 15 commits into
mainfrom
quest/m1/release-perf/README
Oct 27, 2026
Merged

kixelated merged 15 commits into
mainfrom
quest/m1/release-perf/README

Conversation

@kixelated

@kixelated kixelated commented Oct 25, 2026 •

Copy link
Copy Markdown
Collaborator

Works quest/m1/release-perf/README.md: the final main against release comparison before the cut.

Run

release (3db33f6715) against main (018f1c46e5): 1,019 paired Criterion cases with just bench origin/release, three relay load pairings, and nine Chromium frame rounds with an A/A pair. Every slower case was re-run in alternating rounds and counted in callgrind instructions (host load 20 to 60).

BENCH_ARTIFACTS=<scratch> just bench origin/release
bun bench/browser/micro.ts --bench frames --out <scratch>/frames.json base=<release export> current=. aa=.
valgrind --tool=callgrind --toggle-collect='*<boundary>*' <bench> --test --exact <id>   # per-case instructions
/usr/bin/time -f '%R faults' <bench> --bench --exact <id> --profile-time 3          # frame_recv faults
The 40 slowest paired cases (wall-time medians, before triage)
Case release main Ratio
track_sparse_read/timed/4096 2.55 us 2.75 ms 1079.59x
track_gap_flush/4096 4.86 us 1.63 ms 336.48x
track_gap_flush/512 1.77 us 150 us 85.07x
track_sparse_read/timed/256 2.48 us 135 us 54.59x
track_gap_flush/64 1.43 us 13.7 us 9.59x
track_stamp_groups/sparse/512 56.7 ns 320 ns 5.64x
track_stamp_groups/sparse/4096 83.5 ns 456 ns 5.47x
track_stamp_groups/sparse/64 55.8 ns 242 ns 4.35x
track_sparse_read/timed/16 2.13 us 9.1 us 4.27x
frame_recv/upfront/size=262144/streams=64 1.34 ms 5 ms 3.73x
annexb-import-bytes/1tracks-1frames/256 14.7 us 52.7 us 3.59x
frame_recv/budget/size=262144/streams=64 1.6 ms 5.69 ms 3.56x
annexb-import-frames/1tracks-1frames/256 14.6 us 51.9 us 3.55x
annexb-import-frames/1tracks-1frames/4096 14.9 us 52.5 us 3.53x
annexb-import-bytes/1tracks-1frames/4096 14.9 us 52.6 us 3.52x
origin/retire/100f 833 ns 2.89 us 3.46x
session_delivery_group_cost/moq-transport-22/relays=1/publishers=16/broadcasts=1/viewers=64/watch=1/frame=64/fetch 1.5 ms 5.06 ms 3.39x
origin/retire/1000f 853 ns 2.89 us 3.39x
frame_recv/budget/size=16384/streams=64 86 us 286 us 3.33x
cmaf-import-frames/4tracks-1frames/256 225 us 708 us 3.15x
session_delivery_paced/moq-transport-22/relays=1/publishers=16/broadcasts=1/viewers=256/watch=1/frame=64/paced 4.38 ms 13.5 ms 3.08x
moqsink/eos/renditions=1/64 53.5 us 160 us 2.98x
cmaf-import-bytes/4tracks-1frames/256 227 us 673 us 2.96x
contended_notify/threads_1/4096 299 us 884 us 2.96x
track_datagram_subscriber_join/64d/1 303 ns 896 ns 2.96x
session_delivery_group_cost/moq-transport-22/relays=1/publishers=16/broadcasts=1/viewers=16/watch=1/frame=64/fetch 634 us 1.83 ms 2.88x
session_delivery_group_cost/moq-transport-22/relays=1/publishers=1/broadcasts=1/viewers=16/watch=1/frame=64/fetch 309 us 886 us 2.87x
group_write_frames/single/2048 88.3 us 252 us 2.85x
origin/resume_overlap/sequence/1r_64g_32c 94.5 us 267 us 2.83x
session_delivery_group_cost/moq-transport-22/relays=1/publishers=1/broadcasts=1/viewers=64/watch=1/frame=64/fetch 1.19 ms 3.35 ms 2.81x
origin/retire/10000f 2.2 us 6.11 us 2.78x
session_delivery_fetch_frames/moq-transport-22/relays=1/publishers=4/broadcasts=1/viewers=16/watch=1/frame=64/fetch 383 us 1.02 ms 2.66x
session_delivery_fetch_frames/moq-transport-22/relays=1/publishers=1/broadcasts=1/viewers=16/watch=1/frame=64/fetch 309 us 811 us 2.63x
origin/reprice_duplicate/240d_240s_cost2 436 us 1.13 ms 2.59x
session_idle/moq-transport-16/sessions=1024/consumers=1 23.1 ms 58 ms 2.51x
session_delivery_scale/moq-lite-07-wip/relays=1/publishers=64/broadcasts=1/viewers=64/watch=1/frame=64 2.34 ms 5.87 ms 2.51x
origin/resume_overlap/sequence/1r_64g_1c 70.3 us 174 us 2.48x
session_idle/moq-transport-22/sessions=1024/consumers=1 17.1 ms 41.8 ms 2.45x
frame_recv/upfront/size=2097152/streams=64 21.3 ms 51 ms 2.40x
session_dash_relays/moq-transport-22/relays=2/projects=32/tracks=24/total=768 37.2 ms 85.5 ms 2.29x

Instruction counts for the unexamined screen rows: origin/reprice_duplicate/240d_240s_cost2 225M release against 125M main (noise); session_dash_relays/moq-transport-22 at 768 tracks 720M against 1.09G over 2 relays, 731M against 1.09G over 3, and 17.6G against 11.1G over 33 (flat per-track cost, falling with relays).

Fixes

  • MKV and FLV export rebuilt the jitter clock's expected track set on every frame; it is now set once per catalog update. container-export/mkv/16x64 27.1M to 17.6M instructions (2.27x to 1.47x release, now flat across the sweep), flv/16x64 14.5M to 12.9M.
  • TS export hashes PIDs with FxHash in the export and its T-STD schedule: container-export/ts/16x64 30.8M to 25.5M (2.70x to 2.24x).
  • Benches that no longer ran against release: bench/run.sh skips non-Criterion targets; the moq-mux catalog bench caps renditions at hang::catalog::MAX_RENDITIONS; the relay auth bench checks the grant it already holds instead of awaiting a change that never comes since moq-dev/moq#6109 (it hung before its first case); moq-stats producer_tick skips tiers above one at 50,000 paths.
  • Recurring audit: quest/m2/audit-perf.md gains a release baseline step, so every later cut repeats this comparison, plus the TS schedule and e2ee key schedule as things to check first.

Accepted (flat per operation, recorded in the quest)

  • e2ee protect 1.5x on grouped frames (1.21x datagrams): per-group model cost; the crypto is unchanged.
  • gst moqsink/eos +12% per event: moq-dev/moq#5512 and the gstreamer-rs 0.25 bump in moq-dev/moq#6169.
  • group_write_frames 1.67x, track_stamp_groups/sparse 1.48x (was 4.4x), session families 1.2x to 1.76x, session_dash_relays 1.50x at few relays: the per-frame, per-group, and per-track costs the quest already accepts. The import fixed costs and IETF /fetch rows reappear unchanged.
  • TS export still grows from 1.50x at 1x1 to 2.24x at 16x64: the T-STD schedule scans and sorts every queued unit per PCR slot. Left to the performance audit (see decisions).

Noise: frame_recv/upfront/256K/64 is 3 to 4x wall at 0.96x instructions; its 8x page faults are glibc trimming the freed buffers, and with malloc tunables pinned both sides fault 4.6k times and time the same (the relay runs jemalloc). session_idle IETF at 1024 sessions, resume_overlap, retire, contended_notify, and producer/merge are at parity in alternating rounds or instructions.

Relay: video -7.8% relay CPU, same delivery. Fanout +16.4%, but paired deltas were -1.5%, +16.4%, +26.1% at load 25 and the previous run measured -12%: inconclusive. Browser frames: lite 1.09 to 1.14x and IETF 0.95x, the same as at the 2026-10-23 decision.

Remaining

Dynamic range requests (merged 2026-10-24) re-judges every deferred gap on each hand-out through Cursor::probe and Misses::judge. track_sparse_read/timed/4096 is 1079x release (2.75 ms against 2.5 us), with instructions per pair growing from 152k at 16 gaps to 51.4M at 4096, and track_gap_flush setup is quadratic. No open PR fixes it. The quest stays open with a child, sparse-misses.md, plus the fanout re-run.

Decisions

Decided 2026-10-25 by the maintainer, taking the recommendations:

  • ✅ Sparse read slope: the separate child quest quest/m1/release-perf/sparse-misses.md, which blocks the cut.
  • ✅ TS schedule growth: left to the performance audit; it does not block the cut.
  • ✅ Fanout relay CPU: re-run on a quiet host once the child lands, recorded in the epic README.

Impact

Public API: none. Wire: none. moq-mux adds a rustc-hash dependency (already a workspace dependency).

Checks

quest check passes. moq-mux nextest passed (1,742 tests). Scoped Rust lint and test of the changed crates and their dependents passes (4,441 tests). Full just check fails only in check flake, which cannot build the GitHub runner VM image in this sandbox (unshare: write failed /proc/self/uid_map); unrelated to this diff.

(Written by Claude Opus 5.5)

🤖 Generated with Claude Code

kixelated and others added 8 commits October 25, 2026 04:21
`just bench <base>` discovers each target's cases from a short Criterion
run, and failed outright on a harness = false target that prints its own
table, such as moq-cli's play_buffer. Such a target is now listed as
skipped, so a base whose bench later became Criterion reports its cases as new.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Its 256-track case panicked with TooManyRenditions, since one catalog holds
at most hang::catalog::MAX_RENDITIONS (64). The sweep now tops out there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Since #6109, Handle::add resolves once the peer grants the token, so a
watch created afterwards already holds that grant and changed() waited
forever. relay_auth_refresh hung before its first case.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Since #6428 every tier shares the one depth-0 group's snapshot, so 50,000
paths over four tiers (about 86 MB) outgrow the 32 MB frame cap and the
track ends with FRAME_TOO_LARGE, as doc/concept/stats.md describes. The
case measured an ended track and failed its own check; one tier still
sweeps to 50,000.

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>
kixelated and others added 6 commits October 25, 2026 09:14
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… update

Both exports rebuilt the jitter buffer's expected-track set on every poll,
cloning and sorting every track name per frame in MKV. Tracks change only
when the catalog does, so the set is now set there. container-export
instructions: mkv/16x64 27.1M to 17.6M (-35%), flv/16x64 14.5M to 12.9M.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The T-STD schedule looks up per-PID buffers for every queued unit on each
PCR slot, and SipHash was 18% of the export. PIDs are at most 13 bits from a
bounded catalog, so a fast hasher is safe. container-export/ts/16x64
instructions 30.8M to 25.5M (-17%), ts/1x64 -12%.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…read slope

The 2026-10-25 run left one accidental regression, the sparse read slope from
dynamic range requests, and an inconclusive relay fanout pairing. The epic
keeps them as remaining work with a child quest, instead of finishing.

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>
@kixelated

Copy link
Copy Markdown
Collaborator Author

Decisions settled 2026-10-25, applying the maintainer's standing go-ahead to take the recommendations:

  • ✅ The sparse read slope from moq-dev/moq#6171 becomes the separate child quest quest/m1/release-perf/sparse-misses.md, which blocks the cut.
  • ✅ TS schedule growth is left to the performance audit (quest/m2/audit-perf.md).
  • ✅ The relay fanout CPU pairing is re-run on a quiet host once the child lands; recorded in the epic README.

Recorded in 01e3bab249. Marking ready.

(Written by Claude Opus 5.5)

@kixelated
kixelated marked this pull request as ready for review October 26, 2026 00:12
@coderabbitai

coderabbitai Bot commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You'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 6 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 4 included reviews currently available.

Learn how review limits work.

Review configuration:

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 5d9b5e9f-763a-4425-b974-8b94cb23c1df
Commits

Reviewing files that changed from the base of the PR and between fac5194 and b5a3782.

Files ignored due to path filters: 1
  • Cargo.lock is excluded by !**/*.lock
Files selected for processing: 14
  • bench/README.md
  • bench/run.sh
  • quest/m1/release-perf/README.md
  • quest/m1/release-perf/sparse-misses.md
  • quest/m2/audit-perf.md
  • quest/m2/performance-survey/README.md
  • rs/moq-mux/Cargo.toml
  • rs/moq-mux/benches/catalog.rs
  • rs/moq-mux/src/container/flv/export.rs
  • rs/moq-mux/src/container/mkv/export.rs
  • rs/moq-mux/src/container/ts/export.rs
  • rs/moq-mux/src/container/ts/schedule.rs
  • rs/moq-relay/benches/auth.rs
  • rs/moq-stats/benches/producer_tick.rs
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-26T21:15:22.869800Z b5a3782 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@kixelated kixelated changed the title Main is no slower than release perf(mux): fix export regressions found by the main against release comparison Oct 26, 2026

@kixelated kixelated left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review by review (OpenAI)

Reviewed commit: 01e3bab

No actionable bugs found in the changed code. Moving FLV/MKV jitter expectations to catalog updates preserves the bound-track set while removing per-frame rebuilding; the TS hash changes stay confined to bounded integer PID keys. The benchmark repairs and explicit release follow-ups are a reasonable, limited direction without additional abstraction.

This is the first published review; no earlier findings to reconcile. The sparse-read regression and inconclusive fanout result are explicitly tracked outstanding work, not resolved by these optimizations or newly discovered findings.

Verification: static review of the diff and relevant exporter, jitter-buffer, scheduling and benchmark context only. I did not run tests or benchmarks or independently reproduce the reported performance results.

@kixelated
kixelated added this pull request to the merge queue Oct 26, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to no response for status checks Oct 26, 2026
@kixelated

Copy link
Copy Markdown
Collaborator Author

Requeuing after the merge queue dropped this on checks_timed_out (queue load; the PR's own checks pass on 01e3bab249). No changes: the head merges cleanly with main (8810d73651), the changed crates (moq-mux, moq-stats, moq-relay) compile on the merged tree, and quest check passes on both the head and the merge. Codex reviewed this head with no findings; the decisions in the body stand.

(Written by Claude Opus 5.5)

@kixelated
kixelated added this pull request to the merge queue Oct 26, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Oct 26, 2026
…/README

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated

Copy link
Copy Markdown
Collaborator Author

Merged origin/main into the branch (b5a3782) after the PR left the queue on merge_conflict.

  • Conflict: quest/m2/performance-survey/README.md, where moq-dev/moq#6798 rewrote the release-perf ownership bullet to say only the final comparison remained. Kept this PR's text, since this PR performs that comparison and leaves the sparse read slope as the child quest. Checked that Cursor::probe, Misses::judge, and quest/m1/drop-set.md still exist on main, and that nothing on main already fixes the sparse slope.
  • Checks: quest check passes. The selected just check lanes pass (rs lint, 10,470 Rust tests, bench, drill, markdown, gh, ts_paths, shell, toml, nix, justfile, large_files). The only exception is check flake, which cannot build the runner VM image in this sandbox (unshare refused). No wire or moq-ffi changes, so interop was not needed.
  • Review: Codex gave the new head a thumbs-up.

(Written by Claude Opus 5.5)

@kixelated
kixelated added this pull request to the merge queue Oct 26, 2026
Merged via the queue into main with commit 3f3fc19 Oct 27, 2026
11 checks passed
@kixelated
kixelated deleted the quest/m1/release-perf/README branch October 27, 2026 00:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant