Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
rustlane
rustlane brings an ISPC-style SPMD programming model to Rust as a pure
library — you write natural scalar-looking control flow (if, while,
for, break, early return) over varying values, and proc macros lower it
to masked SIMD over nightly std::simd. No compiler fork, no external
toolchain: just #[kernel], #[export], and foreach!.
rustlane is an independent project, not affiliated with Intel.
Status
Experimental. Nightly-only. The API is unstable and will change without notice.
What a kernel looks like
The SPMD idea: write one program as if it runs on a single lane, and the
library runs it across a whole SIMD vector of lanes at once. Control flow that
diverges between lanes is handled for you. Here is the Mandelbrot escape-time
kernel — plain for / if / break over varying values:
use *;
use kernel;
The macro rewrites this into masked std::simd: the varying if/break
become per-lane mask updates and blends (ret = i + 1 stores only into lanes
still iterating), and the for loop keeps running until every lane's mask is
off, so a lane that has already escaped stops contributing without stopping the
others.
Quick start
Nightly only. Select the nightly toolchain and add rustlane to your dependencies:
# rust-toolchain.toml
[]
= "nightly"
# Cargo.toml
[]
= "0.1.0"
A #[kernel] is the per-lane program; an #[export] is the all-uniform entry
point that drives it with foreach! and is callable as an ordinary safe Rust
function:
use *;
use ;
Performance
Seven kernels from ISPC's own example suite (options contributes two),
measured against Intel ISPC on two machines. #[export] dispatches to the
widest target the CPU supports — 8 lanes on the M2 Pro, 16 on the Zen4 box —
and the ISPC target at that same width is the orange bar.


Geometric mean of rustlane / ISPC runtime at matched width:
| Machine | rustlane | ISPC | ratio |
|---|---|---|---|
| Apple M2 Pro | 8 lanes | neon-i32x8 |
0.83 |
| AMD Ryzen 9 7900X (Zen4) | 16 lanes | avx512skx-i32x16 |
1.16 |
Each round is 3 warm-up reps plus an internal min-of-15 (mandelbrot: 20); five
rounds run interleaved over every binary in a fixed order with 2 s cool-downs.
Bars are the mean over the five rounds, whiskers a 95% t interval. Every round
validated its output against the ISPC reference. Full toolchain versions are
printed under each chart; per-round raw timings land in results/<arch>.csv.
The C++ columns are the same source at -O3; scalar adds
-fno-vectorize -fno-slp-vectorize.
What works
if/elsewith divergent (varying) conditionswhile(varying condition) /for/ bareloop, withbreak/continue/ earlyreturnunder divergenceunmasked! { .. }— an all-lanes block, for loop-carried localscif!/cwhile!— opt-in coherent control flow (ISPC-styleany()guards)foreach!/foreach_2d!/foreach_tiled!— inline iteration, no closures#[derive(SpmdValue)]— a struct vectorizes to a generated SoA typeVaryingS<N>;#[spmd(uniform)]keeps a field scalarmath— ISPC-ported transcendentals (exp/log/pow/sin/cos/rsqrt/rcp)rng— LFSR113 combined-Tausworthe varying RNGreduce— horizontal reductions and cross-lane ops (broadcast / rotate / shift / scan / pack)- Runtime target dispatch: SSE2 / SSE4.1 / AVX2 / AVX-512 on x86-64, NEON on aarch64
Nightly is required and will stay required: Varying<T, N> is built on
#![feature(portable_simd)], which is unstable upstream.
Not planned
Varying<S>for a structS.Varying<T, N>isrepr(transparent)overSimd<T, N>, soThas to be a scalar. Structs go through#[derive(SpmdValue)]to SoA instead.matchon a varying scrutinee. Bindings, guards and exhaustiveness have no lane-wise meaning, and anything that does have one is anif/elsechain written out. Use that.- Explicit generic arguments on a kernel call (
k::<8>(x)). The macro supplies::<N, _>itself.
Static safety
The dangerous SPMD mistakes are compile errors, not silent races. Assigning to a uniform (scalar) variable while lanes are diverging would make every lane race on one location, so there is simply no such trait impl — the write fails to type-check with a domain-specific diagnostic:
error[E0277]: cannot assign to a value of type `i32` under execution context `VMask<N>`
--> tests/ui/uniform_assign.rs:14:7
|
14 | s = 5;
| ^ this assignment target cannot be written under the current control-flow mask
|
= note: assigning to a uniform (scalar) variable under VARYING control flow is
not supported: every lane would race on one location. Make the variable a
`Varying`, hoist the assignment out of the varying branch, or wrap it in
`unmasked!` if all-lanes semantics are intended
Two safety properties hold by construction:
- Inactive lanes never touch memory. Every masked gather/scatter carries the active mask as its hardware enable, so an out-of-bounds index on a masked-off lane neither faults nor affects results.
- 48-case compile-fail diagnostics suite. The static rejection rules for
#[kernel]and#[export]are pinned by 48 checked-intrybuildsnapshots — each asserts the error span points at the offending user token — plus an additional case covering theSpmdValuederive.
How it works
Each execution context (all-on, a uniform branch bool, a varying mask, a
uniform branch under a varying mask) is a distinct Rust type that the macro
threads through the kernel as a hidden first parameter; every operator,
assignment, and control-flow construct resolves against that type by trait, so
the macro itself never inspects a value or a type. Because the context is a
type and the "is this uniform?" test is a const, monomorphization deletes the
mask machinery on every path that turns out uniform: a kernel entered all-on
compiles down to plain scalar branches and contiguous vector loads, with blends
emitted only where control flow is genuinely divergent. There are no
unconditional coherence guards — the one any() reduction per loop is the loop
exit check, not a per-if tax. The whole kernel tree is #[inline(always)], so
#[export] can stamp it out once per SIMD target inside a #[target_feature]
shim and pick the widest at runtime with a single cached indirect call.
License
Licensed under the MIT License.
Portions of the benchmark kernels and math routines are ported from Intel's ISPC examples under their original BSD-3-Clause terms; see THIRD-PARTY.md for the full attributions.