Expand description
Dummy doc-only module: renders src/guide.md (the one-file API overview,
also linked from the README) into the rustdoc output.
§Functionality guide — one-file API overview
This document is a compact but complete map of everything lightweight-simd
offers, written for two audiences: API users who want the big picture without
reading every module, and AI coding agents that need to know what this crate
can and cannot do before writing code against it. The rustdoc API pages remain
the source of truth for exact signatures; this file is the orientation layer.
- Design stance: portable fixed-lane vector types whose every operation is a
plain loop that the compiler (LLVM) auto-vectorizes. No intrinsics, no
inline assembly, no runtime CPU-feature dispatch, no fallback code paths.
SIMD-level performance comes from building with
-C target-cpu=native(or a fixed level such asx86-64-v3); correctness never depends on it. - Everything is stable Rust; dependencies are
duplicate(codegen), andnum-traits(element trait bounds).
use lightweight_simd::{f64x4, Simd};
let a = f64x4::from([1.0, 2.0, 3.0, 4.0]);
let b = a * 2.0 + f64x4::splat(1.0); // vector-scalar and vector-vector ops
assert_eq!(b.to_array(), [3.0, 5.0, 7.0, 9.0]);
assert_eq!(b.reduce_sum(), 24.0); // horizontal sum
let m = a.simd_gt(f64x4::splat(2.0)); // lane-wise compare -> mask
assert_eq!(a.mask_select(m, f64x4::zero()).to_array(), [0.0, 0.0, 3.0, 4.0]);§The six vector types
There is one generic core type and five aligned siblings; they share a single implementation, so every operation listed in this guide exists on all six types (except the handful of mask-only helpers, noted below):
| Type | Alignment | Role |
|---|---|---|
Simd<T, N> | natural (element’s) | the core type; public field .0 of type [T; N] |
Aligned4<T, N> | 4 B | enforced-alignment siblings, |
Aligned8<T, N> | 8 B | structurally identical |
Aligned16<T, N> | 16 B | (#[repr(align(n))] over |
Aligned32<T, N> | 32 B | a public [T; N]) |
Aligned64<T, N> | 64 B |
- The field
.0is public on every type: zero-friction interop with plain array code, and.0/Fromconversions move between plain and aligned forms losslessly (Aligned64::to_simdtoo). - Masks —
Simd<bool, N>— are never aligned. Comparisons on any vector type return plainSimd<bool, N>; masks are register-resident temporaries.
§Concrete type aliases
Defined in the crate root (alias module). The aligned
backing type is chosen as byte size rounded up to a power of two, capped at
64 bytes (so e.g. f64x16 is 128 B of data with 64 B alignment):
f64:f64x2f64x4f64x8f64x16f32:f32x2f32x4f32x8f32x16i64:i64x2i64x4i64x8i64x16i32:i32x4i32x8i32x16i16:i16x4i16x8i16x16i16x32i8:i8x8i8x16i8x32i8x64- masks:
mask4mask8mask16mask32(=Simd<bool, N>; otherSimd<bool, N>widths work directly too)
Feature-gated aliases (off by default):
- feature
complex(depnum-complex):c64x2c64x4c64x8c32x4c32x8c32x16overnum_complex::Complex<f64/f32>. - feature
half(dephalf):f16x2…f16x64andbf16x2…bf16x64. Note:f16/bf16arithmetic goes through thehalfcrate’s scalar impls and stays scalar in the emitted code on x86 (documented in the asm checker’s informational notes).
The lane count N is an arbitrary const generic on Simd/aligned types —
powers of two that fill whole registers are the sweet spot, not a requirement.
Non-builtin element types work wherever their trait bounds fit (bool
supports splat/bitwise/compare/select; Complex<T> supports arithmetic,
compare, and mul_add behind the complex feature; half::f16 supports the
float functions).
§Operation surface
All methods below are inherent methods, #[inline(always)], identical across
the six types. Simd is written below for brevity; read it as “any of the six”.
§Construction, access, conversion
Simd::from_fn(f) — laneiisf(i).Simd::splat(val) — all lanesval(const fn).Simd::fill(&mut self, val) — overwrite all lanes.Simd::zero() /Simd::one() — vianum_traits.From<[T; N]>on every type; plain↔aligned viaFrom,.0, orto_simd. NoFrom<&[T]>/Vecconversions — usefrom_slice.Simd::as_array/as_array_mut/Simd::to_array— borrow/move the backing array (as_arrayisconst fn). There is no iterator over lanes; use.as_array().iter().Index<usize>/IndexMut<usize>— per-lane access.- Derived traits on every type:
Clone,Copy,Debug,PartialEq,Eq,Hash,Default;num_traits::Zero/Onewhere the element admits them.PartialOrd/Ordare deliberately not implemented — comparisons go through thesimd_*methods below so they always produce masks.
§Arithmetic
Add/Sub/Mul/Divas vectoropvector and vectoropscalar; plus the four*Assignforms in both shapes. RequiresT: num_traits::Num(NumAssignOpsfor the assign forms).Neg(element’sNeg).- No saturating/wrapping/checked variants, no widening/narrowing, no
Rem.
§Bitwise and shifts
BitAnd/BitOr/BitXor(+ assign forms) andNot, by the element’s owncore::opsimpls. OnSimd<bool, N>these are the lane-wise logical operators — combine masks with&,|,^,!.Shl<usize>/Shr<usize>: shift every lane by one scalarusize. There is no lane-wise/varying shift.
§Multiply-add — the one target-dependent area
Simd::mul_add(b, c) → self * b + c, and
Simd::fma_from(b, c) → *self = b * c + *self
(accumulator is the receiver — note the operand-order difference; it matches
self += b * c for micro-kernels). Evaluation follows the build target
(policy and measurements: docs/adr/0005-mul-add-fallback-policy.md):
- Hardware-FMA targets (x86 with
-C target-cpu≥x86-64-v3, includingnative; all aarch64): the element’snum_traits::MulAdd— one rounding per lane, one FMA instruction per register. - Targets without (generic x86-64,
x86-64-v2, unknown arches): separated multiply + add, two roundings — deliberately never the slow software libmfma(~4.6× slower measured). Fuses back to a hardware FMA under-C llvm-args=-fp-contract=fast. - Feature
use_libm_fmapins case 1 everywhere, accepting slow libm calls where hardware is absent — the only way to get bit-identicalmul_addresults across build targets.
Floats always have mul_add available (case 1 or 2 by target). Agents:
never assume a.mul_add(b, c) equals a * b + c bit-for-bit; it does only on
FMA targets (single vs double rounding).
§Float functions (T: num_traits::Float)
- Unary, lane-wise:
abs,sqrt,recip,floor,ceil,round,trunc,fract. - Binary:
min,max(NaN handling follows the scalarFloatmethods),copysign. round_ties_evenexists only forf32/f64vectors (not reachable generically throughnum_traits::Float).- No transcendental functions (exp/log/sin/…): use the escape hatch
Simd::map, e.g.v.map(f64::exp)— it vectorizes the same way everything else does.
§Comparisons and masks
simd_lt/simd_le/simd_gt/simd_ge(needPartialOrd) andsimd_eq/simd_ne(needPartialEq) returnSimd<bool, N>masks.- Mask-side (
Simd<bool, N>only):select(if_true, if_false)— lane-wise pick;any_true()/all_true(). - Value-side (any of the six types):
mask_select(mask, other)— keepselfwheremaskis true,otherwhere false.
§Reductions
Simd::reduce_sum/Simd::reduce_product— all lanes, accumulated in plain lane order (sequential fold, not a pairwise/tree reduction; float results depend on that order).reduce_sum_n(n)/reduce_product_n(n)— firstnlanes; panic ifn > N. Useful for masked-out tails.- No horizontal min/max/and/or reductions.
§Slice load/store
Simd::from_slice(src) — firstNelements; panics ifsrc.len() < N.from_slice_pad(src)— same, zero-filling lanes pastsrc.len()(tail-safe load).store_slice(&self, dst)— writesNlanes; panics ifdst.len() < N.store_slice_partial(&self, dst)— writesmin(dst.len(), N)lanes (tail-safe store).
§Unsafe constructor
Simd::uninit() —MaybeUninit-backed scratch vector; caller must fully initialize every lane before reading; element must admit arbitrary bit patterns (floats/integers yes, types with niches no). Intended for hot loops that write-then-read, e.g. accumulate-then-reduce.
§Cargo features
| Feature | Default | Effect |
|---|---|---|
complex | off | num-complex dep + c64x*/c32x* aliases |
half | off | half dep + f16x*/bf16x* aliases |
use_libm_fma | off | pins fused mul_add on every target (see above) |
§Performance model
- Build with
-C target-cpu=nativeor a fixed level (x86-64-v3/-v4); without it the code is still correct but scalar-ish. A single binary is not expected to be fast on multiple micro-architectures — that is the accepted trade (see README “Scopes” anddocs/adr/0001-no-dispatch-autovectorization-fallback.md). - This crate sets
[profile.dev] opt-level = 2, so tests and dev runs already vectorize without--release. - Codegen is statically verified by
scripts/check_asm.py: it compiles probe functions at several target levels and asserts expected vector instruction families (details and CI wiring in README; rationale indocs/adr/0004-codegen-verification-by-assembly-matching.md). - Evidence the approach works end-to-end:
examples/blis_matmul.rsreaches OpenBLAS-parity (~1.2 TFLOP/s) GEMM on an AVX-512 host using only these portable types.
§What this crate does not provide
So agents do not hallucinate APIs: no intrinsics or inline asm, no runtime
CPU dispatch, no shuffles/permutes/interleaves/transposes/swizzles/rotates,
no lane insert/extract beyond Index, no horizontal min/max, no
saturating/wrapping arithmetic, no gather/scatter or masked load/store, no
core::simd (std::simd) interop, no serde, no lane iterator, no
FromIterator, no transcendental math, no PartialOrd operators.
§Non-obvious points (agent checklist)
</>/==operators do not exist on vectors — comparisons are thesimd_*methods returning masks; branch on them withany_true/all_true.selectlives on the mask;mask_selectlives on the value. Same operation, two receivers.fma_from’s receiver is the accumulator (self += b * cshape), unlikemul_add.mul_addresults differ across build targets unlessuse_libm_fmais on.reduce_sumfolds in lane order — not pairwise; do not assume associativity-exact float sums.- Only
splatandas_arrayareconst fn; everything else is runtime (though always#[inline(always)]). - Alignment is capped at 64 bytes even for >64-byte vectors (
f64x16). boollanes: arithmetic traits do not apply — use bitwise/logical ops andselect/mask_select.- Tail handling:
from_slice_pad+reduce_sum_n+store_slice_partialare the intended ragged-edge toolkit. - Custom element types need:
Copy+num_traits::Numfor arithmetic,num_traits::Floatfor float math, the element’s owncore::opsimpls for bitwise.
§Repository pointers
README.md— project scope, FMA notes, codegen verification, usage example.examples/blis_matmul.rs— BLIS-style GEMM built only on these types.tests/basic.rs,tests/ops.rs,tests/aligned.rs— behavioral ground truth, including target-dependentmul_addtests and feature-gated complex/halftests.docs/adr/0001–0005— architecture decisions: no-dispatch rationale, single generic type, aligned aliases, assembly-based codegen verification, mul-add fallback policy.scripts/check_asm.py(+scripts/asm/probes.rs) — static codegen checker;--dump/--histfor inspecting emitted assembly.src/lib.rs—Simdand shared impls;src/aligned.rs,src/alias.rs,src/ops.rs,src/reduce.rs,src/memory.rs— one module each for the surfaces described above.