Skip to content

feat(builtins): vectorized buffer kernels — buf_mix / buf_scale_range / buf_fill / buf_peak / buf_dot (#597)#598

Merged
InauguralPhysicist merged 1 commit into
mainfrom
feat/buf-vectorized-597
Jul 14, 2026
Merged

feat(builtins): vectorized buffer kernels — buf_mix / buf_scale_range / buf_fill / buf_peak / buf_dot (#597)#598
InauguralPhysicist merged 1 commit into
mainfrom
feat/buf-vectorized-597

Conversation

@InauguralPhysicist

Copy link
Copy Markdown
Collaborator

Closes #597

Why

DeslanStudio (the DAW, first end-user product) measured its render mix-down at 9.6 s for a 5 s arrangement on the N3350 — the mix kernel (dst[i] += src[j] * gain) is an interpreted per-sample loop (~441k dispatched iterations per stem pass), while stem rendering took 0.16 s riding the C-backed buf_copy. The autotune preview (YIN autocorrelation) freezes the UI for ~minutes. First native-performance capability demand from a product consumer; the fix is the same move that made buf_copy fast.

What

C bulk kernels over explicit [off, count) windows of numeric buffers (plain loops — C-speed is the win):

builtin semantics
buf_mix of [dst, src, dst_off, src_off, count, gain] in-place dst[dst_off+i] += src[src_off+i] * gain (the ab_mix_into kernel); same-buffer overlap runs forward in index order
buf_scale_range of [b, off, count, gain] in-place range multiply (fades/normalize)
buf_fill of [b, off, count, value] bulk store over a range
buf_peak of [b, off, count] max abs value (normalize/meter scans); 0 for empty window
buf_dot of [a, b, a_off, b_off, count] windowed dot product (YIN autocorrelation), under dot's unspecified-association contract, num_guard per step
  • VM-identical arithmetic: num_guard per step, so each builtin is exactly equal to the equivalent interpreted loop — pinned by a differential suite leg on seeded (Park–Miller) pseudo-random buffers.
  • Loud bounds (no silent truncation — a clamped mix is a silently wrong render): negative count / non-numeric args raise value, out-of-range windows raise index_range; overflow-immune subtraction-form checks (off > n - count); count 0 is a valid no-op. buf_copy's silent-null bad-bounds path upgraded to raise the same way (the load_file: missing path returns null without raising (silent success) #490matmul: shape/type errors return null without raising #512 direction); test_builtin_overflow.sh's huge-offset probe now pins the catchable-raise property.
  • Sandbox: all five join the pure-compute allowlist — argument-only, no allocation (nothing to sandbox_charge), no globals.
  • Tape-neutral: pure compute, no nondeterminism, deterministic replay unaffected.
  • Freestanding: ungated like the rest of the buf_* family; make freestanding-check green (no new symbols).
  • Existing buf_copy reused (src-first arg order kept); existing full-buffer dot untouched — buf_dot adds the windowed form.

Perf proof (n=5 medians, N3350)

tests/bench_buf_mix.eigs — mixing 220,500 stereo samples (5 s @ 44.1 kHz = 441,000 doubles), wall-clock per process run, setup-only base mode subtracted; builtin leg runs 100 passes to rise above the startup floor:

leg median total per mix pass
base (setup only) 95 ms
interpreted loop, JIT on 199 ms 104 ms
interpreted loop, EIGS_JIT_OFF=1 423 ms (base 225 ms) 198 ms
buf_mix ×100 318 ms 2.23 ms

Speedup: ~47x vs the JIT'd loop, ~89x vs the interpreter. A 5 s arrangement's per-stem mix kernel drops from ~104 ms to ~2.2 ms per pass — the 9.6 s render's kernel cost falls to milliseconds (consumer rewiring happens downstream in DeslanStudio, separately).

Gates

  • Release suite: 2871/2871
  • ASan+UBSan detect_leaks=1: 2875/2875, leak tally 0 (no NOTE line)
  • make freestanding-check: both stages OK
  • tools/stdlib_index_check.sh: 222 builtins + 75 modules all documented
  • New suite section: vectorized buffer kernels (#597) — correctness, raise-on-bad-window, differential legs

🤖 Generated with Claude Code

…uf_fill/buf_peak/buf_dot (#597)

DeslanStudio's render mix-down measured 9.6s for a 5s arrangement on the
N3350 (stems: 0.16s, riding C-backed buf_copy) because the mix kernel
dst[i] += src[j] * gain was an interpreted per-sample loop — the first
native-performance demand from a product consumer. Add C bulk ops over
explicit [off, count) windows of numeric buffers:

- buf_mix of [dst, src, dst_off, src_off, count, gain] — in-place mix
  (the ab_mix_into kernel); same-buffer overlap runs forward in index order
- buf_scale_range of [b, off, count, gain] — in-place range multiply
- buf_fill of [b, off, count, value] — bulk store over a range
- buf_peak of [b, off, count] — max |x| (normalize/meter scans)
- buf_dot of [a, b, a_off, b_off, count] — windowed dot product (YIN
  autocorrelation), dot's unspecified-association contract

Arithmetic mirrors the VM (num_guard per step) so each builtin is exactly
equal to the equivalent interpreted loop — pinned by a differential suite
leg on seeded pseudo-random buffers (test_buf_vectorized.eigs, suite
section "vectorized buffer kernels (#597)").

Bounds are loud (no silent truncation — a clamped mix is a silently wrong
render): negative count / non-numeric args raise `value`, out-of-range
windows raise `index_range`, overflow-immune subtraction-form checks;
count 0 is a valid no-op. buf_copy's silent-null bad-bounds path is
upgraded to raise the same way (the #490#512 direction);
test_builtin_overflow.sh's probe now pins the catchable-raise property.

All five join the sandbox pure-compute allowlist (argument-only, no
allocation, tape-neutral — nothing nondeterministic to record).

Perf (tests/bench_buf_mix.eigs, 220,500 stereo samples = 5s @ 44.1kHz,
n=5 medians on the N3350): interpreted mix pass 104 ms (JIT) / 198 ms
(interpreter) vs buf_mix 2.23 ms/pass — ~47x / ~89x.

Closes #597

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@InauguralPhysicist InauguralPhysicist merged commit 83037d5 into main Jul 14, 2026
17 checks passed
@InauguralPhysicist InauguralPhysicist deleted the feat/buf-vectorized-597 branch July 14, 2026 03:41
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.

buffer: vectorized bulk math builtins — interpreted per-sample loops make real-time audio impossible

1 participant