Skip to content

Rational comparison: ↑2, ↓2, 3/2 and 2/3 against libsamplerate, soxr, r8brain and SpeexDSP at matched spec and frontier - #77

Merged
tap merged 4 commits into
mainfrom
claude/sample-rate-solutions-comparison-mqc190
Oct 10, 2026
Merged

tap merged 4 commits into
mainfrom
claude/sample-rate-solutions-comparison-mqc190

Conversation

@tap

@tap tap commented Oct 10, 2026

Copy link
Copy Markdown
Owner

What this changes

PR 3 of the comparison plan, the last (after #73, #75, #76). The rational engine measured against the four resampler libraries at four ratios of its vocabulary — ↑2 (48 → 96 kHz), ↓2 (96 → 48), 3/2 (32 → 48) and 2/3 (48 → 32) — in quality, latency, host throughput and embedded instruction counts, through the family's tools/compare; and compare.yml restructured as one job per engine.

  • rational/bench/compare (host, Google Benchmark, 68 subjects) and rational/bench/icount/cmp_main.cpp (M33/M55/Hexagon, 2 s and 4 s, four ratios, 80 binaries per target).
  • compare.yml is a matrix over async, bridge, rational, each job measuring its engine's binaries (scripts/compare_binaries.sh <engine>), harvesting its own record and uploading it as compare_counts-<engine>; the three ran in parallel in 16 / 28 / 44 minutes. update_compare_docs.py derives the third doc; ci.yml's compare-docs gates all three and compare-smoke builds the new targets.
  • rational/notebooks/rational_comparison.ipynb, executed: the matched-setting sweep across the four ratios at once (spurious products found at the exact candidate frequencies |a·f_in + b·f_out ± f|; decimations probed in their stopbands, since a Nyquist design's transition band aliases above the passband edge by contract), THD+N and DR at the 24-bit interface, latency in each engine's own unit as milliseconds.
  • rational/docs/COMPARISON.md, the family index's rational row, the README link.

Why

The async comparison asked what clock recovery costs and the bridge comparison asked the synchronous question at one pair; rational is the family's within-family engine, so its question is the same one at the ratios a Nyquist stage serves. The matching rule is the bridge comparison's, applied across four ratios at once: a competitor's matched setting is its cheapest whose measured stopband is at least the tier's and whose passband droops no more than 0.1 dB at the tier's edge (3/8 and 5/12 of the lower rate) at every ratio; a setting that passes three and fails one is not matched. The picks are asserted against the benches' constants.

Verification

  • Matched settings (notebook, asserted): economy — libsamplerate MEDIUM, soxr 16-bit at a 0.75 passband, r8brain 70 dB at a 16 % band, SpeexDSP quality 2 (float) and 3 (fixed-point); transparent — BEST, soxr 20-bit at 0.80, r8brain 120 dB at 10 %, SpeexDSP quality 9 (fixed-point: None). One notch below the bridge picks, as the fractional edges predict.
  • Quality (notebook, 24-bit interface, asserted for rational's rows): at ↑2, ↓2 and 2/3 no image of a 997 Hz probe lands in band, so every float row measures the 24-bit ceiling (−144 to −147 dB); at 3/2 the image lands at 17 kHz and the table separates the tiers (economy −88 dB, transparent −140 dB, SpeexDSP quality 2 −82 dB, r8brain and libsamplerate's economy rows −122 dB). The doc says the 997 Hz table is a slice and the sweep is the contract measurement. SpeexDSP's quality 2 is the one competitor setting that lands on economy's 70 dB (70–76 dB worst product).
  • Embedded counts: CI-measured (run 38013347369). The rational record reproduces the local derivation in every steady-state cell; its Hexagon construction cells round 0.1 M lower from the same constant 1,620-instruction start-up offset seen on Bridge comparison: 44.1 ↔ 48 against libsamplerate, soxr, r8brain and SpeexDSP at matched spec and frontier #76. The async and bridge records re-measured in their own jobs are identical to the committed ones in every count. Headline: the Q15 stage is the cheapest row at every ratio on every target (189–379 instructions per stereo frame on the M55, 291–575 on the M33), and in float the short Nyquist stage holds its own against r8brain's FFT (0.8–0.9× rational's cost only at the by-2 pair on the M55, 1.2–2.5× everywhere else), which bridge's 58-tap polyphase did not.
  • Host table: one session, median of 5, all 68 subjects. soxr 2.4–3.3× and r8brain 0.9–1.8× economy; rational 7–12× libsamplerate; SpeexDSP's fixed-point build out-runs rational's Q15 stage on x86 by 1.6–2× (its 32-bit integer math vectorizes where rational's 64-bit accumulation does not), stated plainly with the embedded tables as the counterweight.
  • Gates: the family battery with every engine's WERROR and the C ABIs on, zero warnings; clang-format and clang-tidy clean on the two new units (house naming, mandatory braces); retired-words clean; both workflows parse; update_compare_docs.py idempotent on all three records; the local native runs of all 40 workloads report ok.
  • Not run: the compare-smoke job's new rational targets on the hosted compilers (built locally with GCC; this PR's CI is its first run).

Notes for the reviewer

  • Construction is a wash and mostly fixture in rational's tables: every row's one-time figure is within a few hundred thousand instructions of the others at the same ratio because the 0.25 s sin() fixture dominates the 2 s / 4 s split, and rational's one-prototype design is a fraction of a million. The doc says so; the siblings' construction columns carry the same fixture but are dominated by their designs.
  • Negative construction cells (libsamplerate BEST, down to −16 M on the M33) are the split's resolution against that engine's per-frame drift, footnoted as on Bridge comparison: 44.1 ↔ 48 against libsamplerate, soxr, r8brain and SpeexDSP at matched spec and frontier #76, not clamped.
  • The matrix job keeps the harvest and artifact per engine, so a re-measure of one engine does not re-run the others; the compare_counts artifact name is now compare_counts-<engine>.
  • Notebooks: rational_comparison.ipynb is new and committed executed; the sibling notebooks are untouched.
  • Follow-up noted, not in this PR: rational's construct_q15_eco ratchet leg does not include the input fixture its streaming workloads synthesize, as async's and bridge's construct legs do since Comparison freshness gate, construct-only ratchet legs, async comparison refresh #73; aligning it re-records that baseline.

🤖 Generated with Claude Code

https://claude.ai/code/session_01G3HxzEGiZp7jStoMuYNuBK


Generated by Claude Code

…gine matrix

PR 3 of the comparison plan, in progress: the rational engine measured
against libsamplerate, soxr, r8brain and SpeexDSP at four ratios of its
vocabulary (up 2, down 2, 3/2, 2/3), at matched spec and at each library's
frontier, through the family's tools/compare.

- rational/bench/compare (host) and rational/bench/icount/cmp_main.cpp
  (M33/M55/Hexagon, 2 s and 4 s, four ratios); compare.yml becomes a
  matrix job per engine (scripts/compare_binaries.sh <engine>), each
  harvesting its own record; update_compare_docs.py derives the third doc.
- notebooks/rational_comparison.ipynb, executed: the matched settings by
  the same rule as bridge's, applied across the four ratios at once, with
  the spurious products found at the exact candidate frequencies and the
  decimations probed in their stopbands; THD+N and DR at the 24-bit
  interface; latency in each engine's own unit, as milliseconds.
- rational/docs/COMPARISON.md with the quality and latency tables; the
  family index's rational row; the README link.

Still to land: the host table, the harvested record and its derived block.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3HxzEGiZp7jStoMuYNuBK
… cost line

From the local M55/M33/Hexagon derivation; the committed record follows
from the compare.yml dispatch.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3HxzEGiZp7jStoMuYNuBK
One session on this host, median of 5, every subject at the four ratios.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3HxzEGiZp7jStoMuYNuBK
…gines)

The first per-engine matrix run, on this branch's head: rational's record
and its derived block (four ratios, three targets), with the async and
bridge records re-measured in their own jobs — every count identical to
the committed records, so only their provenance lines move. The rational
block reproduces the local derivation in every steady-state cell; the
Hexagon construction cells round 0.1 M lower from the same constant
1,620-instruction difference in the runner's process start-up.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3HxzEGiZp7jStoMuYNuBK
@tap
tap merged commit e71d0b1 into main Oct 10, 2026
30 checks passed
@tap
tap deleted the claude/sample-rate-solutions-comparison-mqc190 branch October 10, 2026 12:50
tap pushed a commit that referenced this pull request Oct 10, 2026
The comparison plan (#73, #75, #76, #77) and its follow-up (#78) closed;
this is the plan for what the family's plans and ledgers still carry
open, sequenced into twelve weeks in three blocks, with "production-ready"
defined as a thirteen-row checklist per engine so the verdict can be
checked rather than believed.

Drafted and then audited by two independent adversarial reviews (one
against the trees' facts and settled decisions, one against engineering
judgement). The audit changed the draft materially: the book's bridge
chapters are done (#68), so they are off the list; rational has no
cross-engine validation by design (scipy and decimate.h are its legs);
TSan runs async only; four host compilers and three QEMU targets, not the
draft's counts; the +1.9 % M55 ratio23_q15_eco drift is ab8fea3's stated
move, not a DspTap bump, and the chain rows sit +926/+1,102; the hardware
block never ran bridge or rational on silicon; nothing builds with
-fno-exceptions; TSan has never seen the converter with two threads; no
footprint, stack or worst-single-call figure exists; Setup 1's "< 1 ppm"
criterion tests the counting, not the servo, and cheap USB dongles may not
own a crystal; the timestamp servo is a four-to-five-week extension, not an
overload, and must not be frozen in the release that adds it; "C ABIs
frozen" needs a reconciled NULL model, an extension rule, SOVERSION and a
compat test. Section 7 holds the six decisions the maintainer takes before
week 1, each with a recommendation.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3HxzEGiZp7jStoMuYNuBK
tap pushed a commit that referenced this pull request Oct 10, 2026
The comparison plan (#73, #75, #76, #77) and its follow-up (#78) closed;
this is the plan for what the family's plans and ledgers still carry
open, sequenced into twelve weeks in three blocks, with "production-ready"
defined as a thirteen-row checklist per engine so the verdict can be
checked rather than believed.

Drafted and then audited by two independent adversarial reviews (one
against the trees' facts and settled decisions, one against engineering
judgement). The audit changed the draft materially: the book's bridge
chapters are done (#68), so they are off the list; rational has no
cross-engine validation by design (scipy and decimate.h are its legs);
TSan runs async only; four host compilers and three QEMU targets, not the
draft's counts; the +1.9 % M55 ratio23_q15_eco drift is ab8fea3's stated
move, not a DspTap bump, and the chain rows sit +926/+1,102; the hardware
block never ran bridge or rational on silicon; nothing builds with
-fno-exceptions; TSan has never seen the converter with two threads; no
footprint, stack or worst-single-call figure exists; Setup 1's "< 1 ppm"
criterion tests the counting, not the servo, and cheap USB dongles may not
own a crystal; the timestamp servo is a four-to-five-week extension, not an
overload, and must not be frozen in the release that adds it; "C ABIs
frozen" needs a reconciled NULL model, an extension rule, SOVERSION and a
compat test. Section 7 holds the six decisions the maintainer takes before
week 1, each with a recommendation.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3HxzEGiZp7jStoMuYNuBK
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants