Repository navigation
A8: the host ratchet, callgrind counts gated beside the QEMU legs - #91
Merged
Merged
Conversation
The roadmap's A8 (row P10, Q6: a hard gate, two-sided at ±3 %). The
channel-parallel host path sat outside every ratchet: no QEMU leg compiles
it (TAP_DSP_CHANNEL_PARALLEL is off under Helium, the M33's DSP extension
and Hexagon).
- Workloads (<engine>/bench/icount/host_main.cpp, binaries
tap_sr_<engine>_hostcount_*, host builds only): each engine's float
converter streams 128-frame blocks for about two seconds at the default
profile, at 2 and 12 channels. async runs duplex push()/pull() (its 12
channels take the frame-major channel-parallel path), bridge runs both
directions, rational runs up and down by 2.
- scripts/icount.py --target host runs them under valgrind's callgrind
and gates against the "host" key of each engine's bench/baselines.json
with the same two-sided mechanics. The count is pinned to the code, the
compiler and the C library:
- Only tap_sr_host_counted(), the streaming loop, is counted
(--toggle-collect). Construction, the fixture's sin() calls, process
start-up and the environment stay outside.
- Symbols bind eagerly (LD_BIND_NOW), so no PLT resolution lands in
the window.
- GLIBC_TUNABLES holds glibc's CPU-dispatched routines (memcpy, libm)
to their x86-64 baseline variants and pins the copy thresholds glibc
otherwise derives from cache sizes. Before the pin, this machine's
memcpy was the AVX variant, 1 % of the 12-channel async count.
- CI: a host-ratchet job on ubuntu-24.04 that installs valgrind, records
the image's g++-13, libc6 and valgrind versions, builds the workloads
Release with g++-13 and no -march, and gates each engine in its own
step.
Recorded here with g++ 13.3.0 (13.3.0-6ubuntu2~24.04.1), glibc 2.39 and
valgrind 3.22, the runner image's versions. Two runs of one binary, a
second build with another generator, and a replay of the CI job's steps
all count identically. The counts (instructions over the stream):
async pipeline_float 2ch 109,526,808 12ch 362,078,536
bridge up_float 2ch 79,352,295 12ch 470,621,528
down_float 2ch 99,664,796 12ch 593,181,679
rational up2_float 2ch 57,296,876 12ch 288,223,796
down2_float 2ch 29,983,981 12ch 161,054,642
async at 12 channels is about 30 M instructions per channel against 55 M at
2, which is what the channel-parallel path buys; bridge, with no frame-major
path, costs the same per channel at both counts.
The README tables gain a host table from the same generator; the QEMU
tables are unchanged. The book's icount chapter gains "The host, under
callgrind", and the root CLAUDE.md and the roadmap (A8 landed, P10 green)
describe it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016GKzq1VSdcMYCRMhddiMaf
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.
What this changes
This is the roadmap's A8 (row P10; Q6 decided a hard gate, two-sided at ±3 %). Each engine gets host workloads (
bench/icount/host_main.cpp, binariestap_sr_<engine>_hostcount_*):scripts/icount.py --target hostcounts them under valgrind's callgrind and gates them against a newhostkey in eachbench/baselines.json, with the same mechanics as the QEMU legs.Instruction-count ratchet (host), runs the gate.Why
The channel-parallel host path sat outside every ratchet.
TAP_DSP_CHANNEL_PARALLELis off under Helium, the M33's DSP extension and Hexagon, so no QEMU leg compiles it, and a host regression there could land unseen. async's 12-channel rows run that path.Verification
What pins the count to the code, the compiler and the C library:
tap_sr_host_counted(), the streaming loop, is counted (--toggle-collect). Construction, the fixture'ssin()calls, process start-up and the environment stay outside.LD_BIND_NOW=1, so no PLT resolution lands inside the counted window.GLIBC_TUNABLESholds glibc's CPU-dispatched routines to their x86-64 baseline variants and pins the copy thresholds glibc derives from cache sizes. Before the pin, this machine'smemcpyresolved to the AVX variant, 1 % of the 12-channel async count.-march. The compiled code is the x86-64 baseline whatever the runner.Recorded with g++ 13.3.0 (
13.3.0-6ubuntu2~24.04.1), glibc 2.39 (2.39-0ubuntu8.9) and valgrind 3.22; the job prints the runner's versions. All of these count identically:On CI: the runner reproduced all ten counts exactly, +0 against every baseline with the same checksums (job 115919836408, run 38583667902), and all 32 checks are green.
pipeline_floatup_floatdown_floatup2_floatdown2_floatasync at 12 channels runs about 30 M instructions per channel over the stream, against 55 M at 2: that is what the channel-parallel path buys. bridge has no frame-major path and costs the same per channel at both counts.
Also checked:
-Werrorbuild, clang-tidy and clang-format are clean on the three new files.check_retired_words.pypasses.Notes for the reviewer
🤖 Generated with Claude Code
https://claude.ai/code/session_016GKzq1VSdcMYCRMhddiMaf