feat(builtins): vectorized buffer kernels — buf_mix / buf_scale_range / buf_fill / buf_peak / buf_dot (#597)#598
Merged
Conversation
…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>
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.
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-backedbuf_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 madebuf_copyfast.What
C bulk kernels over explicit
[off, count)windows of numeric buffers (plain loops — C-speed is the win):buf_mix of [dst, src, dst_off, src_off, count, gain]dst[dst_off+i] += src[src_off+i] * gain(theab_mix_intokernel); same-buffer overlap runs forward in index orderbuf_scale_range of [b, off, count, gain]buf_fill of [b, off, count, value]buf_peak of [b, off, count]buf_dot of [a, b, a_off, b_off, count]dot's unspecified-association contract, num_guard per stepnum_guardper 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.value, out-of-range windows raiseindex_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) #490–matmul: shape/type errors return null without raising #512 direction);test_builtin_overflow.sh's huge-offset probe now pins the catchable-raise property.sandbox_charge), no globals.buf_*family;make freestanding-checkgreen (no new symbols).buf_copyreused (src-first arg order kept); existing full-bufferdotuntouched —buf_dotadds 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-onlybasemode subtracted; builtin leg runs 100 passes to rise above the startup floor:EIGS_JIT_OFF=1buf_mix×100Speedup: ~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
detect_leaks=1: 2875/2875, leak tally 0 (no NOTE line)make freestanding-check: both stages OKtools/stdlib_index_check.sh: 222 builtins + 75 modules all documentedvectorized buffer kernels (#597)— correctness, raise-on-bad-window, differential legs🤖 Generated with Claude Code