Skip to content

scripts/bench: add memory.py, the peak-memory harness - #223

Merged
cjpais merged 1 commit into
mainfrom
memory/bench-memory-py
Oct 20, 2026
Merged

cjpais merged 1 commit into
mainfrom
memory/bench-memory-py

Conversation

@cjpais

@cjpais cjpais commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

What

scripts/bench/memory.py, the peak-memory harness that the memory work keeps quoting numbers from.

Every memory branch is accepted on a number, and those numbers are only comparable if they come from one harness run one process at a time (two concurrent runs on a 16 GB host corrupt peaks). Until now that was a throwaway shell script in tmp/, which is why the numbers in the audit, in #220 and in the moonshine work were hand-copied between files.

# one cell
uv run scripts/bench/memory.py --models granite-4.0-1b-speech --samples jfk

# a sweep at the plan's reference length, warm (2 runs, keep the last)
uv run scripts/bench/memory.py --models granite-4.0-1b-speech,canary-qwen-2.5b \
    --samples dots-full --repeat 2

# duration scaling (cuts and caches the sample with ffmpeg)
uv run scripts/bench/memory.py --models sensevoice --samples dots-full --duration 1200

# A/B a branch switch; --env for library env vars, `--` for CLI flags
uv run scripts/bench/memory.py --models granite-4.0-1b-speech --samples jfk \
    --label copy --env TRANSCRIBE_NO_GATE_UP_ALIAS=1

Reuses run.py's model/quant resolution and machine detection, so variants are spelled the same way as the perf matrices, and writes reports/memory/<machine-slug>/<label>.csv.

Why these columns

After #195 peak RSS stopped being the number to trust: it counts mapped file pages the OS can reclaim and mostly misses Metal-owned buffers. cohere sits at 490 MB RSS with 4.4 GB of scheduler scratch; qwen3-asr at 1215 MB RSS with 6.0 GB. A harness that only reported RSS would have said both were fine. So per cell it records:

  • peak RSS — the kernel's high-water mark, never sampled, so no peak is missed
  • peak footprint — macOS phys_footprint; the best single "does this fit on this machine"
  • peak RssAnon — Linux only, sampled; the share an OOM killer charges, which is the whole argument behind mmap model weights instead of copying them into anonymous memory #195
  • scheduler scratch — summed over backends from the wave-0 instrumentation (backend: log per-graph scheduler scratch at debug level #217), with the CPU=x;MTL0=y split in notes; the portable number where RSS under-reports GPU scratch
  • load time, wall, exit code, transcript hash (so two runs can be compared without keeping the text), and flags such as gate_up=aliased / repetition-guard

Anything the host cannot measure stays empty rather than being faked. --repeat 2 gives a warm number; --report min exists for noisy hosts and is never mixed with last in one table.

Scope

Script only: no library code, no build change, no ABI. It is already what produced the 16-variant re-baseline in reports/memory/ and the numbers in the moonshine PR.

Every memory branch is accepted on a number, and notes/memory-reduction-plan.md
assumes a harness that makes those numbers comparable: one transcribe-cli
process at a time (a 16 GB host corrupts peaks otherwise), the same sample, the
same metric, appended to a CSV. Until now that was a throwaway shell script in
tmp/, which is why the numbers in the audit and the pack-gate-up work were
hand-copied.

    uv run scripts/bench/memory.py --models granite-4.0-1b-speech --samples jfk
    uv run scripts/bench/memory.py --models <list> --samples dots-full --repeat 2
    uv run scripts/bench/memory.py --models sensevoice --duration 1200

Reuses run.py's model/sample resolution and machine detection, so variants and
quants are spelled the same way as the perf matrices. Per cell it records:

  - peak RSS (the kernel's high-water mark, never sampled: no missed peak)
  - peak footprint (macOS /usr/bin/time -l; what the OS actually accounts)
  - peak RssAnon (Linux, sampled - the share an OOM killer charges, and the
    reason #195 exists)
  - scheduler scratch, summed over backends from the wave-0 instrumentation,
    which is the portable number on Metal where RSS under-reports GPU scratch
  - load time, wall time, exit code, and a transcript hash so a branch can be
    diffed against another run without keeping the text

Anything hostile to measurement is left out rather than faked: columns the host
cannot measure stay empty. --repeat 2 gives a warm number; --report min is
there for noisy hosts but never mixed with last in one table. --env and `--`
pass branch-specific switches through without the script knowing about them.
@cjpais
cjpais merged commit 473dfeb into main Oct 20, 2026
2 checks passed
cunba-ai pushed a commit to cunba-ai/transcribe.cpp that referenced this pull request Oct 27, 2026
…r#220, handy-computer#223, handy-computer#222, handy-computer#225, handy-computer#224)

- handy-computer#220 causal_lm: alias the packed gate+up projection onto the
  existing weights (no duplicate conversion copies at load)
- handy-computer#223 scripts/bench: memory.py peak-memory harness
- handy-computer#222 moonshine_streaming: bounded encoder memory past the 82 s
  training window and an out-of-bounds read fix; input-cap smoke
  replaces the truncation test
- handy-computer#225 catalog: Apple M4 speed cells published; weak-machine targets
  capped
- handy-computer#224 qwen3-asr nar: encoder graph memory bounded

Fork note: no fork-owned files in the conflict set.
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.

1 participant