Repository navigation
feat(ffi): primitive arrays cross cljw.ffi and wasm memory at memcpy speed (ADR-0204) - #82
Merged
Merged
Conversation
A Java array now carries its element kind. object arrays hold Values and are traced; byte/short/int/long/float/double/char/boolean arrays hold raw contiguous native elements (i8 .. f64), untraced. A primitive array is a C array as it stands, so cljw.ffi :pointer passes its storage with no copy, :bytes is one memcpy, and socket I/O, String. and System/arraycopy are memcpy/memmove. Constructors are native (__array-make / __array-from / __array-fill), converting like clj's RT.longArray; aset is strict (clj's argument type mismatch). bytes? is true only for a byte array, retiring AD-051; AD-019 narrows to make-array / into-array, which have no Class to read a kind from. Kanban 20261021235518-022419a9. Smell-audited: 3: type erasure removed at the container, Values unchanged (F-004/F-005 hold); one store path with strict/convert modes instead of per-ctor coerce fns
A :pointer argument takes a primitive array and passes its storage; C reads and writes it in place for the call. The ffi suite covers double, int, float, long (C write-back) and byte arrays, :bytes copy semantics, and the arg-type refusals. README gains the full types table and the array-constructor to C element table. Kanban 20261021235518-022419a9.
wasm/mem-write! takes a primitive array as well as a vector: where the
array's kind is the dtype's layout (double-array and :f64, long-array
and :i64, ...) it is one memcpy, else element-wise with the vector
path's refusals. wasm/mem-read-array answers a primitive array, one
memcpy where the layout matches (:u16 / :u32 widen). New module
memory_array.zig keeps surface.zig from growing.
bench/ffi_prim_arrays.clj measures the 1000-element dot. ReleaseSafe
-Dwasm, loaded host, best of 5, us per call:
v1.15.3 :bytes call only 19.6, pack + call 51,679, Clojure dot 2,174
this :pointer double-array call 1.16, build arrays + call 29.1,
:bytes call only 1.73 (memcpy), pack + call 40,224
e2e phase16_wasm_memory covers every array kind (i8 i16 i32 i64 f32
f64), unsigned widening, a cross-kind convert and three refusals.
Kanban 20261021235518-022419a9.
Smell-audited: 2: bulk path split into its own module (surface.zig over the size ceiling); shared arg resolvers made pub rather than duplicated
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.
Primitive arrays cross cljw.ffi and wasm memory at memcpy speed
Kanban 20261021235518-022419a9 (CLJW-FFI-PRIM-ARRAY-BYTES). ADR-0204, recorded in memory as 20261022135049-25d05162.
What changed
object-arrayholds Values and is traced.byte/short/int/long/float/double/char/booleanarrays hold raw contiguous native elements (i8 to f64, a u32 code point for char, u8 for boolean) and are untraced. The constructors are native (__array-make/__array-from/__array-fill) and convert values the way clj'sRT.longArraydoes.asetis strict and raises clj's argument type mismatch.:pointerargument takes a primitive array and passes its storage, with no copy: C reads and writes it in place for the call.:bytesis now one memcpy.wasm/mem-write!accepts a primitive array, and the newwasm/mem-read-arrayreturns one. Each is one memcpy when the array's kind matches the dtype's layout; otherwise elements are converted one at a time. The code lives in a newmemory_array.zigbecausesurface.zigis already over the size ceiling.bytes?is true only for a byte array, which retires AD-051. AD-019 now covers onlymake-array/into-array: cljw has no Class object to read an element kind from.docs/examples/ffi/README.mdtypes table now includes the array-constructor-to-C-element table. The wasm and zig-native READMEs are updated.Measurement
bench/ffi_prim_arrays.clj, 1000-element f64 dot, ReleaseSafe -Dwasm, on a loaded shared host, best of 5, µs per call::pointer+ double-array, call:bytes, call only:bytes+ call(reduce + (map * xs xs))Acceptance asked for the dot within 2x of the raw 12 µs call. The
:pointercall takes 1.16 µs.Tests
test/clj/suites/ffi_test.clj: double, int, float, long (C writes back) and byte arrays;:bytescopy semantics; 64 KiB byte array; arg-type refusals.test/clj/suites/arrays_test.clj: per-kind storage, strict versus convert behaviour.test/e2e/phase16_wasm_memory.sh: every array kind (i8 i16 i32 i64 f32 f64) round-trips, unsigned widening, a cross-kind conversion, three refusals.test/e2e/phase14_arity_parity.sh:bytes?parity.Ready for gate (run_gate.sh was not run locally; the host is shared).