NativeArray
NativeArray — flat numeric arrays for high-performance numeric loops.
Unlike Array (which uses a 32-way persistent trie), NativeArray is backed by a flat OCaml array that maps directly to sequential memory. This makes it cache-friendly for sequential scan patterns and enables tight inner loops for sum, map, and fold operations.
Compiled (--compile) vectorization, in practice:
- sum_int / sum_float: both auto-vectorize under clang -O2 (NEON on
arm64, SSE4.2+ on x86-64) — sum_float needed a scoped
`#pragma clang fp reassociate(on)` in the runtime C loop, since
strict IEEE 754 float semantics otherwise block the reduction from
vectorizing at all.
- map_int with a non-capturing or single-capture lambda: the compiler
inlines the callback directly into the loop instead of dispatching
through a closure pointer, which lets clang's own inliner and
vectorizer see through it — arithmetic-heavy bodies (`fn x -> x*x+1`,
`fn x -> x + captured`) compile to real SIMD instructions. A closure
reused elsewhere, or one with more than one captured variable, still
works correctly but falls back to the slower closure-dispatch path.
- map_float: correct and (for capturing/non-capturing alike) gets the
same closure-inlining treatment as map_int. Float crossing a
closure-call boundary is normally heap-boxed (march_alloc_float),
which blocks vectorization — but when the callback's signature is
concretely all-Float (no generic type variable), the compiler clones
the inlined callback under natural double params/return with zero
boxing, and the loop DOES vectorize (real NEON/SSE, confirmed via
-emit-llvm). A callback whose signature is still generic at this
point falls back to the boxed (correct, non-vectorized) path. See
specs/optimizations.md P10 "Float-boxing Stage 4, Option B" and
specs/plans/2026-07-13-float-boxing-design.md.
- fold_int / fold_float have no compiled implementation yet — calling
them from a --compile build will fail to link. Use sum/map or a
manual index loop until this lands.
- map2_int / map2_float (two-array zip-with, e.g. for column-column
arithmetic) get the identical closure-inlining/vectorization treatment
map_int/map_float do above — same eligibility bar, same unboxed clone
for a concrete-Float callback. Verified ~47x faster than the pre-fix
boxed closure-call path on a 5M-element benchmark; see
docs/simd-benchmarks.md's "Fix history: map2".Two opaque element types are supported: NativeIntArr — wraps int array NativeFloatArr — wraps float array
Usage: let arr = NativeArray.make_float(1000, 0.0) let arr2 = NativeArray.map_float(arr, fn x -> x *. 2.0) let s = NativeArray.sum_float(arr2)
Functions
Left fold over all elements. Argument order: collection first, init second, callback last (uncurried-collection convention). Callback f(acc, elem) = new_acc. O(n).
Left fold over all elements. Argument order: collection first, init second, callback last (uncurried-collection convention). Callback f(acc, elem) = new_acc. O(n).
Build a NativeFloatArr from a List(Float). O(n).
Build a NativeIntArr from a List(Int). O(n).
Return the element at index [i] (0-based). Panics if out of bounds.
Return the element at index [i] (0-based). Panics if out of bounds.
Return the number of elements in [arr].
Return the number of elements in [arr].
Create a new float array of [n] elements, each initialised to [fill].
Create a new integer array of [n] elements, each initialised to [fill].
Apply [f] elementwise to two same-length arrays, returning a new NativeFloatArr. f(a_elem, b_elem) = out_elem. Panics if the arrays differ in length. O(n).
Apply [f] elementwise to two same-length arrays, returning a new NativeIntArr. f(a_elem, b_elem) = out_elem. Panics if the arrays differ in length. O(n).
Apply [f] to every element, returning a new NativeFloatArr. O(n).
Apply [f] to every element, returning a new NativeIntArr. O(n).
Return a new array with element [i] replaced by [v]. O(n).
Return a new array with element [i] replaced by [v]. O(n).
Sum all elements. O(n) tight loop.
Sum all elements. O(n) tight loop.
Widen every element to Float, returning a new NativeFloatArr. O(n).
Convert a NativeFloatArr to a List(Float). O(n).
Convert a NativeIntArr to a List(Int). O(n).