tur/numvec
typed numeric arrays: F64Vec, F32Vec, I64Vec, I32Vec, U8Vec.
GENERATED by tools/gen-numvec.py -- edit the template there, not this file.
A fixed-length, homogeneous, unboxed array of one numeric element type, in the manner of Racket's flvector. Each type is its own opaque handle over one C layout:
struct { int64_t len; T *data; void *raw; } /* T = double, float, ... */
data is 32-byte aligned (raw is the malloc block it was carved from, and what free is given), so an aligned SIMD load is legal on it and gcc can skip its peeling prologue. The element is its real width: a U8Vec uses one byte a slot, not eight as a Vec does.
| Type | Element | Constructor | |--------|-----------|---------------------------| | F64Vec | float | (f64vec-new n init) | | F32Vec | float32 | (f32vec-new n init) | | I64Vec | int | (i64vec-new n init) | | I32Vec | int32 | (i32vec-new n init) | | U8Vec | uint8 | (u8vec-new n init) |
Every type has these operations, named by its prefix P (f64vec, f32vec, i64vec, i32vec, u8vec):
- access:
P-new,P-of,P-len,P-ref,P-set!,P-copy,P->vec,vec->P,P-show; - bulk kernels (phase N1), each one C loop that vectorizes at tur's default -O2 build as well as at
--release:P-fill!,P-copy!,P-add!,P-sum,P-min,P-max, and except on U8VecP-scale!,P-axpy!,P-dot; the float types addP-sum-ordered. A float reduction uses a fixed eight-lane order, so it is vectorized without -ffast-math and is bit-identical on every engine; integer arithmetic wraps; - loops (phase N2):
P-for,P-for-index,P-fold,P-build,P-map!, which bind elements, never an index the program could get wrong, and go through the unchecked accessors (#fx{Unsafe}). Reads vectorize;P-buildandP-map!store through a view (P-view,P-view-ref,P-view-set!), which is what lets a store loop vectorize too; - more kernels for common store loops, vectorized at -O2 on every compiler:
P-sub!,P-add-scalar!, and except on U8VecP-mul!.
Generic code over all five types goes through the NumArray class at the end of the module: na-len, na-ref, na-sum, ... and the loops na-for, na-fold, na-map!, with the element type as (Elem V).
Ownership: each type is ^affine with a Drop instance, so a let-bound array is freed at scope exit, exactly once, and moving it (returning it, passing it to a consumer) hands that duty on. The accessors take it ^borrow. P-free consumes an array early.
A temporary handed straight to an accessor that returns a number -- (f64vec-len (f64vec-new 4 0.0)) -- is freed after the call. One handed to anything else (P-copy, P->vec, P-show, or after an argument with side effects) has no owner and is not freed, so bind it first: (let [v (f64vec-new 4 0.0)] (f64vec-show v)) (docs/reported/borrowed-drop-temporary-leaks-outside-the-hoist.md).
A closure that captures an array the enclosing scope OWNS takes it over (the scope can no longer use it, and the closure frees it); one that captures a ^borrow parameter borrows it, and frees nothing.
Length is fixed at construction. Growth stays Vec's job; P->vec and vec->P convert. An index outside [0, len) panics, at the caller's line, on every engine.
Region stores: an element store writes a number, never a pointer, so the setters carry no TUR_REGION_NOTE (CLAUDE.md, "Region Store Hooks" covers a caller's word that can outlive a bracket -- a pointer).
Since: Phase N0 (simd-and-vectorization-plan)
F64Vec
(defopaque ^affine F64Vec : int)
a fixed-length, unboxed array of 64-bit floats (float).
Opaque handle to a struct { int64_t len; double *data; void *raw; }. ^affine: freed at scope exit by its Drop instance. Use f64vec-new or f64vec-of to make one.
Since: Phase N0
F64View
(defopaque F64View : int)
a view of a F64Vec's elements: the data pointer, for a store loop.
Made by f64vec-view, read and written by f64vec-view-ref / f64vec-view-set!. Not owned and not affine: it frees nothing, and it is valid exactly as long as the array it came from (an array never resizes, so its data never moves).
Since: Phase N2
f64vec-new
(f64vec-new [n : int init : float])
allocate a F64Vec of n elements, every one init.
| n | element count (>= 0; a negative or absurd n panics) | |
| init | the value every slot starts with |
A fresh F64Vec, owned by the caller and freed at scope exit.
(let [v (f64vec-new 4 7.1)]
(f64vec-ref v 2)) ; => 7.1
Since: Phase N0
f64vec-of
(f64vec-of [& xs])
a F64Vec holding the given elements, in order.
| & xs -- zero or more elements of type float |
A fresh F64Vec of length (count xs).
(let [v (f64vec-of 1.5 2.5)]
(f64vec-len v)) ; => 2
Since: Phase N0
f64vec-free
(f64vec-free [v : F64Vec])
release a F64Vec now, consuming it.
Rarely needed: a let-bound F64Vec is freed at scope exit. Call it to release a large array before the scope ends.
| v | the array (consumed) |
(f64vec-free v)
Since: Phase N0
Drop[F64Vec]
(definstance Drop [F64Vec])
free the array when a bound F64Vec dies unmoved.
f64vec-len
(f64vec-len [^borrow v : F64Vec])
the element count of a F64Vec.
| v | the array (borrowed) |
The length fixed at construction.
(let [v (f64vec-new 8 7.1)]
(f64vec-len v)) ; => 8
Since: Phase N0
f64vec-ref
(f64vec-ref [^borrow v : F64Vec i : int])
the element at index i (bounds-checked).
| v | the array (borrowed) | |
| i | zero-based index; outside [0, len) panics |
The element, typed float.
(let [v (f64vec-of 1.5 2.5)]
(f64vec-ref v 1)) ; => 2.5
Since: Phase N0
f64vec-set!
(f64vec-set! [^borrow v : F64Vec i : int x : float])
store x at index i (bounds-checked).
| v | the array (borrowed) | |
| i | zero-based index; outside [0, len) panics | |
| x | the new element |
(f64vec-set! v 0 7.1)
Since: Phase N0
f64vec-copy
(f64vec-copy [^borrow v : F64Vec])
a fresh F64Vec with the same elements.
| v | the array (borrowed) |
An independent copy, owned by the caller.
(let [c (f64vec-copy v)] (f64vec-set! c 0 7.1)) ; v is unchanged
Since: Phase N0
f64vec->vec-go__
(f64vec->vec-go__ [^borrow v : F64Vec out : (Vec float) i : int n : int] : (Vec float))
internal: push v[i..n) onto out.
f64vec->vec
(f64vec->vec [^borrow v : F64Vec] : (Vec float))
copy a F64Vec into a fresh (Vec float).
| v | the array (borrowed) |
A growable Vec holding the same elements; the caller frees it.
(let [a (f64vec-of 1.5 2.5)
v (f64vec->vec a)]
(vec-len v)) ; => 2
Since: Phase N0
vec->f64vec-go__
(vec->f64vec-go__ [^borrow out : F64Vec ^borrow src : (Vec float) i : int n : int] : nil)
internal: store src[i..n) into out.
vec->f64vec
(vec->f64vec [^borrow src : (Vec float)] : F64Vec)
copy a (Vec float) into a fresh F64Vec.
| src | the Vec (borrowed) |
A F64Vec of the same length and elements, owned by the caller.
(let [v (vec-of 1.5 2.5)
a (vec->f64vec v)]
(f64vec-len a)) ; => 2
Since: Phase N0
f64vec-show-loop__
(f64vec-show-loop__ [^borrow v : F64Vec i : int n : int b : StringBuilder] : StringBuilder)
internal: push "e0 e1 ..." onto the builder.
f64vec-show
(f64vec-show [^borrow v : F64Vec] : String)
render a F64Vec as an owned "#f64vec[a b c]" String.
| v | the array (borrowed) |
An owned String; release it with string/release.
(let [v (f64vec-of 1.5 2.5)]
(f64vec-show v)) ; => String "#f64vec[1.5 2.5]"
Since: Phase N0
f64vec-fill!
(f64vec-fill! [^borrow v : F64Vec x : float])
store x in every slot.
| v | the array (borrowed) | |
| x | the value |
(f64vec-fill! v 1.5)
Since: Phase N1
f64vec-copy!
(f64vec-copy! [^borrow dst : F64Vec ^borrow src : F64Vec])
copy src's elements into dst (same length, or a panic).
| dst | the array written (borrowed) | |
| src | the array read (borrowed) |
(f64vec-copy! dst src)
Since: Phase N1
f64vec-scale!
(f64vec-scale! [^borrow v : F64Vec s : float])
multiply every element by s, in place.
Integer arithmetic wraps.
| v | the array (borrowed) | |
| s | the factor |
(f64vec-scale! v 2.5)
Since: Phase N1
f64vec-add!
(f64vec-add! [^borrow dst : F64Vec ^borrow src : F64Vec])
dst[i] += src[i] for every i (same length, or a panic).
Integer arithmetic wraps; dst and src may be the same array.
| dst | the array updated (borrowed) | |
| src | the array added (borrowed) |
(f64vec-add! y x)
Since: Phase N1
f64vec-axpy!
(f64vec-axpy! [^borrow dst : F64Vec s : float ^borrow src : F64Vec])
dst[i] += s * src[i] for every i (same length, or a panic).
Integer arithmetic wraps; the multiply and the add are not fused.
| dst | the array updated (borrowed) | |
| s | the factor | |
| src | the array scaled and added (borrowed) |
(f64vec-axpy! y 2.5 x)
Since: Phase N1
f64vec-sub!
(f64vec-sub! [^borrow dst : F64Vec ^borrow src : F64Vec])
dst[i] -= src[i] for every i (same length, or a panic).
Integer arithmetic wraps; dst and src may be the same array.
| dst | the array updated (borrowed) | |
| src | the array subtracted (borrowed) |
(f64vec-sub! y x)
Since: Phase N1
f64vec-mul!
(f64vec-mul! [^borrow dst : F64Vec ^borrow src : F64Vec])
dst[i] *= src[i] for every i (same length, or a panic).
Element-wise, not a dot product. Integer arithmetic wraps; dst and src may be the same array.
| dst | the array updated (borrowed) | |
| src | the array multiplied in (borrowed) |
(f64vec-mul! y x)
Since: Phase N1
f64vec-add-scalar!
(f64vec-add-scalar! [^borrow v : F64Vec s : float])
add s to every element, in place.
Integer arithmetic wraps.
| v | the array (borrowed) | |
| s | the value added |
(f64vec-add-scalar! v 2.5)
Since: Phase N1
f64vec-sum
(f64vec-sum [^borrow v : F64Vec])
the sum of the elements.
A float sum is computed in eight interleaved partial sums (lane k takes the elements i with i mod 8 = k), combined in one fixed tree: the same answer on every engine and machine, and vectorizable without -ffast-math. It can differ in the last bits from -sum-ordered, the left-to-right sum. An integer sum wraps modulo 2^64; a narrow element is widened to int first.
| v | the array (borrowed) |
A float.
(let [v (f64vec-of 1.5 2.5)] (f64vec-sum v))
Since: Phase N1
f64vec-sum-ordered
(f64vec-sum-ordered [^borrow v : F64Vec])
the strict left-to-right sum of the elements.
| v | the array (borrowed) |
A float.
(let [v (f64vec-of 1.5 2.5)] (f64vec-sum-ordered v))
Since: Phase N1
f64vec-dot
(f64vec-dot [^borrow a : F64Vec ^borrow b : F64Vec])
the dot product of two arrays (same length, or a panic).
Floats use the same eight-lane order as -sum. Integers wrap modulo 2^64; an int32 product is formed in 64 bits.
| a | the first array (borrowed) | |
| b | the second array (borrowed) |
A float.
(let [a (f64vec-of 1.5 2.5) b (f64vec-of 2.5 1.5)] (f64vec-dot a b))
Since: Phase N1
f64vec-min
(f64vec-min [^borrow v : F64Vec])
the smallest element (an empty array panics).
A NaN element makes the result NaN (the first NaN, by index). Which of two equal elements (0.0 and -0.0) is returned is fixed by the reduction order, the same on every engine.
| v | the array (borrowed) |
A float.
(let [v (f64vec-of 1.5 2.5)] (f64vec-min v)) ; => 1.5
Since: Phase N1
f64vec-max
(f64vec-max [^borrow v : F64Vec])
the largest element (an empty array panics).
A NaN element makes the result NaN (the first NaN, by index). Which of two equal elements (0.0 and -0.0) is returned is fixed by the reduction order, the same on every engine.
| v | the array (borrowed) |
A float.
(let [v (f64vec-of 1.5 2.5)] (f64vec-max v)) ; => 2.5
Since: Phase N1
f64vec-ref-unchecked
(f64vec-ref-unchecked [^borrow v : F64Vec i : int])
the element at index i, NOT bounds-checked.
#fx{Unsafe}: call it inside (unsafe ...). The loop macros (-for, -fold, ...) use it where they have already checked the index. Its body calls nothing, so a call to it carries no panic check, and a loop of such calls can vectorize.
| v | the array (borrowed) | |
| i | zero-based index; the caller proves 0 <= i < len |
A float.
(unsafe (f64vec-ref-unchecked v 0))
Since: Phase N2
f64vec-set-unchecked!
(f64vec-set-unchecked! [^borrow v : F64Vec i : int x : float])
store x at index i, NOT bounds-checked.
#fx{Unsafe}: call it inside (unsafe ...). Stores a number: no TUR_REGION_NOTE (see the module header).
| v | the array (borrowed) | |
| i | zero-based index; the caller proves 0 <= i < len | |
| x | the new element |
(unsafe (f64vec-set-unchecked! v 0 1.5))
Since: Phase N2
f64vec-view
(f64vec-view [^borrow v : F64Vec])
a view of v's elements, for a loop that stores into it.
#fx{Unsafe}: nothing ties the view to the array, so a view used after the array is freed reads freed memory. Why it exists: a store through the array (-set-unchecked!) re-reads the header's data pointer, which the store might have overwritten as far as the C compiler knows, so the loop does not vectorize; a view is that pointer, read once. -build and -map! store through one; reach for it directly only for a store loop they do not cover.
| v | the array (borrowed); the view is valid while v is |
A F64View.
(let [v (f64vec-of 1.5 2.5) w (unsafe (f64vec-view v))] (unsafe (f64vec-view-ref w 1))) ; => 2.5
Since: Phase N2
f64vec-view-ref
(f64vec-view-ref [w : F64View i : int])
the element at index i of a view, NOT bounds-checked.
#fx{Unsafe}: call it inside (unsafe ...). The interpreter checks the index anyway, as it does for -ref-unchecked.
| w | the view | |
| i | zero-based index; the caller proves 0 <= i < len |
A float.
(unsafe (f64vec-view-ref w 0))
Since: Phase N2
f64vec-view-set!
(f64vec-view-set! [w : F64View i : int x : float])
store x at index i of a view, NOT bounds-checked.
#fx{Unsafe}: call it inside (unsafe ...). Stores a number: no TUR_REGION_NOTE. The interpreter checks the index anyway.
| w | the view | |
| i | zero-based index; the caller proves 0 <= i < len | |
| x | the new element |
(unsafe (f64vec-view-set! w 0 1.5))
Since: Phase N2
f64vec-for
(f64vec-for [bv & body])
run body once per element of one or two F64Vecs.
(for [x v] body...) binds x to each element of v in order; (for [x v y w] body...) walks v and w together (same length, or a panic before the first pass). No index is in scope, so none can be wrong: the reads are f64vec-ref-unchecked, the body sees plain elements, and the loop carries no bounds check or panic test -- gcc can vectorize it at -O3. An array argument that is not a name is evaluated once, and freed after.
| bindings | [x v] or [x v y w] | |
| body | the forms to run per element |
(let [v (f64vec-of 1.5 2.5)] (f64vec-for [x v] (println x)))
Since: Phase N2
f64vec-for-index
(f64vec-for-index [bv & body])
run body once per element, with its index.
(for-index [i x v] body...) binds i (read-only) and x = v[i].
| bindings | [i x v] | |
| body | the forms to run per element |
(let [v (f64vec-of 1.5 2.5)] (f64vec-for-index [i x v] (println i)))
Since: Phase N2
f64vec-fold
(f64vec-fold [bv body])
fold the elements of a F64Vec into an accumulator.
(fold [acc init x v] body): acc starts at init, and for each element x in order becomes the value of body (which sees acc and x). The result is the final acc. A float sum written this way is the strict left fold, so gcc keeps it scalar; f64vec-sum is the vectorized one.
| bindings | [acc init x v] | |
| body | the new accumulator |
The final accumulator.
(let [v (f64vec-of 1.5 2.5)] (f64vec-fold [n 0 x v] (+ n 1))) ; => 2
Since: Phase N2
f64vec-build
(f64vec-build [n bv expr])
a fresh F64Vec of n elements, element i the value of expr.
(build n [i] expr), after Racket's for/flvector: i is bound to each index in turn, and expr runs outside any unsafe. The stores go through a view (f64vec-view), so the loop vectorizes where expr allows: at -O2 under clang, at -O3 (--release) under gcc.
| n | the length (evaluated once) | |
| i | the index name, in a vector | |
| expr | the element at i, of type float |
A fresh F64Vec, owned by the caller.
(let [v (f64vec-build 3 [i] 1.5)] (f64vec-len v)) ; => 3
Since: Phase N2
f64vec-map!
(f64vec-map! [dst bv expr])
store expr into dst[i] for every i, x bound to src[i].
(map! dst [x src] expr) stores into each slot of dst the value of expr with x bound to the element of src at the same index; (map! dst [x a y b] expr) walks two sources together. dst may be one of the sources: (map! v [x v] {x * 2}) maps in place. Every array has the same length, or a panic before the first element. The loop reads and stores through views (f64vec-view), so it vectorizes where expr allows: at -O2 under clang, at -O3 (--release) under gcc. expr runs outside any unsafe; an array argument that is not a name is evaluated once, and freed after the loop.
| dst | the array stored into (borrowed) | |
| bindings | [x src] or [x a y b] | |
| expr | the new element, of type float |
(let [v (f64vec-of 1.5 2.5)] (f64vec-map! v [x v] x) (f64vec-ref v 1)) ; => 2.5
Since: Phase N2
f64vec-same-len__
(f64vec-same-len__ [^borrow a : F64Vec ^borrow b : F64Vec])
internal: the common length of two arrays, or a panic.
F32Vec
(defopaque ^affine F32Vec : int)
a fixed-length, unboxed array of 32-bit floats (float32).
Opaque handle to a struct { int64_t len; float *data; void *raw; }. ^affine: freed at scope exit by its Drop instance. Use f32vec-new or f32vec-of to make one.
Since: Phase N0
F32View
(defopaque F32View : int)
a view of a F32Vec's elements: the data pointer, for a store loop.
Made by f32vec-view, read and written by f32vec-view-ref / f32vec-view-set!. Not owned and not affine: it frees nothing, and it is valid exactly as long as the array it came from (an array never resizes, so its data never moves).
Since: Phase N2
f32vec-new
(f32vec-new [n : int init : float32])
allocate a F32Vec of n elements, every one init.
| n | element count (>= 0; a negative or absurd n panics) | |
| init | the value every slot starts with |
A fresh F32Vec, owned by the caller and freed at scope exit.
(let [v (f32vec-new 4 (:: 3.25 float32))]
(f32vec-ref v 2)) ; => (:: 3.25 float32)
Since: Phase N0
f32vec-of
(f32vec-of [& xs])
a F32Vec holding the given elements, in order.
| & xs -- zero or more elements of type float32 |
A fresh F32Vec of length (count xs).
(let [v (f32vec-of (:: 1.5 float32) (:: 2.5 float32))]
(f32vec-len v)) ; => 2
Since: Phase N0
f32vec-free
(f32vec-free [v : F32Vec])
release a F32Vec now, consuming it.
Rarely needed: a let-bound F32Vec is freed at scope exit. Call it to release a large array before the scope ends.
| v | the array (consumed) |
(f32vec-free v)
Since: Phase N0
Drop[F32Vec]
(definstance Drop [F32Vec])
free the array when a bound F32Vec dies unmoved.
f32vec-len
(f32vec-len [^borrow v : F32Vec])
the element count of a F32Vec.
| v | the array (borrowed) |
The length fixed at construction.
(let [v (f32vec-new 8 (:: 3.25 float32))]
(f32vec-len v)) ; => 8
Since: Phase N0
f32vec-ref
(f32vec-ref [^borrow v : F32Vec i : int])
the element at index i (bounds-checked).
| v | the array (borrowed) | |
| i | zero-based index; outside [0, len) panics |
The element, typed float32.
(let [v (f32vec-of (:: 1.5 float32) (:: 2.5 float32))]
(f32vec-ref v 1)) ; => (:: 2.5 float32)
Since: Phase N0
f32vec-set!
(f32vec-set! [^borrow v : F32Vec i : int x : float32])
store x at index i (bounds-checked).
| v | the array (borrowed) | |
| i | zero-based index; outside [0, len) panics | |
| x | the new element |
(f32vec-set! v 0 (:: 3.25 float32))
Since: Phase N0
f32vec-copy
(f32vec-copy [^borrow v : F32Vec])
a fresh F32Vec with the same elements.
| v | the array (borrowed) |
An independent copy, owned by the caller.
(let [c (f32vec-copy v)] (f32vec-set! c 0 (:: 3.25 float32))) ; v is unchanged
Since: Phase N0
f32vec->vec-go__
(f32vec->vec-go__ [^borrow v : F32Vec out : (Vec float32) i : int n : int] : (Vec float32))
internal: push v[i..n) onto out.
f32vec->vec
(f32vec->vec [^borrow v : F32Vec] : (Vec float32))
copy a F32Vec into a fresh (Vec float32).
| v | the array (borrowed) |
A growable Vec holding the same elements; the caller frees it.
(let [a (f32vec-of (:: 1.5 float32) (:: 2.5 float32))
v (f32vec->vec a)]
(vec-len v)) ; => 2
Since: Phase N0
vec->f32vec-go__
(vec->f32vec-go__ [^borrow out : F32Vec ^borrow src : (Vec float32) i : int n : int] : nil)
internal: store src[i..n) into out.
vec->f32vec
(vec->f32vec [^borrow src : (Vec float32)] : F32Vec)
copy a (Vec float32) into a fresh F32Vec.
| src | the Vec (borrowed) |
A F32Vec of the same length and elements, owned by the caller.
(let [v (vec-of (:: 1.5 float32) (:: 2.5 float32))
a (vec->f32vec v)]
(f32vec-len a)) ; => 2
Since: Phase N0
f32vec-show-loop__
(f32vec-show-loop__ [^borrow v : F32Vec i : int n : int b : StringBuilder] : StringBuilder)
internal: push "e0 e1 ..." onto the builder.
f32vec-show
(f32vec-show [^borrow v : F32Vec] : String)
render a F32Vec as an owned "#f32vec[a b c]" String.
| v | the array (borrowed) |
An owned String; release it with string/release.
(let [v (f32vec-of (:: 1.5 float32) (:: 2.5 float32))]
(f32vec-show v)) ; => String "#f32vec[1.5 2.5]"
Since: Phase N0
f32vec-fill!
(f32vec-fill! [^borrow v : F32Vec x : float32])
store x in every slot.
| v | the array (borrowed) | |
| x | the value |
(f32vec-fill! v (:: 1.5 float32))
Since: Phase N1
f32vec-copy!
(f32vec-copy! [^borrow dst : F32Vec ^borrow src : F32Vec])
copy src's elements into dst (same length, or a panic).
| dst | the array written (borrowed) | |
| src | the array read (borrowed) |
(f32vec-copy! dst src)
Since: Phase N1
f32vec-scale!
(f32vec-scale! [^borrow v : F32Vec s : float32])
multiply every element by s, in place.
Integer arithmetic wraps.
| v | the array (borrowed) | |
| s | the factor |
(f32vec-scale! v (:: 2.5 float32))
Since: Phase N1
f32vec-add!
(f32vec-add! [^borrow dst : F32Vec ^borrow src : F32Vec])
dst[i] += src[i] for every i (same length, or a panic).
Integer arithmetic wraps; dst and src may be the same array.
| dst | the array updated (borrowed) | |
| src | the array added (borrowed) |
(f32vec-add! y x)
Since: Phase N1
f32vec-axpy!
(f32vec-axpy! [^borrow dst : F32Vec s : float32 ^borrow src : F32Vec])
dst[i] += s * src[i] for every i (same length, or a panic).
Integer arithmetic wraps; the multiply and the add are not fused.
| dst | the array updated (borrowed) | |
| s | the factor | |
| src | the array scaled and added (borrowed) |
(f32vec-axpy! y (:: 2.5 float32) x)
Since: Phase N1
f32vec-sub!
(f32vec-sub! [^borrow dst : F32Vec ^borrow src : F32Vec])
dst[i] -= src[i] for every i (same length, or a panic).
Integer arithmetic wraps; dst and src may be the same array.
| dst | the array updated (borrowed) | |
| src | the array subtracted (borrowed) |
(f32vec-sub! y x)
Since: Phase N1
f32vec-mul!
(f32vec-mul! [^borrow dst : F32Vec ^borrow src : F32Vec])
dst[i] *= src[i] for every i (same length, or a panic).
Element-wise, not a dot product. Integer arithmetic wraps; dst and src may be the same array.
| dst | the array updated (borrowed) | |
| src | the array multiplied in (borrowed) |
(f32vec-mul! y x)
Since: Phase N1
f32vec-add-scalar!
(f32vec-add-scalar! [^borrow v : F32Vec s : float32])
add s to every element, in place.
Integer arithmetic wraps.
| v | the array (borrowed) | |
| s | the value added |
(f32vec-add-scalar! v (:: 2.5 float32))
Since: Phase N1
f32vec-sum
(f32vec-sum [^borrow v : F32Vec])
the sum of the elements.
A float sum is computed in eight interleaved partial sums (lane k takes the elements i with i mod 8 = k), combined in one fixed tree: the same answer on every engine and machine, and vectorizable without -ffast-math. It can differ in the last bits from -sum-ordered, the left-to-right sum. An integer sum wraps modulo 2^64; a narrow element is widened to int first.
| v | the array (borrowed) |
A float32.
(let [v (f32vec-of (:: 1.5 float32) (:: 2.5 float32))] (f32vec-sum v))
Since: Phase N1
f32vec-sum-ordered
(f32vec-sum-ordered [^borrow v : F32Vec])
the strict left-to-right sum of the elements.
| v | the array (borrowed) |
A float32.
(let [v (f32vec-of (:: 1.5 float32) (:: 2.5 float32))] (f32vec-sum-ordered v))
Since: Phase N1
f32vec-dot
(f32vec-dot [^borrow a : F32Vec ^borrow b : F32Vec])
the dot product of two arrays (same length, or a panic).
Floats use the same eight-lane order as -sum. Integers wrap modulo 2^64; an int32 product is formed in 64 bits.
| a | the first array (borrowed) | |
| b | the second array (borrowed) |
A float32.
(let [a (f32vec-of (:: 1.5 float32) (:: 2.5 float32)) b (f32vec-of (:: 2.5 float32) (:: 1.5 float32))] (f32vec-dot a b))
Since: Phase N1
f32vec-min
(f32vec-min [^borrow v : F32Vec])
the smallest element (an empty array panics).
A NaN element makes the result NaN (the first NaN, by index). Which of two equal elements (0.0 and -0.0) is returned is fixed by the reduction order, the same on every engine.
| v | the array (borrowed) |
A float32.
(let [v (f32vec-of (:: 1.5 float32) (:: 2.5 float32))] (f32vec-min v)) ; => (:: 1.5 float32)
Since: Phase N1
f32vec-max
(f32vec-max [^borrow v : F32Vec])
the largest element (an empty array panics).
A NaN element makes the result NaN (the first NaN, by index). Which of two equal elements (0.0 and -0.0) is returned is fixed by the reduction order, the same on every engine.
| v | the array (borrowed) |
A float32.
(let [v (f32vec-of (:: 1.5 float32) (:: 2.5 float32))] (f32vec-max v)) ; => (:: 2.5 float32)
Since: Phase N1
f32vec-ref-unchecked
(f32vec-ref-unchecked [^borrow v : F32Vec i : int])
the element at index i, NOT bounds-checked.
#fx{Unsafe}: call it inside (unsafe ...). The loop macros (-for, -fold, ...) use it where they have already checked the index. Its body calls nothing, so a call to it carries no panic check, and a loop of such calls can vectorize.
| v | the array (borrowed) | |
| i | zero-based index; the caller proves 0 <= i < len |
A float32.
(unsafe (f32vec-ref-unchecked v 0))
Since: Phase N2
f32vec-set-unchecked!
(f32vec-set-unchecked! [^borrow v : F32Vec i : int x : float32])
store x at index i, NOT bounds-checked.
#fx{Unsafe}: call it inside (unsafe ...). Stores a number: no TUR_REGION_NOTE (see the module header).
| v | the array (borrowed) | |
| i | zero-based index; the caller proves 0 <= i < len | |
| x | the new element |
(unsafe (f32vec-set-unchecked! v 0 (:: 1.5 float32)))
Since: Phase N2
f32vec-view
(f32vec-view [^borrow v : F32Vec])
a view of v's elements, for a loop that stores into it.
#fx{Unsafe}: nothing ties the view to the array, so a view used after the array is freed reads freed memory. Why it exists: a store through the array (-set-unchecked!) re-reads the header's data pointer, which the store might have overwritten as far as the C compiler knows, so the loop does not vectorize; a view is that pointer, read once. -build and -map! store through one; reach for it directly only for a store loop they do not cover.
| v | the array (borrowed); the view is valid while v is |
A F32View.
(let [v (f32vec-of (:: 1.5 float32) (:: 2.5 float32)) w (unsafe (f32vec-view v))] (unsafe (f32vec-view-ref w 1))) ; => (:: 2.5 float32)
Since: Phase N2
f32vec-view-ref
(f32vec-view-ref [w : F32View i : int])
the element at index i of a view, NOT bounds-checked.
#fx{Unsafe}: call it inside (unsafe ...). The interpreter checks the index anyway, as it does for -ref-unchecked.
| w | the view | |
| i | zero-based index; the caller proves 0 <= i < len |
A float32.
(unsafe (f32vec-view-ref w 0))
Since: Phase N2
f32vec-view-set!
(f32vec-view-set! [w : F32View i : int x : float32])
store x at index i of a view, NOT bounds-checked.
#fx{Unsafe}: call it inside (unsafe ...). Stores a number: no TUR_REGION_NOTE. The interpreter checks the index anyway.
| w | the view | |
| i | zero-based index; the caller proves 0 <= i < len | |
| x | the new element |
(unsafe (f32vec-view-set! w 0 (:: 1.5 float32)))
Since: Phase N2
f32vec-for
(f32vec-for [bv & body])
run body once per element of one or two F32Vecs.
(for [x v] body...) binds x to each element of v in order; (for [x v y w] body...) walks v and w together (same length, or a panic before the first pass). No index is in scope, so none can be wrong: the reads are f32vec-ref-unchecked, the body sees plain elements, and the loop carries no bounds check or panic test -- gcc can vectorize it at -O3. An array argument that is not a name is evaluated once, and freed after.
| bindings | [x v] or [x v y w] | |
| body | the forms to run per element |
(let [v (f32vec-of (:: 1.5 float32) (:: 2.5 float32))] (f32vec-for [x v] (println x)))
Since: Phase N2
f32vec-for-index
(f32vec-for-index [bv & body])
run body once per element, with its index.
(for-index [i x v] body...) binds i (read-only) and x = v[i].
| bindings | [i x v] | |
| body | the forms to run per element |
(let [v (f32vec-of (:: 1.5 float32) (:: 2.5 float32))] (f32vec-for-index [i x v] (println i)))
Since: Phase N2
f32vec-fold
(f32vec-fold [bv body])
fold the elements of a F32Vec into an accumulator.
(fold [acc init x v] body): acc starts at init, and for each element x in order becomes the value of body (which sees acc and x). The result is the final acc. A float sum written this way is the strict left fold, so gcc keeps it scalar; f32vec-sum is the vectorized one.
| bindings | [acc init x v] | |
| body | the new accumulator |
The final accumulator.
(let [v (f32vec-of (:: 1.5 float32) (:: 2.5 float32))] (f32vec-fold [n 0 x v] (+ n 1))) ; => 2
Since: Phase N2
f32vec-build
(f32vec-build [n bv expr])
a fresh F32Vec of n elements, element i the value of expr.
(build n [i] expr), after Racket's for/flvector: i is bound to each index in turn, and expr runs outside any unsafe. The stores go through a view (f32vec-view), so the loop vectorizes where expr allows: at -O2 under clang, at -O3 (--release) under gcc.
| n | the length (evaluated once) | |
| i | the index name, in a vector | |
| expr | the element at i, of type float32 |
A fresh F32Vec, owned by the caller.
(let [v (f32vec-build 3 [i] (:: 1.5 float32))] (f32vec-len v)) ; => 3
Since: Phase N2
f32vec-map!
(f32vec-map! [dst bv expr])
store expr into dst[i] for every i, x bound to src[i].
(map! dst [x src] expr) stores into each slot of dst the value of expr with x bound to the element of src at the same index; (map! dst [x a y b] expr) walks two sources together. dst may be one of the sources: (map! v [x v] {x * 2}) maps in place. Every array has the same length, or a panic before the first element. The loop reads and stores through views (f32vec-view), so it vectorizes where expr allows: at -O2 under clang, at -O3 (--release) under gcc. expr runs outside any unsafe; an array argument that is not a name is evaluated once, and freed after the loop.
| dst | the array stored into (borrowed) | |
| bindings | [x src] or [x a y b] | |
| expr | the new element, of type float32 |
(let [v (f32vec-of (:: 1.5 float32) (:: 2.5 float32))] (f32vec-map! v [x v] x) (f32vec-ref v 1)) ; => (:: 2.5 float32)
Since: Phase N2
f32vec-same-len__
(f32vec-same-len__ [^borrow a : F32Vec ^borrow b : F32Vec])
internal: the common length of two arrays, or a panic.
I64Vec
(defopaque ^affine I64Vec : int)
a fixed-length, unboxed array of 64-bit integers (int).
Opaque handle to a struct { int64_t len; int64_t *data; void *raw; }. ^affine: freed at scope exit by its Drop instance. Use i64vec-new or i64vec-of to make one.
Since: Phase N0
I64View
(defopaque I64View : int)
a view of a I64Vec's elements: the data pointer, for a store loop.
Made by i64vec-view, read and written by i64vec-view-ref / i64vec-view-set!. Not owned and not affine: it frees nothing, and it is valid exactly as long as the array it came from (an array never resizes, so its data never moves).
Since: Phase N2
i64vec-new
(i64vec-new [n : int init : int])
allocate a I64Vec of n elements, every one init.
| n | element count (>= 0; a negative or absurd n panics) | |
| init | the value every slot starts with |
A fresh I64Vec, owned by the caller and freed at scope exit.
(let [v (i64vec-new 4 7)]
(i64vec-ref v 2)) ; => 7
Since: Phase N0
i64vec-of
(i64vec-of [& xs])
a I64Vec holding the given elements, in order.
| & xs -- zero or more elements of type int |
A fresh I64Vec of length (count xs).
(let [v (i64vec-of 1 2)]
(i64vec-len v)) ; => 2
Since: Phase N0
i64vec-free
(i64vec-free [v : I64Vec])
release a I64Vec now, consuming it.
Rarely needed: a let-bound I64Vec is freed at scope exit. Call it to release a large array before the scope ends.
| v | the array (consumed) |
(i64vec-free v)
Since: Phase N0
Drop[I64Vec]
(definstance Drop [I64Vec])
free the array when a bound I64Vec dies unmoved.
i64vec-len
(i64vec-len [^borrow v : I64Vec])
the element count of a I64Vec.
| v | the array (borrowed) |
The length fixed at construction.
(let [v (i64vec-new 8 7)]
(i64vec-len v)) ; => 8
Since: Phase N0
i64vec-ref
(i64vec-ref [^borrow v : I64Vec i : int])
the element at index i (bounds-checked).
| v | the array (borrowed) | |
| i | zero-based index; outside [0, len) panics |
The element, typed int.
(let [v (i64vec-of 1 2)]
(i64vec-ref v 1)) ; => 2
Since: Phase N0
i64vec-set!
(i64vec-set! [^borrow v : I64Vec i : int x : int])
store x at index i (bounds-checked).
| v | the array (borrowed) | |
| i | zero-based index; outside [0, len) panics | |
| x | the new element |
(i64vec-set! v 0 7)
Since: Phase N0
i64vec-copy
(i64vec-copy [^borrow v : I64Vec])
a fresh I64Vec with the same elements.
| v | the array (borrowed) |
An independent copy, owned by the caller.
(let [c (i64vec-copy v)] (i64vec-set! c 0 7)) ; v is unchanged
Since: Phase N0
i64vec->vec-go__
(i64vec->vec-go__ [^borrow v : I64Vec out : (Vec int) i : int n : int] : (Vec int))
internal: push v[i..n) onto out.
i64vec->vec
(i64vec->vec [^borrow v : I64Vec] : (Vec int))
copy a I64Vec into a fresh (Vec int).
| v | the array (borrowed) |
A growable Vec holding the same elements; the caller frees it.
(let [a (i64vec-of 1 2)
v (i64vec->vec a)]
(vec-len v)) ; => 2
Since: Phase N0
vec->i64vec-go__
(vec->i64vec-go__ [^borrow out : I64Vec ^borrow src : (Vec int) i : int n : int] : nil)
internal: store src[i..n) into out.
vec->i64vec
(vec->i64vec [^borrow src : (Vec int)] : I64Vec)
copy a (Vec int) into a fresh I64Vec.
| src | the Vec (borrowed) |
A I64Vec of the same length and elements, owned by the caller.
(let [v (vec-of 1 2)
a (vec->i64vec v)]
(i64vec-len a)) ; => 2
Since: Phase N0
i64vec-show-loop__
(i64vec-show-loop__ [^borrow v : I64Vec i : int n : int b : StringBuilder] : StringBuilder)
internal: push "e0 e1 ..." onto the builder.
i64vec-show
(i64vec-show [^borrow v : I64Vec] : String)
render a I64Vec as an owned "#i64vec[a b c]" String.
| v | the array (borrowed) |
An owned String; release it with string/release.
(let [v (i64vec-of 1 2)]
(i64vec-show v)) ; => String "#i64vec[1 2]"
Since: Phase N0
i64vec-fill!
(i64vec-fill! [^borrow v : I64Vec x : int])
store x in every slot.
| v | the array (borrowed) | |
| x | the value |
(i64vec-fill! v 1)
Since: Phase N1
i64vec-copy!
(i64vec-copy! [^borrow dst : I64Vec ^borrow src : I64Vec])
copy src's elements into dst (same length, or a panic).
| dst | the array written (borrowed) | |
| src | the array read (borrowed) |
(i64vec-copy! dst src)
Since: Phase N1
i64vec-scale!
(i64vec-scale! [^borrow v : I64Vec s : int])
multiply every element by s, in place.
Integer arithmetic wraps.
| v | the array (borrowed) | |
| s | the factor |
(i64vec-scale! v 2)
Since: Phase N1
i64vec-add!
(i64vec-add! [^borrow dst : I64Vec ^borrow src : I64Vec])
dst[i] += src[i] for every i (same length, or a panic).
Integer arithmetic wraps; dst and src may be the same array.
| dst | the array updated (borrowed) | |
| src | the array added (borrowed) |
(i64vec-add! y x)
Since: Phase N1
i64vec-axpy!
(i64vec-axpy! [^borrow dst : I64Vec s : int ^borrow src : I64Vec])
dst[i] += s * src[i] for every i (same length, or a panic).
Integer arithmetic wraps; the multiply and the add are not fused.
| dst | the array updated (borrowed) | |
| s | the factor | |
| src | the array scaled and added (borrowed) |
(i64vec-axpy! y 2 x)
Since: Phase N1
i64vec-sub!
(i64vec-sub! [^borrow dst : I64Vec ^borrow src : I64Vec])
dst[i] -= src[i] for every i (same length, or a panic).
Integer arithmetic wraps; dst and src may be the same array.
| dst | the array updated (borrowed) | |
| src | the array subtracted (borrowed) |
(i64vec-sub! y x)
Since: Phase N1
i64vec-mul!
(i64vec-mul! [^borrow dst : I64Vec ^borrow src : I64Vec])
dst[i] *= src[i] for every i (same length, or a panic).
Element-wise, not a dot product. Integer arithmetic wraps; dst and src may be the same array.
| dst | the array updated (borrowed) | |
| src | the array multiplied in (borrowed) |
(i64vec-mul! y x)
Since: Phase N1
i64vec-add-scalar!
(i64vec-add-scalar! [^borrow v : I64Vec s : int])
add s to every element, in place.
Integer arithmetic wraps.
| v | the array (borrowed) | |
| s | the value added |
(i64vec-add-scalar! v 2)
Since: Phase N1
i64vec-sum
(i64vec-sum [^borrow v : I64Vec])
the sum of the elements.
A float sum is computed in eight interleaved partial sums (lane k takes the elements i with i mod 8 = k), combined in one fixed tree: the same answer on every engine and machine, and vectorizable without -ffast-math. It can differ in the last bits from -sum-ordered, the left-to-right sum. An integer sum wraps modulo 2^64; a narrow element is widened to int first.
| v | the array (borrowed) |
A int.
(let [v (i64vec-of 1 2)] (i64vec-sum v))
Since: Phase N1
i64vec-dot
(i64vec-dot [^borrow a : I64Vec ^borrow b : I64Vec])
the dot product of two arrays (same length, or a panic).
Floats use the same eight-lane order as -sum. Integers wrap modulo 2^64; an int32 product is formed in 64 bits.
| a | the first array (borrowed) | |
| b | the second array (borrowed) |
A int.
(let [a (i64vec-of 1 2) b (i64vec-of 2 1)] (i64vec-dot a b))
Since: Phase N1
i64vec-min
(i64vec-min [^borrow v : I64Vec])
the smallest element (an empty array panics).
A NaN element makes the result NaN (the first NaN, by index). Which of two equal elements (0.0 and -0.0) is returned is fixed by the reduction order, the same on every engine.
| v | the array (borrowed) |
A int.
(let [v (i64vec-of 1 2)] (i64vec-min v)) ; => 1
Since: Phase N1
i64vec-max
(i64vec-max [^borrow v : I64Vec])
the largest element (an empty array panics).
A NaN element makes the result NaN (the first NaN, by index). Which of two equal elements (0.0 and -0.0) is returned is fixed by the reduction order, the same on every engine.
| v | the array (borrowed) |
A int.
(let [v (i64vec-of 1 2)] (i64vec-max v)) ; => 2
Since: Phase N1
i64vec-ref-unchecked
(i64vec-ref-unchecked [^borrow v : I64Vec i : int])
the element at index i, NOT bounds-checked.
#fx{Unsafe}: call it inside (unsafe ...). The loop macros (-for, -fold, ...) use it where they have already checked the index. Its body calls nothing, so a call to it carries no panic check, and a loop of such calls can vectorize.
| v | the array (borrowed) | |
| i | zero-based index; the caller proves 0 <= i < len |
A int.
(unsafe (i64vec-ref-unchecked v 0))
Since: Phase N2
i64vec-set-unchecked!
(i64vec-set-unchecked! [^borrow v : I64Vec i : int x : int])
store x at index i, NOT bounds-checked.
#fx{Unsafe}: call it inside (unsafe ...). Stores a number: no TUR_REGION_NOTE (see the module header).
| v | the array (borrowed) | |
| i | zero-based index; the caller proves 0 <= i < len | |
| x | the new element |
(unsafe (i64vec-set-unchecked! v 0 1))
Since: Phase N2
i64vec-view
(i64vec-view [^borrow v : I64Vec])
a view of v's elements, for a loop that stores into it.
#fx{Unsafe}: nothing ties the view to the array, so a view used after the array is freed reads freed memory. Why it exists: a store through the array (-set-unchecked!) re-reads the header's data pointer, which the store might have overwritten as far as the C compiler knows, so the loop does not vectorize; a view is that pointer, read once. -build and -map! store through one; reach for it directly only for a store loop they do not cover.
| v | the array (borrowed); the view is valid while v is |
A I64View.
(let [v (i64vec-of 1 2) w (unsafe (i64vec-view v))] (unsafe (i64vec-view-ref w 1))) ; => 2
Since: Phase N2
i64vec-view-ref
(i64vec-view-ref [w : I64View i : int])
the element at index i of a view, NOT bounds-checked.
#fx{Unsafe}: call it inside (unsafe ...). The interpreter checks the index anyway, as it does for -ref-unchecked.
| w | the view | |
| i | zero-based index; the caller proves 0 <= i < len |
A int.
(unsafe (i64vec-view-ref w 0))
Since: Phase N2
i64vec-view-set!
(i64vec-view-set! [w : I64View i : int x : int])
store x at index i of a view, NOT bounds-checked.
#fx{Unsafe}: call it inside (unsafe ...). Stores a number: no TUR_REGION_NOTE. The interpreter checks the index anyway.
| w | the view | |
| i | zero-based index; the caller proves 0 <= i < len | |
| x | the new element |
(unsafe (i64vec-view-set! w 0 1))
Since: Phase N2
i64vec-for
(i64vec-for [bv & body])
run body once per element of one or two I64Vecs.
(for [x v] body...) binds x to each element of v in order; (for [x v y w] body...) walks v and w together (same length, or a panic before the first pass). No index is in scope, so none can be wrong: the reads are i64vec-ref-unchecked, the body sees plain elements, and the loop carries no bounds check or panic test -- gcc can vectorize it at -O3. An array argument that is not a name is evaluated once, and freed after.
| bindings | [x v] or [x v y w] | |
| body | the forms to run per element |
(let [v (i64vec-of 1 2)] (i64vec-for [x v] (println x)))
Since: Phase N2
i64vec-for-index
(i64vec-for-index [bv & body])
run body once per element, with its index.
(for-index [i x v] body...) binds i (read-only) and x = v[i].
| bindings | [i x v] | |
| body | the forms to run per element |
(let [v (i64vec-of 1 2)] (i64vec-for-index [i x v] (println i)))
Since: Phase N2
i64vec-fold
(i64vec-fold [bv body])
fold the elements of a I64Vec into an accumulator.
(fold [acc init x v] body): acc starts at init, and for each element x in order becomes the value of body (which sees acc and x). The result is the final acc. A float sum written this way is the strict left fold, so gcc keeps it scalar; i64vec-sum is the vectorized one.
| bindings | [acc init x v] | |
| body | the new accumulator |
The final accumulator.
(let [v (i64vec-of 1 2)] (i64vec-fold [n 0 x v] (+ n 1))) ; => 2
Since: Phase N2
i64vec-build
(i64vec-build [n bv expr])
a fresh I64Vec of n elements, element i the value of expr.
(build n [i] expr), after Racket's for/flvector: i is bound to each index in turn, and expr runs outside any unsafe. The stores go through a view (i64vec-view), so the loop vectorizes where expr allows: at -O2 under clang, at -O3 (--release) under gcc.
| n | the length (evaluated once) | |
| i | the index name, in a vector | |
| expr | the element at i, of type int |
A fresh I64Vec, owned by the caller.
(let [v (i64vec-build 3 [i] 1)] (i64vec-len v)) ; => 3
Since: Phase N2
i64vec-map!
(i64vec-map! [dst bv expr])
store expr into dst[i] for every i, x bound to src[i].
(map! dst [x src] expr) stores into each slot of dst the value of expr with x bound to the element of src at the same index; (map! dst [x a y b] expr) walks two sources together. dst may be one of the sources: (map! v [x v] {x * 2}) maps in place. Every array has the same length, or a panic before the first element. The loop reads and stores through views (i64vec-view), so it vectorizes where expr allows: at -O2 under clang, at -O3 (--release) under gcc. expr runs outside any unsafe; an array argument that is not a name is evaluated once, and freed after the loop.
| dst | the array stored into (borrowed) | |
| bindings | [x src] or [x a y b] | |
| expr | the new element, of type int |
(let [v (i64vec-of 1 2)] (i64vec-map! v [x v] x) (i64vec-ref v 1)) ; => 2
Since: Phase N2
i64vec-same-len__
(i64vec-same-len__ [^borrow a : I64Vec ^borrow b : I64Vec])
internal: the common length of two arrays, or a panic.
I32Vec
(defopaque ^affine I32Vec : int)
a fixed-length, unboxed array of 32-bit integers (int32).
Opaque handle to a struct { int64_t len; int32_t *data; void *raw; }. ^affine: freed at scope exit by its Drop instance. Use i32vec-new or i32vec-of to make one.
Since: Phase N0
I32View
(defopaque I32View : int)
a view of a I32Vec's elements: the data pointer, for a store loop.
Made by i32vec-view, read and written by i32vec-view-ref / i32vec-view-set!. Not owned and not affine: it frees nothing, and it is valid exactly as long as the array it came from (an array never resizes, so its data never moves).
Since: Phase N2
i32vec-new
(i32vec-new [n : int init : int32])
allocate a I32Vec of n elements, every one init.
| n | element count (>= 0; a negative or absurd n panics) | |
| init | the value every slot starts with |
A fresh I32Vec, owned by the caller and freed at scope exit.
(let [v (i32vec-new 4 (as int32 7))]
(i32vec-ref v 2)) ; => (as int32 7)
Since: Phase N0
i32vec-of
(i32vec-of [& xs])
a I32Vec holding the given elements, in order.
| & xs -- zero or more elements of type int32 |
A fresh I32Vec of length (count xs).
(let [v (i32vec-of (as int32 1) (as int32 2))]
(i32vec-len v)) ; => 2
Since: Phase N0
i32vec-free
(i32vec-free [v : I32Vec])
release a I32Vec now, consuming it.
Rarely needed: a let-bound I32Vec is freed at scope exit. Call it to release a large array before the scope ends.
| v | the array (consumed) |
(i32vec-free v)
Since: Phase N0
Drop[I32Vec]
(definstance Drop [I32Vec])
free the array when a bound I32Vec dies unmoved.
i32vec-len
(i32vec-len [^borrow v : I32Vec])
the element count of a I32Vec.
| v | the array (borrowed) |
The length fixed at construction.
(let [v (i32vec-new 8 (as int32 7))]
(i32vec-len v)) ; => 8
Since: Phase N0
i32vec-ref
(i32vec-ref [^borrow v : I32Vec i : int])
the element at index i (bounds-checked).
| v | the array (borrowed) | |
| i | zero-based index; outside [0, len) panics |
The element, typed int32.
(let [v (i32vec-of (as int32 1) (as int32 2))]
(i32vec-ref v 1)) ; => (as int32 2)
Since: Phase N0
i32vec-set!
(i32vec-set! [^borrow v : I32Vec i : int x : int32])
store x at index i (bounds-checked).
| v | the array (borrowed) | |
| i | zero-based index; outside [0, len) panics | |
| x | the new element |
(i32vec-set! v 0 (as int32 7))
Since: Phase N0
i32vec-copy
(i32vec-copy [^borrow v : I32Vec])
a fresh I32Vec with the same elements.
| v | the array (borrowed) |
An independent copy, owned by the caller.
(let [c (i32vec-copy v)] (i32vec-set! c 0 (as int32 7))) ; v is unchanged
Since: Phase N0
i32vec->vec-go__
(i32vec->vec-go__ [^borrow v : I32Vec out : (Vec int32) i : int n : int] : (Vec int32))
internal: push v[i..n) onto out.
i32vec->vec
(i32vec->vec [^borrow v : I32Vec] : (Vec int32))
copy a I32Vec into a fresh (Vec int32).
| v | the array (borrowed) |
A growable Vec holding the same elements; the caller frees it.
(let [a (i32vec-of (as int32 1) (as int32 2))
v (i32vec->vec a)]
(vec-len v)) ; => 2
Since: Phase N0
vec->i32vec-go__
(vec->i32vec-go__ [^borrow out : I32Vec ^borrow src : (Vec int32) i : int n : int] : nil)
internal: store src[i..n) into out.
vec->i32vec
(vec->i32vec [^borrow src : (Vec int32)] : I32Vec)
copy a (Vec int32) into a fresh I32Vec.
| src | the Vec (borrowed) |
A I32Vec of the same length and elements, owned by the caller.
(let [v (vec-of (as int32 1) (as int32 2))
a (vec->i32vec v)]
(i32vec-len a)) ; => 2
Since: Phase N0
i32vec-show-loop__
(i32vec-show-loop__ [^borrow v : I32Vec i : int n : int b : StringBuilder] : StringBuilder)
internal: push "e0 e1 ..." onto the builder.
i32vec-show
(i32vec-show [^borrow v : I32Vec] : String)
render a I32Vec as an owned "#i32vec[a b c]" String.
| v | the array (borrowed) |
An owned String; release it with string/release.
(let [v (i32vec-of (as int32 1) (as int32 2))]
(i32vec-show v)) ; => String "#i32vec[1 2]"
Since: Phase N0
i32vec-fill!
(i32vec-fill! [^borrow v : I32Vec x : int32])
store x in every slot.
| v | the array (borrowed) | |
| x | the value |
(i32vec-fill! v (as int32 1))
Since: Phase N1
i32vec-copy!
(i32vec-copy! [^borrow dst : I32Vec ^borrow src : I32Vec])
copy src's elements into dst (same length, or a panic).
| dst | the array written (borrowed) | |
| src | the array read (borrowed) |
(i32vec-copy! dst src)
Since: Phase N1
i32vec-scale!
(i32vec-scale! [^borrow v : I32Vec s : int32])
multiply every element by s, in place.
Integer arithmetic wraps.
| v | the array (borrowed) | |
| s | the factor |
(i32vec-scale! v (as int32 2))
Since: Phase N1
i32vec-add!
(i32vec-add! [^borrow dst : I32Vec ^borrow src : I32Vec])
dst[i] += src[i] for every i (same length, or a panic).
Integer arithmetic wraps; dst and src may be the same array.
| dst | the array updated (borrowed) | |
| src | the array added (borrowed) |
(i32vec-add! y x)
Since: Phase N1
i32vec-axpy!
(i32vec-axpy! [^borrow dst : I32Vec s : int32 ^borrow src : I32Vec])
dst[i] += s * src[i] for every i (same length, or a panic).
Integer arithmetic wraps; the multiply and the add are not fused.
| dst | the array updated (borrowed) | |
| s | the factor | |
| src | the array scaled and added (borrowed) |
(i32vec-axpy! y (as int32 2) x)
Since: Phase N1
i32vec-sub!
(i32vec-sub! [^borrow dst : I32Vec ^borrow src : I32Vec])
dst[i] -= src[i] for every i (same length, or a panic).
Integer arithmetic wraps; dst and src may be the same array.
| dst | the array updated (borrowed) | |
| src | the array subtracted (borrowed) |
(i32vec-sub! y x)
Since: Phase N1
i32vec-mul!
(i32vec-mul! [^borrow dst : I32Vec ^borrow src : I32Vec])
dst[i] *= src[i] for every i (same length, or a panic).
Element-wise, not a dot product. Integer arithmetic wraps; dst and src may be the same array.
| dst | the array updated (borrowed) | |
| src | the array multiplied in (borrowed) |
(i32vec-mul! y x)
Since: Phase N1
i32vec-add-scalar!
(i32vec-add-scalar! [^borrow v : I32Vec s : int32])
add s to every element, in place.
Integer arithmetic wraps.
| v | the array (borrowed) | |
| s | the value added |
(i32vec-add-scalar! v (as int32 2))
Since: Phase N1
i32vec-sum
(i32vec-sum [^borrow v : I32Vec])
the sum of the elements.
A float sum is computed in eight interleaved partial sums (lane k takes the elements i with i mod 8 = k), combined in one fixed tree: the same answer on every engine and machine, and vectorizable without -ffast-math. It can differ in the last bits from -sum-ordered, the left-to-right sum. An integer sum wraps modulo 2^64; a narrow element is widened to int first.
| v | the array (borrowed) |
A int.
(let [v (i32vec-of (as int32 1) (as int32 2))] (i32vec-sum v))
Since: Phase N1
i32vec-dot
(i32vec-dot [^borrow a : I32Vec ^borrow b : I32Vec])
the dot product of two arrays (same length, or a panic).
Floats use the same eight-lane order as -sum. Integers wrap modulo 2^64; an int32 product is formed in 64 bits.
| a | the first array (borrowed) | |
| b | the second array (borrowed) |
A int.
(let [a (i32vec-of (as int32 1) (as int32 2)) b (i32vec-of (as int32 2) (as int32 1))] (i32vec-dot a b))
Since: Phase N1
i32vec-min
(i32vec-min [^borrow v : I32Vec])
the smallest element (an empty array panics).
A NaN element makes the result NaN (the first NaN, by index). Which of two equal elements (0.0 and -0.0) is returned is fixed by the reduction order, the same on every engine.
| v | the array (borrowed) |
A int32.
(let [v (i32vec-of (as int32 1) (as int32 2))] (i32vec-min v)) ; => (as int32 1)
Since: Phase N1
i32vec-max
(i32vec-max [^borrow v : I32Vec])
the largest element (an empty array panics).
A NaN element makes the result NaN (the first NaN, by index). Which of two equal elements (0.0 and -0.0) is returned is fixed by the reduction order, the same on every engine.
| v | the array (borrowed) |
A int32.
(let [v (i32vec-of (as int32 1) (as int32 2))] (i32vec-max v)) ; => (as int32 2)
Since: Phase N1
i32vec-ref-unchecked
(i32vec-ref-unchecked [^borrow v : I32Vec i : int])
the element at index i, NOT bounds-checked.
#fx{Unsafe}: call it inside (unsafe ...). The loop macros (-for, -fold, ...) use it where they have already checked the index. Its body calls nothing, so a call to it carries no panic check, and a loop of such calls can vectorize.
| v | the array (borrowed) | |
| i | zero-based index; the caller proves 0 <= i < len |
A int32.
(unsafe (i32vec-ref-unchecked v 0))
Since: Phase N2
i32vec-set-unchecked!
(i32vec-set-unchecked! [^borrow v : I32Vec i : int x : int32])
store x at index i, NOT bounds-checked.
#fx{Unsafe}: call it inside (unsafe ...). Stores a number: no TUR_REGION_NOTE (see the module header).
| v | the array (borrowed) | |
| i | zero-based index; the caller proves 0 <= i < len | |
| x | the new element |
(unsafe (i32vec-set-unchecked! v 0 (as int32 1)))
Since: Phase N2
i32vec-view
(i32vec-view [^borrow v : I32Vec])
a view of v's elements, for a loop that stores into it.
#fx{Unsafe}: nothing ties the view to the array, so a view used after the array is freed reads freed memory. Why it exists: a store through the array (-set-unchecked!) re-reads the header's data pointer, which the store might have overwritten as far as the C compiler knows, so the loop does not vectorize; a view is that pointer, read once. -build and -map! store through one; reach for it directly only for a store loop they do not cover.
| v | the array (borrowed); the view is valid while v is |
A I32View.
(let [v (i32vec-of (as int32 1) (as int32 2)) w (unsafe (i32vec-view v))] (unsafe (i32vec-view-ref w 1))) ; => (as int32 2)
Since: Phase N2
i32vec-view-ref
(i32vec-view-ref [w : I32View i : int])
the element at index i of a view, NOT bounds-checked.
#fx{Unsafe}: call it inside (unsafe ...). The interpreter checks the index anyway, as it does for -ref-unchecked.
| w | the view | |
| i | zero-based index; the caller proves 0 <= i < len |
A int32.
(unsafe (i32vec-view-ref w 0))
Since: Phase N2
i32vec-view-set!
(i32vec-view-set! [w : I32View i : int x : int32])
store x at index i of a view, NOT bounds-checked.
#fx{Unsafe}: call it inside (unsafe ...). Stores a number: no TUR_REGION_NOTE. The interpreter checks the index anyway.
| w | the view | |
| i | zero-based index; the caller proves 0 <= i < len | |
| x | the new element |
(unsafe (i32vec-view-set! w 0 (as int32 1)))
Since: Phase N2
i32vec-for
(i32vec-for [bv & body])
run body once per element of one or two I32Vecs.
(for [x v] body...) binds x to each element of v in order; (for [x v y w] body...) walks v and w together (same length, or a panic before the first pass). No index is in scope, so none can be wrong: the reads are i32vec-ref-unchecked, the body sees plain elements, and the loop carries no bounds check or panic test -- gcc can vectorize it at -O3. An array argument that is not a name is evaluated once, and freed after.
| bindings | [x v] or [x v y w] | |
| body | the forms to run per element |
(let [v (i32vec-of (as int32 1) (as int32 2))] (i32vec-for [x v] (println x)))
Since: Phase N2
i32vec-for-index
(i32vec-for-index [bv & body])
run body once per element, with its index.
(for-index [i x v] body...) binds i (read-only) and x = v[i].
| bindings | [i x v] | |
| body | the forms to run per element |
(let [v (i32vec-of (as int32 1) (as int32 2))] (i32vec-for-index [i x v] (println i)))
Since: Phase N2
i32vec-fold
(i32vec-fold [bv body])
fold the elements of a I32Vec into an accumulator.
(fold [acc init x v] body): acc starts at init, and for each element x in order becomes the value of body (which sees acc and x). The result is the final acc. A float sum written this way is the strict left fold, so gcc keeps it scalar; i32vec-sum is the vectorized one.
| bindings | [acc init x v] | |
| body | the new accumulator |
The final accumulator.
(let [v (i32vec-of (as int32 1) (as int32 2))] (i32vec-fold [n 0 x v] (+ n 1))) ; => 2
Since: Phase N2
i32vec-build
(i32vec-build [n bv expr])
a fresh I32Vec of n elements, element i the value of expr.
(build n [i] expr), after Racket's for/flvector: i is bound to each index in turn, and expr runs outside any unsafe. The stores go through a view (i32vec-view), so the loop vectorizes where expr allows: at -O2 under clang, at -O3 (--release) under gcc.
| n | the length (evaluated once) | |
| i | the index name, in a vector | |
| expr | the element at i, of type int32 |
A fresh I32Vec, owned by the caller.
(let [v (i32vec-build 3 [i] (as int32 1))] (i32vec-len v)) ; => 3
Since: Phase N2
i32vec-map!
(i32vec-map! [dst bv expr])
store expr into dst[i] for every i, x bound to src[i].
(map! dst [x src] expr) stores into each slot of dst the value of expr with x bound to the element of src at the same index; (map! dst [x a y b] expr) walks two sources together. dst may be one of the sources: (map! v [x v] {x * 2}) maps in place. Every array has the same length, or a panic before the first element. The loop reads and stores through views (i32vec-view), so it vectorizes where expr allows: at -O2 under clang, at -O3 (--release) under gcc. expr runs outside any unsafe; an array argument that is not a name is evaluated once, and freed after the loop.
| dst | the array stored into (borrowed) | |
| bindings | [x src] or [x a y b] | |
| expr | the new element, of type int32 |
(let [v (i32vec-of (as int32 1) (as int32 2))] (i32vec-map! v [x v] x) (i32vec-ref v 1)) ; => (as int32 2)
Since: Phase N2
i32vec-same-len__
(i32vec-same-len__ [^borrow a : I32Vec ^borrow b : I32Vec])
internal: the common length of two arrays, or a panic.
U8Vec
(defopaque ^affine U8Vec : int)
a fixed-length, unboxed array of bytes (uint8).
Opaque handle to a struct { int64_t len; uint8_t *data; void *raw; }. ^affine: freed at scope exit by its Drop instance. Use u8vec-new or u8vec-of to make one.
Since: Phase N0
U8View
(defopaque U8View : int)
a view of a U8Vec's elements: the data pointer, for a store loop.
Made by u8vec-view, read and written by u8vec-view-ref / u8vec-view-set!. Not owned and not affine: it frees nothing, and it is valid exactly as long as the array it came from (an array never resizes, so its data never moves).
Since: Phase N2
u8vec-new
(u8vec-new [n : int init : uint8])
allocate a U8Vec of n elements, every one init.
| n | element count (>= 0; a negative or absurd n panics) | |
| init | the value every slot starts with |
A fresh U8Vec, owned by the caller and freed at scope exit.
(let [v (u8vec-new 4 (as uint8 200))]
(u8vec-ref v 2)) ; => (as uint8 200)
Since: Phase N0
u8vec-of
(u8vec-of [& xs])
a U8Vec holding the given elements, in order.
| & xs -- zero or more elements of type uint8 |
A fresh U8Vec of length (count xs).
(let [v (u8vec-of (as uint8 1) (as uint8 2))]
(u8vec-len v)) ; => 2
Since: Phase N0
u8vec-free
(u8vec-free [v : U8Vec])
release a U8Vec now, consuming it.
Rarely needed: a let-bound U8Vec is freed at scope exit. Call it to release a large array before the scope ends.
| v | the array (consumed) |
(u8vec-free v)
Since: Phase N0
Drop[U8Vec]
(definstance Drop [U8Vec])
free the array when a bound U8Vec dies unmoved.
u8vec-len
(u8vec-len [^borrow v : U8Vec])
the element count of a U8Vec.
| v | the array (borrowed) |
The length fixed at construction.
(let [v (u8vec-new 8 (as uint8 200))]
(u8vec-len v)) ; => 8
Since: Phase N0
u8vec-ref
(u8vec-ref [^borrow v : U8Vec i : int])
the element at index i (bounds-checked).
| v | the array (borrowed) | |
| i | zero-based index; outside [0, len) panics |
The element, typed uint8.
(let [v (u8vec-of (as uint8 1) (as uint8 2))]
(u8vec-ref v 1)) ; => (as uint8 2)
Since: Phase N0
u8vec-set!
(u8vec-set! [^borrow v : U8Vec i : int x : uint8])
store x at index i (bounds-checked).
| v | the array (borrowed) | |
| i | zero-based index; outside [0, len) panics | |
| x | the new element |
(u8vec-set! v 0 (as uint8 200))
Since: Phase N0
u8vec-copy
(u8vec-copy [^borrow v : U8Vec])
a fresh U8Vec with the same elements.
| v | the array (borrowed) |
An independent copy, owned by the caller.
(let [c (u8vec-copy v)] (u8vec-set! c 0 (as uint8 200))) ; v is unchanged
Since: Phase N0
u8vec->vec-go__
(u8vec->vec-go__ [^borrow v : U8Vec out : (Vec uint8) i : int n : int] : (Vec uint8))
internal: push v[i..n) onto out.
u8vec->vec
(u8vec->vec [^borrow v : U8Vec] : (Vec uint8))
copy a U8Vec into a fresh (Vec uint8).
| v | the array (borrowed) |
A growable Vec holding the same elements; the caller frees it.
(let [a (u8vec-of (as uint8 1) (as uint8 2))
v (u8vec->vec a)]
(vec-len v)) ; => 2
Since: Phase N0
vec->u8vec-go__
(vec->u8vec-go__ [^borrow out : U8Vec ^borrow src : (Vec uint8) i : int n : int] : nil)
internal: store src[i..n) into out.
vec->u8vec
(vec->u8vec [^borrow src : (Vec uint8)] : U8Vec)
copy a (Vec uint8) into a fresh U8Vec.
| src | the Vec (borrowed) |
A U8Vec of the same length and elements, owned by the caller.
(let [v (vec-of (as uint8 1) (as uint8 2))
a (vec->u8vec v)]
(u8vec-len a)) ; => 2
Since: Phase N0
u8vec-show-loop__
(u8vec-show-loop__ [^borrow v : U8Vec i : int n : int b : StringBuilder] : StringBuilder)
internal: push "e0 e1 ..." onto the builder.
u8vec-show
(u8vec-show [^borrow v : U8Vec] : String)
render a U8Vec as an owned "#u8vec[a b c]" String.
| v | the array (borrowed) |
An owned String; release it with string/release.
(let [v (u8vec-of (as uint8 1) (as uint8 2))]
(u8vec-show v)) ; => String "#u8vec[1 2]"
Since: Phase N0
u8vec-fill!
(u8vec-fill! [^borrow v : U8Vec x : uint8])
store x in every slot.
| v | the array (borrowed) | |
| x | the value |
(u8vec-fill! v (as uint8 1))
Since: Phase N1
u8vec-copy!
(u8vec-copy! [^borrow dst : U8Vec ^borrow src : U8Vec])
copy src's elements into dst (same length, or a panic).
| dst | the array written (borrowed) | |
| src | the array read (borrowed) |
(u8vec-copy! dst src)
Since: Phase N1
u8vec-add!
(u8vec-add! [^borrow dst : U8Vec ^borrow src : U8Vec])
dst[i] += src[i] for every i (same length, or a panic).
Integer arithmetic wraps; dst and src may be the same array.
| dst | the array updated (borrowed) | |
| src | the array added (borrowed) |
(u8vec-add! y x)
Since: Phase N1
u8vec-sub!
(u8vec-sub! [^borrow dst : U8Vec ^borrow src : U8Vec])
dst[i] -= src[i] for every i (same length, or a panic).
Integer arithmetic wraps; dst and src may be the same array.
| dst | the array updated (borrowed) | |
| src | the array subtracted (borrowed) |
(u8vec-sub! y x)
Since: Phase N1
u8vec-add-scalar!
(u8vec-add-scalar! [^borrow v : U8Vec s : uint8])
add s to every element, in place.
Integer arithmetic wraps.
| v | the array (borrowed) | |
| s | the value added |
(u8vec-add-scalar! v (as uint8 2))
Since: Phase N1
u8vec-sum
(u8vec-sum [^borrow v : U8Vec])
the sum of the elements.
A float sum is computed in eight interleaved partial sums (lane k takes the elements i with i mod 8 = k), combined in one fixed tree: the same answer on every engine and machine, and vectorizable without -ffast-math. It can differ in the last bits from -sum-ordered, the left-to-right sum. An integer sum wraps modulo 2^64; a narrow element is widened to int first.
| v | the array (borrowed) |
A int.
(let [v (u8vec-of (as uint8 1) (as uint8 2))] (u8vec-sum v))
Since: Phase N1
u8vec-min
(u8vec-min [^borrow v : U8Vec])
the smallest element (an empty array panics).
A NaN element makes the result NaN (the first NaN, by index). Which of two equal elements (0.0 and -0.0) is returned is fixed by the reduction order, the same on every engine.
| v | the array (borrowed) |
A uint8.
(let [v (u8vec-of (as uint8 1) (as uint8 2))] (u8vec-min v)) ; => (as uint8 1)
Since: Phase N1
u8vec-max
(u8vec-max [^borrow v : U8Vec])
the largest element (an empty array panics).
A NaN element makes the result NaN (the first NaN, by index). Which of two equal elements (0.0 and -0.0) is returned is fixed by the reduction order, the same on every engine.
| v | the array (borrowed) |
A uint8.
(let [v (u8vec-of (as uint8 1) (as uint8 2))] (u8vec-max v)) ; => (as uint8 2)
Since: Phase N1
u8vec-ref-unchecked
(u8vec-ref-unchecked [^borrow v : U8Vec i : int])
the element at index i, NOT bounds-checked.
#fx{Unsafe}: call it inside (unsafe ...). The loop macros (-for, -fold, ...) use it where they have already checked the index. Its body calls nothing, so a call to it carries no panic check, and a loop of such calls can vectorize.
| v | the array (borrowed) | |
| i | zero-based index; the caller proves 0 <= i < len |
A uint8.
(unsafe (u8vec-ref-unchecked v 0))
Since: Phase N2
u8vec-set-unchecked!
(u8vec-set-unchecked! [^borrow v : U8Vec i : int x : uint8])
store x at index i, NOT bounds-checked.
#fx{Unsafe}: call it inside (unsafe ...). Stores a number: no TUR_REGION_NOTE (see the module header).
| v | the array (borrowed) | |
| i | zero-based index; the caller proves 0 <= i < len | |
| x | the new element |
(unsafe (u8vec-set-unchecked! v 0 (as uint8 1)))
Since: Phase N2
u8vec-view
(u8vec-view [^borrow v : U8Vec])
a view of v's elements, for a loop that stores into it.
#fx{Unsafe}: nothing ties the view to the array, so a view used after the array is freed reads freed memory. Why it exists: a store through the array (-set-unchecked!) re-reads the header's data pointer, which the store might have overwritten as far as the C compiler knows, so the loop does not vectorize; a view is that pointer, read once. -build and -map! store through one; reach for it directly only for a store loop they do not cover.
| v | the array (borrowed); the view is valid while v is |
A U8View.
(let [v (u8vec-of (as uint8 1) (as uint8 2)) w (unsafe (u8vec-view v))] (unsafe (u8vec-view-ref w 1))) ; => (as uint8 2)
Since: Phase N2
u8vec-view-ref
(u8vec-view-ref [w : U8View i : int])
the element at index i of a view, NOT bounds-checked.
#fx{Unsafe}: call it inside (unsafe ...). The interpreter checks the index anyway, as it does for -ref-unchecked.
| w | the view | |
| i | zero-based index; the caller proves 0 <= i < len |
A uint8.
(unsafe (u8vec-view-ref w 0))
Since: Phase N2
u8vec-view-set!
(u8vec-view-set! [w : U8View i : int x : uint8])
store x at index i of a view, NOT bounds-checked.
#fx{Unsafe}: call it inside (unsafe ...). Stores a number: no TUR_REGION_NOTE. The interpreter checks the index anyway.
| w | the view | |
| i | zero-based index; the caller proves 0 <= i < len | |
| x | the new element |
(unsafe (u8vec-view-set! w 0 (as uint8 1)))
Since: Phase N2
u8vec-for
(u8vec-for [bv & body])
run body once per element of one or two U8Vecs.
(for [x v] body...) binds x to each element of v in order; (for [x v y w] body...) walks v and w together (same length, or a panic before the first pass). No index is in scope, so none can be wrong: the reads are u8vec-ref-unchecked, the body sees plain elements, and the loop carries no bounds check or panic test -- gcc can vectorize it at -O3. An array argument that is not a name is evaluated once, and freed after.
| bindings | [x v] or [x v y w] | |
| body | the forms to run per element |
(let [v (u8vec-of (as uint8 1) (as uint8 2))] (u8vec-for [x v] (println x)))
Since: Phase N2
u8vec-for-index
(u8vec-for-index [bv & body])
run body once per element, with its index.
(for-index [i x v] body...) binds i (read-only) and x = v[i].
| bindings | [i x v] | |
| body | the forms to run per element |
(let [v (u8vec-of (as uint8 1) (as uint8 2))] (u8vec-for-index [i x v] (println i)))
Since: Phase N2
u8vec-fold
(u8vec-fold [bv body])
fold the elements of a U8Vec into an accumulator.
(fold [acc init x v] body): acc starts at init, and for each element x in order becomes the value of body (which sees acc and x). The result is the final acc. A float sum written this way is the strict left fold, so gcc keeps it scalar; u8vec-sum is the vectorized one.
| bindings | [acc init x v] | |
| body | the new accumulator |
The final accumulator.
(let [v (u8vec-of (as uint8 1) (as uint8 2))] (u8vec-fold [n 0 x v] (+ n 1))) ; => 2
Since: Phase N2
u8vec-build
(u8vec-build [n bv expr])
a fresh U8Vec of n elements, element i the value of expr.
(build n [i] expr), after Racket's for/flvector: i is bound to each index in turn, and expr runs outside any unsafe. The stores go through a view (u8vec-view), so the loop vectorizes where expr allows: at -O2 under clang, at -O3 (--release) under gcc.
| n | the length (evaluated once) | |
| i | the index name, in a vector | |
| expr | the element at i, of type uint8 |
A fresh U8Vec, owned by the caller.
(let [v (u8vec-build 3 [i] (as uint8 1))] (u8vec-len v)) ; => 3
Since: Phase N2
u8vec-map!
(u8vec-map! [dst bv expr])
store expr into dst[i] for every i, x bound to src[i].
(map! dst [x src] expr) stores into each slot of dst the value of expr with x bound to the element of src at the same index; (map! dst [x a y b] expr) walks two sources together. dst may be one of the sources: (map! v [x v] {x * 2}) maps in place. Every array has the same length, or a panic before the first element. The loop reads and stores through views (u8vec-view), so it vectorizes where expr allows: at -O2 under clang, at -O3 (--release) under gcc. expr runs outside any unsafe; an array argument that is not a name is evaluated once, and freed after the loop.
| dst | the array stored into (borrowed) | |
| bindings | [x src] or [x a y b] | |
| expr | the new element, of type uint8 |
(let [v (u8vec-of (as uint8 1) (as uint8 2))] (u8vec-map! v [x v] x) (u8vec-ref v 1)) ; => (as uint8 2)
Since: Phase N2
u8vec-same-len__
(u8vec-same-len__ [^borrow a : U8Vec ^borrow b : U8Vec])
internal: the common length of two arrays, or a panic.
NumArray
(defclass NumArray [v])
the operations every typed numeric array has, as one class.
Write a numeric algorithm once, over [V] [(NumArray V)], and it runs on F64Vec, F32Vec, I64Vec, I32Vec and U8Vec, each call going straight to that type's own function (one specialization per type, no runtime dispatch). The element type is the associated type Elem -- (Elem V) in a signature -- so a method returns the honest float32 or uint8, not a carrier word. Sum is what na-sum returns (int for the integer types, the element type for the floats) and View the type of na-view's result. Arithmetic on an element needs (Num (Elem V)), and a conversion or a constant (NumCast (Elem V)) (stdlib/typeclass.tur). Constructors stay per type (f64vec-new ...): a length alone cannot pick an instance. The loops na-for, na-fold and na-map! below work inside such a generic.
| v | the array type |
(type Elem : Type)
(type Sum : Type)
(type View : Type)
(na-len [^borrow a : v] : int)-
the element count
(na-ref [^borrow a : v i : int] : Elem)-
the element at i (bounds-checked)
(na-set! [^borrow a : v i : int x : Elem] : nil)-
store x at i (bounds-checked)
(na-ref-unchecked [^borrow a : v i : int] : Elem)-
the element at i, NOT bounds-checked (
#fx{Unsafe}) (na-set-unchecked! [^borrow a : v i : int x : Elem] : nil)-
store x at i, NOT bounds-checked (
#fx{Unsafe}) (na-view [^borrow a : v] : View)-
a view of a's elements (see
-view) (#fx{Unsafe}) (na-view-ref [^borrow a : v w : View i : int] : Elem)-
the element at i of a's view w, NOT bounds-checked (
#fx{Unsafe}) (na-view-set! [^borrow a : v w : View i : int x : Elem] : nil)-
store x at i of a's view w, NOT bounds-checked (
#fx{Unsafe}) (na-fill! [^borrow a : v x : Elem] : nil)-
store x in every slot
(na-copy! [^borrow dst : v ^borrow src : v] : nil)-
copy src into dst (same length)
(na-add! [^borrow dst : v ^borrow src : v] : nil)-
dst[i] += src[i] (same length)
(na-sub! [^borrow dst : v ^borrow src : v] : nil)-
dst[i] -= src[i] (same length)
(na-add-scalar! [^borrow a : v s : Elem] : nil)-
add s to every element
(na-sum [^borrow a : v] : Sum)-
the sum (eight-lane order for floats; integers wrap)
(na-min [^borrow a : v] : Elem)-
the smallest element (an empty array panics)
(na-max [^borrow a : v] : Elem)-
the largest element (an empty array panics)
(na-copy [^borrow a : v] : v)-
a fresh copy, owned by the caller -- free it with
na-free(a generic local is not dropped yet) (na-free [a : v] : nil)-
release an array now, consuming it
(na-show [^borrow a : v] : String)-
an owned "#P[a b c]" String
(defn mean [V] [(NumArray V) (NumCast (Sum V))] [^borrow a : V] : float
{(to-float (na-sum a)) / (as float (na-len a))})
(mean (f64vec-of 1.5 2.5)) ; => 2.0
Since: Phase N2
NumArray[F64Vec]
(definstance NumArray [F64Vec])
delegates to the f64vec-* functions.
NumArray[F32Vec]
(definstance NumArray [F32Vec])
delegates to the f32vec-* functions.
NumArray[I64Vec]
(definstance NumArray [I64Vec])
delegates to the i64vec-* functions.
NumArray[I32Vec]
(definstance NumArray [I32Vec])
delegates to the i32vec-* functions.
NumArray[U8Vec]
(definstance NumArray [U8Vec])
delegates to the u8vec-* functions.
na-same-len__
(na-same-len__ [V])
internal: the common length of two arrays, or a panic.
na-for
(na-for [bv & body])
run body once per element of one or two arrays, in a generic.
-for over the class: (na-for [x v] body...), or (na-for [x v y w] body...) to walk two together (same length, or a panic first). The enclosing function needs (NumArray V); x is an (Elem V).
| bindings | [x v] or [x v y w] | |
| body | the forms to run per element |
(defn count-pos [V] [(NumArray V) (Ord (Elem V)) (NumCast (Elem V))]
[^borrow a : V] : int
(let [^mut n 0]
(na-for [x a] (when (> x (:: (from-int 0) (Elem V))) (set! n {n + 1})))
n))
Since: Phase N2
na-fold
(na-fold [bv body])
fold the elements of an array into an accumulator, in a generic.
-fold over the class: (na-fold [acc init x v] body).
| bindings | [acc init x v] | |
| body | the new accumulator |
The final accumulator.
(na-fold [n 0 x a] {n + 1}) ; => the length, in a generic over a
Since: Phase N2
na-map!
(na-map! [dst bv expr])
store expr into dst[i] for every i, in a generic.
-map! over the class: (na-map! dst [x src] expr) or (na-map! dst [x a y b] expr); dst may be a source. Stores through views.
| dst | the array stored into (borrowed) | |
| bindings | [x src] or [x a y b] | |
| expr | the new element, an (Elem V) |
(defn double! [V] [(NumArray V) (Num (Elem V))] [^borrow a : V] : nil
(na-map! a [x a] {x + x}))
Since: Phase N2