Repository navigation
perf: SIMD kernels for counts array scans in the WASM histograms - #62
Open
alexvictoor wants to merge 1 commit into
Open
alexvictoor wants to merge 1 commit into
alexvictoor wants to merge 1 commit into
Conversation
Vectorize the whole-array loops of the AssemblyScript histograms whose counts live in a flat StaticArray (8/16/32/64 bit storage): - getValueAtPercentile: sum 64 bytes of counts per step with widening pairwise adds, only falling back to the scalar loop inside the block where the running total crosses the target count. - add / subtract (same bucket layout, same element width): lane-wise add/sub, one vector at a time, with the other side's total summed in the same pass. - establishInternalTackingValues: skip all-zero vectors with any_true, only inspect lanes of the first and last non-zero vector. - Storage.clear / Storage.resize: memory.fill / memory.copy. Type and width dispatch is resolved at compile time (idof / sizeof), so each Histogram instantiation gets a straight-line kernel and the PackedArray-backed histogram keeps its scalar paths. Builds and the AssemblyScript test runner now pass --enable simd. Measured against the previous binary in the same process (node 24, median of 15 rounds), 32 bit histogram with ~45k counts: getValueAtPercentile 20.7µs -> 3.2µs, add+subtract 42.5µs -> 12.1µs, reset 16.1µs -> 0.8µs. 8 bit storage gains 14-16x on percentiles. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013Jg4gNfTZpd8eVPAvqx3js
This was referenced Oct 26, 2026
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.
Summary
Vectorizes the whole-counts-array loops of the AssemblyScript histograms backed by flat storage (8/16/32/64 bit counts). The packed histogram keeps its scalar paths.
getValueAtPercentile: sums 64 bytes of counts per step with widening pairwise adds (extadd_pairwise/extend_low|high), and only runs the scalar loop inside the block where the running total crosses the target count.add/subtract(same bucket layout and same element width): lane-wiseadd/subone vector at a time, with the other side's total accumulated in the same pass. Mixed widths and packed operands fall back to the existing scalar loop.establishInternalTackingValues: skips all-zero vectors withany_trueand only looks at individual lanes of the first and last non-zero vector (used bysubtractand by decoding).Storage.clear/Storage.resize:memory.fill/memory.copy(bulk memory, already enabled by default).Dispatch on storage type and element width is resolved at compile time (
idof/sizeofin foldedifs), so eachHistogram<T, U>instantiation gets a straight-line kernel and no runtime branch.Builds and the AssemblyScript test runner now pass
--enable simd. WebAssembly SIMD is supported by every current browser (Chrome 91+, Firefox 89+, Safari 16.4+) and Node 16.4+; the README WebAssembly section now says so. On an older runtimeinitWebAssembly()rejects, as before for any unsupported module.Benchmarks
src/bench/wasm-compare.mjs(new) loads two builds of the module in the same process and runs an identical seeded workload against both. Numbers below: previousmasterbinary vs this branch, Node 24.16 on an Apple Silicon laptop, median of 15 rounds. Histograms hold 1024 random values with counts 1..100 (the "wide" scenario mirrorssrc/bench's defaultbuild()config: auto-resize, 3 digits, values up to 2^53, about 45k counts).Full run (all scenarios, all storage types)
Packed histograms are unchanged within noise (they never take the SIMD paths). Wasm binary grows from 119,670 to 122,509 bytes before compression.
Tests
assembly/__tests__/HistogramCountsKernels.test.ts(new) cross-checks each kernel against a scalar reference loop, for every storage width, with array lengths that leave partial tail blocks, with counts that exercise the top bit of each width, and with edge cases (empty histogram, count only at index 0, single count in the last lane, histogram added to itself). A mutation check (signed instead of unsigned widening,subinstead ofadd) fails these tests as expected.npm run astest: 249/249.npm test: 180/180 against the regenerated binary.🤖 Generated with Claude Code
https://claude.ai/code/session_013Jg4gNfTZpd8eVPAvqx3js