Numerical

The workshop's bearing-only example estimates planar position and heading from linear velocity, angular velocity, and a bearing measurement to a known landmark. Its persistent state consists of a three-component mean and a 3×3 covariance matrix. Prediction advances the mean with the motion model and propagates covariance through its Jacobians. Correction compares the predicted and observed bearings, wraps the innovation with atan2(sin(r), cos(r)), and applies the Kalman gain. The covariance uses the Joseph update. The following listing is also the source used by the Rust embedding examples; the live fixed-camera extension is presented separately below.

Why an EKF? It is a representative robotics algorithm that combines matrix operations, nonlinear functions, persistent state, and successive sensor measurements. Here it also serves as a stress test of the current architecture: the same program exercises compilation, Rust embedding, checked reactive updates, and CPU/GPU execution. Earlier Mech implementations demonstrated the underlying reactive approach in the alpha-era LIVE 2019 work and the v0.1 HYTRADBOI 2022 presentation.[9][10] This case study extends that work to the current implementation; benchmarks and validation of additional algorithms, applications, and robot systems are planned.

Initialization. The imports, motion inputs, measurement covariance, and initial state establish the filter. The control vector u carries forward velocity, angular velocity, and a measurement-availability flag. Matrix shapes are inferred from their values.

--

Extended Kalman Filter

+> math/*+> logic/all--

EKF step @compute

Δt f32 :=0.1u [f32] :=[
1
0.015
1
]
'
v:=u[1]w:=u[2]visible:=u[3]bearing f32 :=-0.55R f32 :=0.25fmax f32 :=3.402823466e+38ε f32 :=0.0001ρ f32 :=0.000001m [f32] :=[
140
12
]
'
Q [f32] :=[
0.01 0
0 0.0025
]
I [f32] :=[
1 0 0
0 1 0
0 0 1
]
J [f32] :=[
1 0
0 1
0 0
]
ex [f32] :=[
1
0
0
]
'
ey [f32] :=[
0
1
0
]
'
et [f32] :=[
0
0
1
]
'
~μ [f32] :=[
55
25
0.4
]
'
~Σ [f32] :=[
100 0 0
0 100 0
0 0 0.15
]

Time update. The motion model predicts the next pose, and its Jacobians propagate the state and process-noise covariance.

θ:=μ·etsinθ:=sin(θ)cosθ:=cos(θ)d:=v*Δtμ̄:=μ+[
d*cosθ
d*sinθ
w*Δt
]
'
G:=[
1f32 0f32 0f32
0f32 1f32 0f32
(0f32-d*sinθ) d*cosθ 1f32
]
V:=[
cosθ*Δt sinθ*Δt 0f32
0f32 0f32 Δt
]
Σ̄:=G**Σ**G'+V**Q**V'

Measurement update. An available bearing corrects the prediction. When the selected landmark is outside camera range, visible is zero and the gain is zero, giving a prediction-only update. Wrapping the angular innovation avoids a discontinuity at a full revolution; the Joseph form updates the covariance.

δ:=m-J**μ̄δx:=δ·[
1f32
0f32
]
'
δy:=δ·[
0f32
1f32
]
'
q:=δ·δẑ:=atan2(δy, δx)-(μ̄·et)r:=bearing-ẑν:=atan2(sin(r), cos(r))H:=[
δy/q
(0f32-δx)/q
-1f32
]
S:=H**Σ̄·H+RK:=(Σ̄**H'/S)*visibleμ₊:=μ̄+K*νA:=I-K**HΣraw:=A**Σ̄**A'+(K**K')*R

Checked publication. Integrity predicates validate the candidate before the new mean and covariance replace the accepted state.

ΔΣ:=Σraw[[
4
7
8
]
]
-Σraw[[
2
3
6
]
]
τ:=ε+ρ*abs(Σraw[[
4
7
8
]
]
)
+ρ*abs(Σraw[[
2
3
6
]
]
)
Σ₊:=Σraw*0.5 f32 +(Σraw')*0.5 f32 xyz:=[
μ₊·ex
μ₊·ey
μ₊·et
]
'
finμ:=all(xyz≤fmax)&&all(xyz≥-fmax)finraw:=all(Σraw≤fmax)&&all(Σraw≥-fmax)finΣ:=all(Σ₊≤fmax)&&all(Σ₊≥-fmax)finite-candidate!:=finμ&&finraw&&finΣpositive-covariance!:=all(Σraw[[
1
5
9
]
]
>0f32
)
&&all(Σ₊[[
1
5
9
]
]
>0f32
)
symmetric-covariance!:=all(ΔΣ≤τ)&&all(ΔΣ≥-τ) μ = μ₊ Σ = Σ₊ (μ,Σ)

Download the complete bearing-only ekf.mec. The Rust embedding examples below use this source; the interactive fixed-camera extension has its own listing and download.

The complete source includes initialization, prediction, correction, integrity constraints, and publication. := defines a binding, ~ marks assignable persistent state, and = assigns it. Matrix multiplication uses **, a trailing apostrophe transposes a matrix, and * applies broadcast multiplication. Types and dimensions are explicit where needed and inferred through the equations. The +> line imports numerical functions and the Boolean all reduction.

Alongside matrices, Mech provides sets, tables, tuples, maps, and records. Built-in set operators include union, intersection, difference, membership, and subset tests. Tables support row filtering, column selection, and relational joins, including inner, outer, semi, and anti joins. The linked reference pages describe their syntax and operations.

Ordinary Mech matrix storage and operations use Rust's nalgebra library. The accelerated fixed-shape kernel instead lowers the typed graph into specialized instructions or shaders. Alternative Rust numerical libraries could support other execution strategies without requiring a different mathematical notation.

The three named integrity constraints check finite candidate values, positive covariance diagonals, and symmetry of the raw Joseph update within an absolute-plus-relative tolerance. Linear indices [1 5 9] select the diagonal of the 3×3 covariance, and all reduces the comparisons to a single Boolean result. The stored candidate is the symmetric part of that update. Positive diagonal entries alone do not establish a positive-definite covariance; these checks do not constitute a complete proof of filter validity.

An earlier compact presentation of the archived kernel passed state, rejection, rollback, and recovery parity tests on five CPU execution paths, including JIT and AOT. This bearing-only listing includes a numerical correction prompted by longer browser runs, described in the Reactive section. It intentionally changes covariance handling. The timing and source-size measurements below retain their original, separately identified sources; they do not measure either this correction or the fixed-camera extension.1

Extending the Filter to Fixed Cameras

The interactive program adapts the repository's fixed-camera localization example. Four cameras occupy known positions in the field and supply noisy range and bearing observations of the robot. Bearing is measured from each camera in world coordinates, rather than relative to the robot's heading. One turn predicts motion once, then incorporates every enabled camera whose range contains the robot, in camera-index order. If none can observe it, the filter publishes only the prediction.

Initialization. The filter receives commanded motion and range-and-bearing observations from four fixed cameras. Each observation includes an availability flag; the state and covariance persist between turns.

--

Fixed-camera Extended Kalman Filter

+> math/*+> logic/all--

Four-camera EKF step @compute

control [f32]:3,1 :=[
0.1
1
0.015
]
'
cameras [f32]:2,4 :=[
20 20
180 20
180 110
20 110
]
measurements [f32]:3,4 :=[
0 0 0
0 0 0
0 0 0
0 0 0
]
Q [f32] :=[
0.08 0
0 0.018
]
R [f32] :=[
0.0225 0
0 0.0004
]
fmax f32 :=3.402823466e+38ε f32 :=0.0001ρ f32 :=0.000001~μ [f32] :=[
55
25
0.4
]
'
~Σ [f32] :=[
100 0 0
0 100 0
0 0 0.15
]
field-extent [f32] :=[
200
130
]
'

Time update. A midpoint motion model predicts the next pose, and its Jacobians propagate state and process-noise covariance.

Δt:=control[1]v:=control[2]ω:=control[3]θ:=μ[3]+ω*Δt/2f32c:=cos(θ)s:=sin(θ)d:=v*Δtμ̄raw:=μ+[
d*c d*s ω*Δt
]
--

Both positive and negative edge crossings wrap into the field.

μ̄position:=μ̄raw[1..=2]+field-extent*ceil(-μ̄raw[1..=2]/field-extent)μ̄:=[
μ̄position μ̄raw[3]
]
G:=[
1f32 0f32 0f32
0f32 1f32 0f32
-d*s d*c 1f32
]
V:=[
c*Δt s*Δt 0f32
-d*s*Δt/2f32 d*c*Δt/2f32 Δt
]
Σ̄:=G**Σ**G'+V**Q**V'

Measurement update. Available cameras correct the prediction in sequence. Each correction wraps the bearing innovation and updates covariance in Joseph form; an unavailable camera contributes no correction.

--

Fixed cameras measure range and world-referenced bearing to the robot.

--

Their observation contains no robot heading measurement.

--

Align the nearest position representation when a boundary was crossed.

--

Camera ranges remain ordinary distances within the visible field.

camera-correct(μ- [f32]:3,1 , Σ- [f32]:3,3 , camera [f32]:2,1 , z [f32]:3,1 , R [f32]:2,2 ) = (μ+ [f32]:3,1 , Σraw [f32]:3,3 ) :=
inactive:=1f32-z[3]
field:=[
200f32
130f32
]
'
observed-position:=camera+z[1]*[
cos(z[2]) sin(z[2])
]
chart-shift:=field*ceil((μ-[1..=2]-observed-position)/field-0.5 f32 )*z[3]
μlocal:=[
μ-[1..=2]-chart-shift μ-[3]
]
Δ:=(μlocal[1..=2]-camera)*z[3]+[
inactive 0f32
]
q:=Δ·Δ
r:=sqrt(q)
ẑ:=[
r atan2(Δ[2], Δ[1])
]
H:=[
Δ[1]/r -Δ[2]/q
Δ[2]/r Δ[1]/q
0f32 0f32
]
S:=H**Σ-**H'+R
B:=H**Σ-
K:=(S\B)'*z[3]
νθ:=z[2]-ẑ[2]
ν:=[
z[1]-ẑ[1] atan2(sin(νθ), cos(νθ))
]
μ+:=μlocal+K**ν
I:=[
1f32 0f32 0f32
0f32 1f32 0f32
0f32 0f32 1f32
]
A:=I-K**H
Σraw:=A**Σ-**A'+K**R**K'.
(μ1, Σ1raw):=camera-correct(μ̄, Σ̄, cameras[:,1], measurements[:,1], R)Σ1:=Σ1raw*0.5 f32 +Σ1raw'*0.5 f32 (μ2, Σ2raw):=camera-correct(μ1, Σ1, cameras[:,2], measurements[:,2], R)Σ2:=Σ2raw*0.5 f32 +Σ2raw'*0.5 f32 (μ3, Σ3raw):=camera-correct(μ2, Σ2, cameras[:,3], measurements[:,3], R)Σ3:=Σ3raw*0.5 f32 +Σ3raw'*0.5 f32 (μraw, Σ4raw):=camera-correct(μ3, Σ3, cameras[:,4], measurements[:,4], R)μposition:=μraw[1..=2]+field-extent*ceil(-μraw[1..=2]/field-extent)μ₊:=[
μposition μraw[3]
]
Σ₊:=Σ4raw*0.5 f32 +Σ4raw'*0.5 f32

Checked publication. Integrity predicates validate the candidate before its mean and covariance replace the accepted state.

raw-covariances:=[
Σ1raw
Σ2raw
Σ3raw
Σ4raw
]
lower-pairs:=[
Σ1raw[[
4
7
8
]
]
Σ2raw[[
4
7
8
]
]
Σ3raw[[
4
7
8
]
]
Σ4raw[[
4
7
8
]
]
]
upper-pairs:=[
Σ1raw[[
2
3
6
]
]
Σ2raw[[
2
3
6
]
]
Σ3raw[[
2
3
6
]
]
Σ4raw[[
2
3
6
]
]
]
ΔΣ:=lower-pairs-upper-pairsτ:=ε+ρ*abs(lower-pairs)+ρ*abs(upper-pairs)finμ:=all(μraw≤fmax)&&all(μraw≥-fmax)&&all(μ₊≤fmax)&&all(μ₊≥-fmax)finraw:=all(raw-covariances≤fmax)&&all(raw-covariances≥-fmax)finΣ:=all(Σ₊≤fmax)&&all(Σ₊≥-fmax)finite-candidate!:=finμ&&finraw&&finΣpositive-covariance!:=(finΣ&&(all(Σ₊[[
1
5
9
]
]
>0f32
)
⩵false
)
)
⩵false
symmetric-covariance!:=(finraw&&((all(ΔΣ≤τ)&&all(ΔΣ≥-τ))⩵false))⩵false μ = μ₊ Σ = Σ₊ (μ,Σ)

Download the complete live camera-ekf.mec. These blocks share the ekf namespace and compile together as the numerical program running in the Output pane.

Run the Example

The EKF runs in the console’s Output pane. Open it alongside the article or use the existing full-screen view to inspect the robot, covariance ellipse, and timing measurements.

The browser demonstration makes successive state updates visible. A pose marker represents the estimated position and heading, while an angled ellipse represents the position covariance. The ellipse uses the covariance's principal axes rather than an arbitrary drawing scale. A radius-two covariance ellipse in two dimensions is not a 95% probability region; it encloses approximately 86.5% under the Gaussian model.

Start with Run or advance One turn at a time. Click a fixed camera to enable or disable it, and adjust camera range to change which cameras can observe the robot. The forward and angular velocities control its commanded motion, which wraps at the field edges. Separate noise multipliers change the simulated motion and camera measurements. The filter receives measurements and commanded motion, not simulated truth; its process and measurement covariance models stay fixed as the noise changes. The drawing and telemetry show the first filter, while larger batches execute additional independent filters. CPU and GPU compile the same camera kernel where WebGPU is available. Changing the backend or batch size starts a new episode rather than migrating a running session between devices.

Inject an invalid observation to exercise rejection in the last filter of the batch. The accepted state stays visible and the behavior enters Fault; Reset starts a new episode. The displayed source is read-only, and the source links provide complete downloadable examples. The verification control separately checks CPU/GPU numerical agreement, rejection, rollback, and recovery, and reports when WebGPU is unavailable.

Both browser routes parse the displayed camera-ekf.mec source and construct the same fixed-shape numerical program. The CPU route executes its lowered instructions in the Mech interpreter, whose Rust implementation is compiled to WebAssembly. The GPU route generates WGSL and a binding manifest from that program; the browser host submits it through WebGPU and observes completion and integrity status before accepting the result. A separate resident Mech program computes camera measurements, scene geometry, covariance ellipses, and trails. JavaScript binds the controls, schedules execution, and passes the resulting drawing tables to the shared scene renderer.

Drawing with Mech Tables

The scene uses the same table-based drawing interface as the repository's EKF example. Columns describe properties of each primitive, and expressions connect those properties to the accepted filter state. These rows draw the fixed cameras and their range circles; lines and line strips describe observation rays, headings, trails, and the covariance ellipse. The supporting calculations are in the downloadable scene program rather than repeated throughout the article.

scene-circles:=
id string
x f64
y f64
radius f64
fill *
stroke *
stroke-width f64
opacity f64
"camera-range-1" camera-screen[1,1] camera-screen[1,2] @input/camera-range "none" 0x687780 0.35 camera-range-opacity[1]
"camera-range-2" camera-screen[2,1] camera-screen[2,2] @input/camera-range "none" 0x687780 0.35 camera-range-opacity[2]
"camera-range-3" camera-screen[3,1] camera-screen[3,2] @input/camera-range "none" 0x687780 0.35 camera-range-opacity[3]
"camera-range-4" camera-screen[4,1] camera-screen[4,2] @input/camera-range "none" 0x687780 0.35 camera-range-opacity[4]
"camera-1" camera-screen[1,1] camera-screen[1,2] 2.3 camera-colors[1] 0x91cabc 0.3 1.0
"camera-2" camera-screen[2,1] camera-screen[2,2] 2.3 camera-colors[2] 0x91cabc 0.3 1.0
"camera-3" camera-screen[3,1] camera-screen[3,2] 2.3 camera-colors[3] 0x91cabc 0.3 1.0
"camera-4" camera-screen[4,1] camera-screen[4,2] 2.3 camera-colors[4] 0x91cabc 0.3 1.0
"camera-hit-1" camera-screen[1,1] camera-screen[1,2] 2.5 "none" "none" 0.0 1.0
"camera-hit-2" camera-screen[2,1] camera-screen[2,2] 2.5 "none" "none" 0.0 1.0
"camera-hit-3" camera-screen[3,1] camera-screen[3,2] 2.5 "none" "none" 0.0 1.0
"camera-hit-4" camera-screen[4,1] camera-screen[4,2] 2.5 "none" "none" 0.0 1.0

Download the complete camera and scene program. The table above is extracted from this file, which supplies the live drawing.

Browser results describe the browser and device running this page. The live summary uses the latest accepted turns after warmup, rather than independent process trials. Turn time includes input binding, execution, validation, synchronization, and one filter's readback; drawing and compilation are excluded. WebAssembly CPU and WebGPU have different timing boundaries from the native programs in the archived charts. A browser may translate WebGPU to Metal, Vulkan, or another platform API, which does not make its timings interchangeable with direct-Metal measurements.

Application Source Size

The source audit compares backend-neutral Mech with textbook-style Rust and a separate SIMD Rust implementation. It counts programmer-chosen names as one character per occurrence, excludes comments and nonliteral whitespace, and retains operators, types, numbers, and library names. The table reports normalized source characters, not lines or bytes. General libraries, compilers, and runtime internals are outside this application-source boundary.[11]

Application source category

Mech

Rust textbook-style

Rust SIMD

Setup, constants and declarations

340

486

561

EKF update and candidate handling

356

1,024

1,119

Validation and component extraction

383

263

396

Matrix helpers

0

251

310

SIMD wrappers and adapters

0

0

1,243

Batch dispatch, workers and rollback

0

331

1,614

Total normalized characters

1,079

2,355

5,243

The selected SIMD Rust application is almost five times as long as Mech. Its SIMD and batch support accounts for 2,857 characters, more than half of its total. A Rust library could provide some of that machinery, and different implementations could be shorter. In this case, the Mech application reuses the compiler's backend support instead of expressing a second set of equations and execution scaffolding. The counts describe the archived audit, not the compact document displayed above.

Embeddable

A Rust caller supplies source, identifies live inputs and exported state, and selects an execution backend. These examples use the bearing-only ekf.mec listing, not the extended camera kernel. Source can come from a file with include_str! or from a string embedded directly in mech::mech!. The macro constructs a kernel builder and preserves the source text. Compilation is explicit in .compile(...); the macro itself does not compile Mech during Rust compilation.

fn main() -> Result<(), Box<dyn std::error::Error>> {
    use mech::kernel::Backend;
    let source = include_str!("ekf.mec");
    let kernel = mech::mech!(source)
        .input("bearing", [-0.55; 4])
        .export("μ")
        .compile(Backend::Jit)?;
    let mut ekf = kernel.start()?;
    ekf.turn([("bearing", [-0.54; 4])])?;
    let state = ekf.state("μ")?;

    println!("{} filter instances", kernel.instances());
    for (instance, pose) in state.chunks_exact(3).enumerate() {
        println!(
            "filter {instance}: x={:.6}, y={:.6}, heading={:.6}",
            pose[0], pose[1], pose[2]
        );
    }
    Ok(())
}

Download the complete main.rs.

Compilation prepares a reusable kernel. start() initializes an independent session, and turn(...) binds an update packet and executes synchronously. The four bearing values in this example select four independent filters. A caller can submit new measurements as they arrive and borrow the published state by its source name. A rejected numerical update returns an error without replacing the accepted state. Invalid measurements must be replaced before retrying because a numerical rejection does not discard the newly bound inputs.

This interface is useful when Rust already provides the application's event loop, sensor access, or ROS integration. It executes a fixed-shape numerical kernel with checked publication; it does not implicitly construct the full reactive coordinator or deliver external effects. The larger runtime described next is a separate integration choice. Rust-native function implementations can also be registered in Mech's function catalog, allowing an application to expose its own operations to Mech.

Preparing and Loading an AOT Library

An application with a known kernel can compile it before deployment. The AOT producer below saves the native library and the interface information needed to initialize and address its inputs and state.

use mech::kernel::Backend;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let bundle = std::env::args_os()
        .nth(1)
        .unwrap_or_else(|| "ekf.bundle".into());
    let kernel = mech::mech!(include_str!("ekf.mec"))
        .input("bearing", [-0.55; 4])
        .export("μ")
        .compile(Backend::AotSimd)?;
    kernel.save_bundle(&bundle)?;
    println!(
        "Saved AOT bundle: {}",
        std::path::Path::new(&bundle).display()
    );
    Ok(())
}

Download the complete build.rs.

A separate process loads that bundle and submits updates through the same session interface.

use mech::kernel::Kernel;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // SAFETY: Load only a trusted bundle produced for this host by the matching
    // Mech build. Loading a native library can execute its initialization code.
    let kernel = unsafe { Kernel::load_bundle("ekf.bundle")? };
    let mut ekf = kernel.start()?;
    ekf.turn([("bearing", [-0.54; 4])])?;
    for (instance, pose) in ekf.state("μ")?.chunks_exact(3).enumerate() {
        println!(
            "filter {instance}: x={:.6}, y={:.6}, heading={:.6}",
            pose[0], pose[1], pose[2]
        );
    }
    Ok(())
}

Download the complete load.rs.

The bundle describes named inputs, exported state, initial values, and the native layout. It is a deployment artifact, not a checkpoint of an already running filter. Loading a compatible bundle does not parse Mech source, invoke Cranelift, or run a linker.2

JIT and AOT share the relevant Cranelift numerical lowering. Their distinction is when native code is prepared and whether it is saved for later loading. Scalar and four-lane SIMD AOT are both implemented. Worker scheduling is an additional execution choice: the matched chart includes single-worker SIMD AOT and eight-worker SIMD JIT, so their difference must not be attributed to compilation time alone.

Dynamic Library Size, Throughput, and Memory

An independent checked-only dynamic-library experiment used one host thread, 10,000 filters, 200 timed turns, and seven fresh processes per library. The same minimal ABI loader called each library. Throughput and whole-process peak resident memory (RSS) are reported as median ± MAD.

Checked dynamic library

File size (bytes)

Throughput (M filter-turns/s)

Peak process RSS (MiB)

Mech scalar AOT

33,544

14.671 ± 0.008

2.69 ± 0.00

Rust optimized scalar

50,016

21.121 ± 0.005

2.69 ± 0.00

Mech four-lane SIMD AOT

33,864

34.863 ± 0.010

2.69 ± 0.00

This is a code-generation and packaging diagnostic, not the matched SIMD language comparison. In particular, the Rust library is a specialized scalar implementation, while the faster Mech row uses explicit cross-filter SIMD. The experiment does not measure the new ergonomic Rust wrapper.

All three rows have the same median whole-process peak RSS, 2,818,048 bytes, with MAD zero. Their ranges overlap, providing no evidence of a memory-use advantage. A later inspection of rebuilt or retained libraries traced most of the file-size difference to Mach-O segment padding and different linker/deployment settings. The smaller Mech file therefore does not show that it contains fewer numerical instructions or avoids a large linked Rust runtime.[12]

Reactive

Robot software commonly implements feedback loops over continuous data streams. In Mech, Rust host drivers supply updates from direct device interfaces or channels such as ROS. The runtime evaluates the affected computation, validates candidate state, and either publishes it or reports a rejection while retaining the previous state. This makes the acceptance boundary explicit in a program that is expected to process many successive inputs.

Build, activation, and checked reactive execution Mech source compiles into a typed Program Artifact, serializable as program.mecb. Configuration and host capabilities determine activation. On each live turn Rust drivers provide inputs, the resident computation produces a candidate, and validation either publishes state and outputs or rejects the candidate and retains prior state. InputsProgramOutputsFailures Build and activation (once) program.mecbSerialized artifact encode / decode ekf.mec Mech compiler Program Artifact(typed dataflow graph) .mcfg configuration Activate Activation failed Residentcomputation Activation checks hostsand capabilities: permissionsfor resource operations. Live execution (each turn) Rust hostdrivers Resident computation Time updateMeasurement updateμ → robot poseΣ → ellipse Validatecandidate Publish state+ recordpass Abort candidateretain prior statefail EKF state telemetryμ = [98.2, 55.2, −0.03]Σ = [107.9 −28.9 …−28.9 19.9 …] SVG scenepose + 2σ
Build, activation, and checked reactive execution Mech source compiles into a typed Program Artifact, serializable as program.mecb. Configuration and host capabilities determine activation. On each live turn Rust drivers provide inputs, the resident computation produces a candidate, and validation either publishes state and outputs or rejects the candidate and retains prior state. Inputs Program Outputs Failures Build and activation (once) ekf.mec Mech compiler program.mecbSerialized artifact encode / decode Program Artifact(typed dataflow graph) Activate .mcfgconfiguration Residentcomputation Activationfailed Activation checks hostsand capabilities: permissionsfor resource operations. Live execution (each turn) Rust host drivers Resident computation Time updateMeasurement updateμ → robot poseΣ → ellipse Validatecandidate Publish state+ record Abort candidateretain prior state passfail EKF state telemetryμ = [98.2, 55.2, −0.03]Σ = [107.9 −28.9 …−28.9 19.9 …] SVG scenepose + 2σ
Poster architecture diagram, adapted for the article. The build lane runs during build and activation; the execution lane repeats for each accepted or rejected turn. Telemetry values are illustrative, while the live figure above uses the actual computed values.

The build lane produces a Program Artifact containing the typed dataflow graph. A .mecb file is its serialized representation, so encoding and decoding connect the two representations. A .mcfg configuration supplies deployment choices such as hosts, capability grants, runtime policy, and compute targets. Activation checks those requirements against the available host services before creating the resident computation. Missing services or permissions can cause activation to fail before live execution starts.

During each turn, input changes identify affected computation. The resident graph calculates candidate state and proposed effects. Integrity predicates run before publication. A successful turn makes its accepted state available and records the result; after-commit output can deliver telemetry or a scene. A failed turn reports the rejection without publishing the candidate or its external effects. The diagram separates those outcomes rather than treating validation as a warning attached to an already published result.

A constraint caught rounding drift. While developing the earlier bearing-only demo, we exercised six combinations of CPU/GPU execution and filter batch size. The 65,536-filter CPU run rejected turn 360 after mirrored covariance entries differed by about 0.0001006, exceeding the original fixed tolerance of 0.0001; GPU runs reached the same failure at different turns. Rejection retained the accepted state and identified the failing constraint and filter. The Joseph update preserves symmetry in exact arithmetic, but separate f32 evaluations had accumulated different roundoff. We changed the Mech source to check each raw pair against 0.0001 + 0.000001 * abs(a) + 0.000001 * abs(b), then store the symmetric part as Σraw * 0.5 + (Σraw') * 0.5. Checking before projection exposes large asymmetries; projecting prevents small differences from accumulating into the next turn. Both browser backends compile that one source, so the correction required no separate CPU or GPU implementation. The corrected source completed 1,000 turns in each of the six configurations. The diagnostic record and reproducible tests document these bounded checks. They predate the fixed-camera extension and do not revise the native benchmark charts.

The EKF is one numerical component within this model. Its mean and covariance can drive a telemetry view and a robot scene from the same accepted update. The high-throughput kernel experiments below exercise double-buffered state and publication boundaries, but they do not time the complete host admission, recording, and output-delivery lifecycle. A separate retained f64, range-and-bearing EKF experiment measured a resident turn at approximately 315 ns median with zero steady-state allocations on its Apple M3 test machine.3 That result concerns a different kernel, machine, and execution boundary and is not plotted beside the parallel f32 measurements.

Heterogeneous

The typed numerical graph supports several execution targets. The following native bearing-only experiments examine cross-language CPU implementations, Mech's backend choices, and GPU execution. The fixed-camera browser example provides a separate way to explore CPU and GPU execution on the reader's machine; its workload and timings are not substituted for these retained measurements.

How the Measurements Were Collected

The native charts use an Apple M1 with four performance and four efficiency CPU cores, an eight-core GPU, 8 GiB of memory, and macOS 15.6.1. Each throughput chart processes 500,000 independent f32 filters for 40 timed turns. Each displayed mode has ten retained measurements from process trials. The CPU campaign's Mech trials measure both modes in the same process, while other CPU commands select one mode per process. The matched five-backend campaign uses a fresh process for each backend/mode case. The collectors interleave cases in a shuffled order, and no samples are removed as outliers. Values are medians with unscaled median absolute deviation (MAD), which describes observed variation rather than a confidence interval.

Checked execution evaluates the candidate-state predicates. Unchecked execution omits those predicates while retaining the specified state-publication mechanism. This does not refer to Rust's unsafe keyword or to a general removal of all runtime checks. The cross-language CPU experiment, same-source backend experiment, and Metal comparison have distinct execution boundaries and source identities; equal throughput units do not make them one experiment.

The repository retains raw process output, source and executable hashes, toolchain versions, and reproduction scripts. Native Rust uses LLVM 22.1.0 in the recorded nightly build, while Mech's native code uses Cranelift 0.131.3. The other compiler and package versions, including Julia, Mojo, Futhark/ISPC, Numba, Taichi, and Halide, are recorded with the individual campaigns.[13] Compilation and device setup are outside these steady-state throughput measurements. Their cost matters for short-lived applications but requires a separate experiment.

CPU Implementations

Mech and Rust reach similar checked CPU throughput Apple M1 · 500,000 f32 filters × 40 turns · eight workers · median ± MAD Unchecked bars are above checked bars. Diagonal hatching indicates GPU execution. Values and whiskers show the median and unscaled median absolute deviation from ten retained process trials per mode. No samples were removed. Chart uses a linear throughput axis. 2026-09-24 CPU campaign. Fused 40-turn execution; failure interfaces differ. Ten process trials per mode; Mech modes share a process. MAD is descriptive spread. {"units":"million filter-turns/s","summary":"median and unscaled MAD","archives":[{"filename":"apple-m1-cpu-equal-n10-2026-09-24.json","sha256":"c64ec81408bd951ff4808e6a51484500c9d1decc2a594c448c6d5b6cfb5e81d3"}],"rows":[{"key":"Mech fused SIMD/JIT","label":"Mech","source":"apple-m1-cpu-equal-n10-2026-09-24.json","values":{"unchecked":{"median":184.1365,"mad":8.125,"samples":[199.403,193.178,163.078,180.108,181.294,186.979,189.018,125.852,198.785,176.928]},"checked":{"median":151.323,"mad":2.563999999999993,"samples":[151.514,154.585,154.47,152.211,141.301,153.304,150.755,141.186,151.132,143.617]}}},{"key":"Rust packed SIMD","label":"Rust","source":"apple-m1-cpu-equal-n10-2026-09-24.json","values":{"unchecked":{"median":170.4901536685,"mad":1.719562261500002,"samples":[171.227319154,169.75298818299999,172.267699699,171.71644079499998,171.99449425,142.440757998,164.707956898,161.18113536,172.151732161,159.05484268200001]},"checked":{"median":149.941070295,"mad":2.4944931120000007,"samples":[155.12539181,150.13591992500002,154.442738822,149.799970227,154.383527278,150.08217036300002,148.636630875,147.476402964,147.41675140200002,138.559488722]}}},{"key":"Mojo SIMD-4","label":"Mojo","source":"apple-m1-cpu-equal-n10-2026-09-24.json","values":{"unchecked":{"median":128.15217547786744,"mad":0.22885622684089668,"samples":[126.78931419659952,128.07951176090117,128.08689414899067,128.26844019163303,127.85516566833091,128.3128781220127,128.21745680674422,129.19896640826875,126.79011797820479,128.69681604077115]},"checked":{"median":119.29087271247445,"mad":0.4423979625641792,"samples":[119.64441679329033,119.20869269787153,119.66732483695327,119.91414147470411,119.00440911335767,119.3730527270774,119.799216513124,117.8995024640996,117.58480804280087,117.71699656854956]}}},{"key":"Julia SIMD.jl","label":"Julia","source":"apple-m1-cpu-equal-n10-2026-09-24.json","values":{"unchecked":{"median":136.40167593235222,"mad":1.077462938770168,"samples":[137.3035807915723,137.65469695067247,135.9584646890375,137.09107557181872,133.85279371694986,137.22531026436812,134.30822480467688,134.21186181211178,136.84488717566694,135.12201095332802]},"checked":{"median":129.04738995978437,"mad":2.8440061619301957,"samples":[131.95774683915505,132.8321867159352,125.82711668734949,125.94124630537972,126.54096605916311,132.30342438241107,130.84477475480915,126.26973451529466,130.93214686986244,127.25000516475959]}}},{"key":"Futhark ISPC AOT","label":"Futhark","source":"apple-m1-cpu-equal-n10-2026-09-24.json","values":{"unchecked":{"median":149.93130931364527,"mad":1.3701831075792512,"samples":[148.89150276193737,148.87931098654875,154.32455998209835,151.61967720170725,152.66010228226853,150.45286311798515,149.8149785015506,147.1897791417364,150.04764012573992,144.7418890263937]},"checked":{"median":97.8970065347768,"mad":1.1622517695540182,"samples":[99.07513362758648,98.59502095144195,96.75063008847845,99.18420987378809,98.83180802909608,98.21542571476276,97.57858735479087,95.65487720305138,95.87635784891803,87.80864739559551]}}},{"key":"NumPy/Numba","label":"Numba","source":"apple-m1-cpu-equal-n10-2026-09-24.json","values":{"unchecked":{"median":81.55697735300001,"mad":0.3498035079999937,"samples":[81.849819628,81.963742094,82.42566362100001,81.027578177,81.725900985,81.56094070200001,81.55301400399999,80.607863895,79.012449593,81.505543299]},"checked":{"median":79.38031587649999,"mad":0.5502568595000099,"samples":[80.043316605,79.342304957,79.584766478,76.088288555,79.517856478,79.418326796,79.817828867,76.55556129,78.617202029,72.445689478]}}}]} Mech and Rust reach similar checked CPU throughput Apple M1 · 500,000 f32 filters × 40 turns · eight workers · median ± MAD unchecked (upper) checked (lower) GPU 0 50 100 150 200 Mech SIMD/JIT Mech, SIMD/JIT, unchecked: 184.1 million filter-turns per second; MAD 8.1; ten samples. 184.1 ± 8.1 Mech, SIMD/JIT, checked: 151.3 million filter-turns per second; MAD 2.6; ten samples. 151.3 ± 2.6 Rust packed SIMD Rust, packed SIMD, unchecked: 170.5 million filter-turns per second; MAD 1.7; ten samples. 170.5 ± 1.7 Rust, packed SIMD, checked: 149.9 million filter-turns per second; MAD 2.5; ten samples. 149.9 ± 2.5 Mojo SIMD-4 Mojo, SIMD-4, unchecked: 128.2 million filter-turns per second; MAD 0.2; ten samples. 128.2 ± 0.2 Mojo, SIMD-4, checked: 119.3 million filter-turns per second; MAD 0.4; ten samples. 119.3 ± 0.4 Julia SIMD.jl Julia, SIMD.jl, unchecked: 136.4 million filter-turns per second; MAD 1.1; ten samples. 136.4 ± 1.1 Julia, SIMD.jl, checked: 129.0 million filter-turns per second; MAD 2.8; ten samples. 129.0 ± 2.8 Futhark AOT Futhark, AOT, unchecked: 149.9 million filter-turns per second; MAD 1.4; ten samples. 149.9 ± 1.4 Futhark, AOT, checked: 97.9 million filter-turns per second; MAD 1.2; ten samples. 97.9 ± 1.2 Numba compiled Python kernel Numba, compiled Python kernel, unchecked: 81.6 million filter-turns per second; MAD 0.3; ten samples. 81.6 ± 0.3 Numba, compiled Python kernel, checked: 79.4 million filter-turns per second; MAD 0.6; ten samples. 79.4 ± 0.6 Throughput (million filter-turns/s, linear scale) 2026-09-24 CPU campaign. Fused 40-turn execution; failure interfaces differ. Ten process trials per mode; Mech modes share a process. MAD is descriptive spread. Raw samples and provenance
Mech and Rust reach similar checked CPU throughput Apple M1 · 500,000 f32 filters × 40 turns · eight workers · median ± MAD Unchecked bars are above checked bars. Diagonal hatching indicates GPU execution. Values and whiskers show the median and unscaled median absolute deviation from ten retained process trials per mode. No samples were removed. Chart uses a linear throughput axis. 2026-09-24 CPU campaign. Fused 40-turn execution; failure interfaces differ. Ten process trials per mode; Mech modes share a process. MAD is descriptive spread. {"units":"million filter-turns/s","summary":"median and unscaled MAD","archives":[{"filename":"apple-m1-cpu-equal-n10-2026-09-24.json","sha256":"c64ec81408bd951ff4808e6a51484500c9d1decc2a594c448c6d5b6cfb5e81d3"}],"rows":[{"key":"Mech fused SIMD/JIT","label":"Mech","source":"apple-m1-cpu-equal-n10-2026-09-24.json","values":{"unchecked":{"median":184.1365,"mad":8.125,"samples":[199.403,193.178,163.078,180.108,181.294,186.979,189.018,125.852,198.785,176.928]},"checked":{"median":151.323,"mad":2.563999999999993,"samples":[151.514,154.585,154.47,152.211,141.301,153.304,150.755,141.186,151.132,143.617]}}},{"key":"Rust packed SIMD","label":"Rust","source":"apple-m1-cpu-equal-n10-2026-09-24.json","values":{"unchecked":{"median":170.4901536685,"mad":1.719562261500002,"samples":[171.227319154,169.75298818299999,172.267699699,171.71644079499998,171.99449425,142.440757998,164.707956898,161.18113536,172.151732161,159.05484268200001]},"checked":{"median":149.941070295,"mad":2.4944931120000007,"samples":[155.12539181,150.13591992500002,154.442738822,149.799970227,154.383527278,150.08217036300002,148.636630875,147.476402964,147.41675140200002,138.559488722]}}},{"key":"Mojo SIMD-4","label":"Mojo","source":"apple-m1-cpu-equal-n10-2026-09-24.json","values":{"unchecked":{"median":128.15217547786744,"mad":0.22885622684089668,"samples":[126.78931419659952,128.07951176090117,128.08689414899067,128.26844019163303,127.85516566833091,128.3128781220127,128.21745680674422,129.19896640826875,126.79011797820479,128.69681604077115]},"checked":{"median":119.29087271247445,"mad":0.4423979625641792,"samples":[119.64441679329033,119.20869269787153,119.66732483695327,119.91414147470411,119.00440911335767,119.3730527270774,119.799216513124,117.8995024640996,117.58480804280087,117.71699656854956]}}},{"key":"Julia SIMD.jl","label":"Julia","source":"apple-m1-cpu-equal-n10-2026-09-24.json","values":{"unchecked":{"median":136.40167593235222,"mad":1.077462938770168,"samples":[137.3035807915723,137.65469695067247,135.9584646890375,137.09107557181872,133.85279371694986,137.22531026436812,134.30822480467688,134.21186181211178,136.84488717566694,135.12201095332802]},"checked":{"median":129.04738995978437,"mad":2.8440061619301957,"samples":[131.95774683915505,132.8321867159352,125.82711668734949,125.94124630537972,126.54096605916311,132.30342438241107,130.84477475480915,126.26973451529466,130.93214686986244,127.25000516475959]}}},{"key":"Futhark ISPC AOT","label":"Futhark","source":"apple-m1-cpu-equal-n10-2026-09-24.json","values":{"unchecked":{"median":149.93130931364527,"mad":1.3701831075792512,"samples":[148.89150276193737,148.87931098654875,154.32455998209835,151.61967720170725,152.66010228226853,150.45286311798515,149.8149785015506,147.1897791417364,150.04764012573992,144.7418890263937]},"checked":{"median":97.8970065347768,"mad":1.1622517695540182,"samples":[99.07513362758648,98.59502095144195,96.75063008847845,99.18420987378809,98.83180802909608,98.21542571476276,97.57858735479087,95.65487720305138,95.87635784891803,87.80864739559551]}}},{"key":"NumPy/Numba","label":"Numba","source":"apple-m1-cpu-equal-n10-2026-09-24.json","values":{"unchecked":{"median":81.55697735300001,"mad":0.3498035079999937,"samples":[81.849819628,81.963742094,82.42566362100001,81.027578177,81.725900985,81.56094070200001,81.55301400399999,80.607863895,79.012449593,81.505543299]},"checked":{"median":79.38031587649999,"mad":0.5502568595000099,"samples":[80.043316605,79.342304957,79.584766478,76.088288555,79.517856478,79.418326796,79.817828867,76.55556129,78.617202029,72.445689478]}}}]} Mech and Rust reach similar checked CPU throughput Apple M1 · 500,000 f32 filters × 40 turns · eight workers · median ± MAD unchecked (upper) checked (lower) GPU Mech · SIMD/JIT Mech · SIMD/JIT, unchecked: 184.1 million filter-turns per second; MAD 8.1; ten samples. 184.1 ± 8.1 Mech · SIMD/JIT, checked: 151.3 million filter-turns per second; MAD 2.6; ten samples. 151.3 ± 2.6 Rust · packed SIMD Rust · packed SIMD, unchecked: 170.5 million filter-turns per second; MAD 1.7; ten samples. 170.5 ± 1.7 Rust · packed SIMD, checked: 149.9 million filter-turns per second; MAD 2.5; ten samples. 149.9 ± 2.5 Mojo · SIMD-4 Mojo · SIMD-4, unchecked: 128.2 million filter-turns per second; MAD 0.2; ten samples. 128.2 ± 0.2 Mojo · SIMD-4, checked: 119.3 million filter-turns per second; MAD 0.4; ten samples. 119.3 ± 0.4 Julia · SIMD.jl Julia · SIMD.jl, unchecked: 136.4 million filter-turns per second; MAD 1.1; ten samples. 136.4 ± 1.1 Julia · SIMD.jl, checked: 129.0 million filter-turns per second; MAD 2.8; ten samples. 129.0 ± 2.8 Futhark · AOT Futhark · AOT, unchecked: 149.9 million filter-turns per second; MAD 1.4; ten samples. 149.9 ± 1.4 Futhark · AOT, checked: 97.9 million filter-turns per second; MAD 1.2; ten samples. 97.9 ± 1.2 Numba · compiled Python kernel Numba · compiled Python kernel, unchecked: 81.6 million filter-turns per second; MAD 0.3; ten samples. 81.6 ± 0.3 Numba · compiled Python kernel, checked: 79.4 million filter-turns per second; MAD 0.6; ten samples. 79.4 ± 0.6 0 50 100 150 200 Throughput (million filter-turns/s) Linear scale 2026-09-24 CPU campaign. Fused 40-turn execution; failure interfaces differ. Ten process trials per mode; Mech modes share a process. MAD is descriptive spread. Raw samples and provenance
Archived bearing-only implementations with matched population and eight-worker CPU budget. The Mech and Rust rows use four-wide SIMD with fused turns. Every bar is median ± MAD from ten retained samples.

The cross-language CPU chart includes Mech, Rust, Mojo, Julia, Futhark, and a Numba kernel launched from Python. The NumPy/Numba path performs its numerical work in compiled code; it is not an interpreted Python or eager NumPy baseline. The comparison fixes the machine, precision, filter population, turn count, and eight-worker budget while using implementations suited to each system.

The closest comparison is between Mech and Rust. Both use four-wide packed SIMD and execute a fused 40-turn block with block-atomic rollback. Checked medians are 151.3 ± 2.6 million filter-turns/s for Mech and 149.9 ± 2.5 for Rust. Their small median difference is within the observed variation. Unchecked medians are 184.1 ± 8.1 and 170.5 ± 1.7, respectively. These results compare the selected implementations and their code generation. They do not establish that Rust cannot match the Mech result with a different implementation or optimization.

The remaining rows provide context rather than an exact fault-interface equivalence. Runtime setup, failure observation, and compiler transformations differ. Rust's measured interval includes worker creation and checkpoint work; Futhark's entry includes initialization and checksum work. Performance specialists may improve these implementations. The retained sources make those choices inspectable rather than attributing the entire difference to a language name.

One Mech Kernel, Five Execution Backends

Backend choice changes EKF throughput Apple M1 · same f32 source · 500,000 filters × 40 turns · median ± MAD Unchecked bars are above checked bars. Diagonal hatching indicates GPU execution. Values and whiskers show the median and unscaled median absolute deviation from ten retained process trials per mode. No samples were removed. Chart uses a logarithmic throughput axis. 2026-09-25 matched campaign. Both modes publish state after every turn. Ten fresh processes per mode. JIT and AOT share the numerical lowering pipeline. {"units":"million filter-turns/s","summary":"median and unscaled MAD","archives":[{"filename":"apple-m1-mech-backend-pairs-n10-2026-09-25.json","sha256":"8bd64d4ef6dc480f7e5d49c59af4a4dc2342eb30a5171fc8b151b4a1df64d62f"}],"rows":[{"key":"metal","label":"Metal GPU","source":"apple-m1-mech-backend-pairs-n10-2026-09-25.json","values":{"unchecked":{"median":420.74678044303346,"mad":2.282657675358166,"samples":[423.62447808140473,425.918558323605,419.0591627306884,426.6647734128454,421.34498290413796,416.5849379091606,416.0710955659553,421.60960005059314,420.1485779819289,420.13717478756814]},"checked":{"median":420.89616624669395,"mad":2.2913551544579036,"samples":[423.1927216268747,418.0445502134316,418.6100113179589,424.3394037984699,420.5634498819794,415.87174515379456,418.70092511132003,421.22888261140844,421.7177648376493,424.1530384881557]}}},{"key":"simd-jit-8w","label":"SIMD JIT","source":"apple-m1-mech-backend-pairs-n10-2026-09-25.json","values":{"unchecked":{"median":141.64502845825743,"mad":0.8321959454704881,"samples":[141.3460026467039,142.3079865020875,114.72258904929407,142.53808662217696,141.94405426981095,140.79281051087037,111.9140947408769,142.45720240181134,140.15195976237234,142.03793097379827]},"checked":{"median":121.07117587985559,"mad":0.5278566332348547,"samples":[121.44138783218014,119.38195959517577,120.53935382082871,121.80128880988723,121.30331263152804,121.0121153215448,121.98524893377268,121.13023643816636,120.48102047424342,120.54728467241276]}}},{"key":"simd-aot","label":"SIMD AOT","source":"apple-m1-mech-backend-pairs-n10-2026-09-25.json","values":{"unchecked":{"median":37.059153390893584,"mad":0.08453453477176254,"samples":[37.00580194725714,35.68238126885224,36.993934513340825,37.135500510019895,37.1518753413108,37.11250483453003,36.808455370402136,37.18796681066932,37.11343745353572,36.695143627085606]},"checked":{"median":34.61659955143526,"mad":0.010267028789513688,"samples":[34.641627333538175,34.61564090426439,34.61328705684537,34.627335207970695,34.60680115039167,34.62262459236231,34.605755770145116,34.617558198606126,34.29748567276126,34.6836120760145]}}},{"key":"scalar-jit","label":"Scalar JIT","source":"apple-m1-mech-backend-pairs-n10-2026-09-25.json","values":{"unchecked":{"median":15.948693690507435,"mad":0.032873735724533226,"samples":[15.970489193459889,15.897644074231627,15.68643189085235,15.980435153241887,15.974050678621442,15.982320357220843,15.879842529097784,15.980814495243093,15.92689818755498,15.90943395626059]},"checked":{"median":13.224083710585894,"mad":0.0053554494577738865,"samples":[13.21602531987453,13.230390242373392,13.22333429530666,13.224833125865127,13.25392099011784,13.219679343457845,13.221980746763814,13.226292944533226,13.213348055231718,13.291288195047787]}}},{"key":"evaluator","label":"Interpreter","source":"apple-m1-mech-backend-pairs-n10-2026-09-25.json","values":{"unchecked":{"median":0.8278867294891764,"mad":0.0010342682130255465,"samples":[0.8254288291068161,0.8253607752653016,0.8277262381964557,0.8287030600235645,0.8280472207818971,0.8274474938954149,0.8252878258347235,0.8291389353808394,0.8284888884047696,0.8294064093323731]},"checked":{"median":0.695042081257192,"mad":0.00106619241415834,"samples":[0.6963119506784141,0.691474818202843,0.6914666541027176,0.6943103467182352,0.6954768079240193,0.6963518763439652,0.6945469192474181,0.6959045966642866,0.6963894080737908,0.6946073545903646]}}}]} Backend choice changes EKF throughput Apple M1 · same f32 source · 500,000 filters × 40 turns · median ± MAD unchecked (upper) checked (lower) GPU 0.1 1 10 100 1000 Metal GPU 64-thread groups Metal GPU, 64-thread groups, unchecked: 420.7 million filter-turns per second; MAD 2.3; ten samples. 420.7 ± 2.3 Metal GPU, 64-thread groups, checked: 420.9 million filter-turns per second; MAD 2.3; ten samples. 420.9 ± 2.3 SIMD JIT 8 CPU workers SIMD JIT, 8 CPU workers, unchecked: 141.6 million filter-turns per second; MAD 0.8; ten samples. 141.6 ± 0.8 SIMD JIT, 8 CPU workers, checked: 121.1 million filter-turns per second; MAD 0.5; ten samples. 121.1 ± 0.5 SIMD AOT 1 CPU worker SIMD AOT, 1 CPU worker, unchecked: 37.1 million filter-turns per second; MAD <0.1; ten samples. 37.1 ± <0.1 SIMD AOT, 1 CPU worker, checked: 34.6 million filter-turns per second; MAD <0.1; ten samples. 34.6 ± <0.1 Scalar JIT 1 CPU worker Scalar JIT, 1 CPU worker, unchecked: 15.9 million filter-turns per second; MAD <0.1; ten samples. 15.9 ± <0.1 Scalar JIT, 1 CPU worker, checked: 13.2 million filter-turns per second; MAD <0.1; ten samples. 13.2 ± <0.1 Interpreter numeric instructions Interpreter, numeric instructions, unchecked: 0.83 million filter-turns per second; MAD <0.1; ten samples. 0.83 ± <0.1 Interpreter, numeric instructions, checked: 0.70 million filter-turns per second; MAD <0.1; ten samples. 0.70 ± <0.1 Throughput (million filter-turns/s, logarithmic scale) 2026-09-25 matched campaign. Both modes publish state after every turn. Ten fresh processes per mode. JIT and AOT share the numerical lowering pipeline. Raw samples and provenance
Backend choice changes EKF throughput Apple M1 · same f32 source · 500,000 filters × 40 turns · median ± MAD Unchecked bars are above checked bars. Diagonal hatching indicates GPU execution. Values and whiskers show the median and unscaled median absolute deviation from ten retained process trials per mode. No samples were removed. Chart uses a logarithmic throughput axis. 2026-09-25 matched campaign. Both modes publish state after every turn. Ten fresh processes per mode. JIT and AOT share the numerical lowering pipeline. {"units":"million filter-turns/s","summary":"median and unscaled MAD","archives":[{"filename":"apple-m1-mech-backend-pairs-n10-2026-09-25.json","sha256":"8bd64d4ef6dc480f7e5d49c59af4a4dc2342eb30a5171fc8b151b4a1df64d62f"}],"rows":[{"key":"metal","label":"Metal GPU","source":"apple-m1-mech-backend-pairs-n10-2026-09-25.json","values":{"unchecked":{"median":420.74678044303346,"mad":2.282657675358166,"samples":[423.62447808140473,425.918558323605,419.0591627306884,426.6647734128454,421.34498290413796,416.5849379091606,416.0710955659553,421.60960005059314,420.1485779819289,420.13717478756814]},"checked":{"median":420.89616624669395,"mad":2.2913551544579036,"samples":[423.1927216268747,418.0445502134316,418.6100113179589,424.3394037984699,420.5634498819794,415.87174515379456,418.70092511132003,421.22888261140844,421.7177648376493,424.1530384881557]}}},{"key":"simd-jit-8w","label":"SIMD JIT","source":"apple-m1-mech-backend-pairs-n10-2026-09-25.json","values":{"unchecked":{"median":141.64502845825743,"mad":0.8321959454704881,"samples":[141.3460026467039,142.3079865020875,114.72258904929407,142.53808662217696,141.94405426981095,140.79281051087037,111.9140947408769,142.45720240181134,140.15195976237234,142.03793097379827]},"checked":{"median":121.07117587985559,"mad":0.5278566332348547,"samples":[121.44138783218014,119.38195959517577,120.53935382082871,121.80128880988723,121.30331263152804,121.0121153215448,121.98524893377268,121.13023643816636,120.48102047424342,120.54728467241276]}}},{"key":"simd-aot","label":"SIMD AOT","source":"apple-m1-mech-backend-pairs-n10-2026-09-25.json","values":{"unchecked":{"median":37.059153390893584,"mad":0.08453453477176254,"samples":[37.00580194725714,35.68238126885224,36.993934513340825,37.135500510019895,37.1518753413108,37.11250483453003,36.808455370402136,37.18796681066932,37.11343745353572,36.695143627085606]},"checked":{"median":34.61659955143526,"mad":0.010267028789513688,"samples":[34.641627333538175,34.61564090426439,34.61328705684537,34.627335207970695,34.60680115039167,34.62262459236231,34.605755770145116,34.617558198606126,34.29748567276126,34.6836120760145]}}},{"key":"scalar-jit","label":"Scalar JIT","source":"apple-m1-mech-backend-pairs-n10-2026-09-25.json","values":{"unchecked":{"median":15.948693690507435,"mad":0.032873735724533226,"samples":[15.970489193459889,15.897644074231627,15.68643189085235,15.980435153241887,15.974050678621442,15.982320357220843,15.879842529097784,15.980814495243093,15.92689818755498,15.90943395626059]},"checked":{"median":13.224083710585894,"mad":0.0053554494577738865,"samples":[13.21602531987453,13.230390242373392,13.22333429530666,13.224833125865127,13.25392099011784,13.219679343457845,13.221980746763814,13.226292944533226,13.213348055231718,13.291288195047787]}}},{"key":"evaluator","label":"Interpreter","source":"apple-m1-mech-backend-pairs-n10-2026-09-25.json","values":{"unchecked":{"median":0.8278867294891764,"mad":0.0010342682130255465,"samples":[0.8254288291068161,0.8253607752653016,0.8277262381964557,0.8287030600235645,0.8280472207818971,0.8274474938954149,0.8252878258347235,0.8291389353808394,0.8284888884047696,0.8294064093323731]},"checked":{"median":0.695042081257192,"mad":0.00106619241415834,"samples":[0.6963119506784141,0.691474818202843,0.6914666541027176,0.6943103467182352,0.6954768079240193,0.6963518763439652,0.6945469192474181,0.6959045966642866,0.6963894080737908,0.6946073545903646]}}}]} Backend choice changes EKF throughput Apple M1 · same f32 source · 500,000 filters × 40 turns · median ± MAD unchecked (upper) checked (lower) GPU Metal GPU · 64-thread groups Metal GPU · 64-thread groups, unchecked: 420.7 million filter-turns per second; MAD 2.3; ten samples. 420.7 ± 2.3 Metal GPU · 64-thread groups, checked: 420.9 million filter-turns per second; MAD 2.3; ten samples. 420.9 ± 2.3 SIMD JIT · 8 CPU workers SIMD JIT · 8 CPU workers, unchecked: 141.6 million filter-turns per second; MAD 0.8; ten samples. 141.6 ± 0.8 SIMD JIT · 8 CPU workers, checked: 121.1 million filter-turns per second; MAD 0.5; ten samples. 121.1 ± 0.5 SIMD AOT · 1 CPU worker SIMD AOT · 1 CPU worker, unchecked: 37.1 million filter-turns per second; MAD <0.1; ten samples. 37.1 ± <0.1 SIMD AOT · 1 CPU worker, checked: 34.6 million filter-turns per second; MAD <0.1; ten samples. 34.6 ± <0.1 Scalar JIT · 1 CPU worker Scalar JIT · 1 CPU worker, unchecked: 15.9 million filter-turns per second; MAD <0.1; ten samples. 15.9 ± <0.1 Scalar JIT · 1 CPU worker, checked: 13.2 million filter-turns per second; MAD <0.1; ten samples. 13.2 ± <0.1 Interpreter · numeric instructions Interpreter · numeric instructions, unchecked: 0.83 million filter-turns per second; MAD <0.1; ten samples. 0.83 ± <0.1 Interpreter · numeric instructions, checked: 0.70 million filter-turns per second; MAD <0.1; ten samples. 0.70 ± <0.1 0.1 1 10 100 1000 Throughput (million filter-turns/s) Logarithmic scale 2026-09-25 matched campaign. Both modes publish state after every turn. Ten fresh processes per mode. JIT and AOT share the numerical lowering pipeline. Raw samples and provenance
Same archived bearing-only Mech source, per-turn publication, five execution targets. JIT and AOT share numerical lowering; worker count and host capability also determine available parallelism.

The second chart holds the Mech source and publication boundary fixed. It compares interpretation of lowered numerical instructions, scalar JIT, single-worker SIMD AOT, eight-worker SIMD JIT, and direct Metal execution. Seven bindings are live inputs. Each process performs five untimed turns followed by 40 timed turns in the same resident session, without resetting the state between them. Inputs stay resident and unchanged during measurement. Every turn publishes separately, including a worker or device synchronization where required.

Checked medians range from about 0.70 million filter-turns/s for the numerical instruction interpreter to 420.9 for Metal. SIMD AOT reaches 34.6 on one worker and SIMD JIT reaches 121.1 on eight. These rows differ in interpretation, specialization, parallelism, and device execution, so the chart uses a logarithmic axis. The source equations require no backend-specific annotations to select these measured paths.

Checking reduces SIMD AOT throughput by approximately 6.6% and eight-worker SIMD JIT throughput by approximately 14.5% in this campaign. The Metal checked and unchecked medians differ by less than either MAD, so this measurement does not resolve a checking cost there. Both modes retain per-turn publication. Before collection, component-wise numerical preflights compared all backends with the scalar reference, and each checked backend rejected an injected NaN without changing the published batch state.

Native Metal Implementations

Metal implementations use a common turn boundary Apple M1 GPU · 500,000 f32 filters × 40 turns · median ± MAD Unchecked bars are above checked bars. Diagonal hatching indicates GPU execution. Values and whiskers show the median and unscaled median absolute deviation from ten retained process trials per mode. No samples were removed. Chart uses a linear throughput axis. 2026-09-24 Metal campaign. Resident state; completed publication after every turn. Ten fresh processes per mode. Schedules and fault-status observation differ. {"units":"million filter-turns/s","summary":"median and unscaled MAD","archives":[{"filename":"apple-m1-metal-equal-n10-2026-09-24.json","sha256":"93d75dc4775769d57c30ad1a38f33bca79115fbc18cda8c7e8a6f6e844cde7e9"}],"rows":[{"key":"Mech generated MSL","label":"Mech","source":"apple-m1-metal-equal-n10-2026-09-24.json","values":{"unchecked":{"median":410.43899999999996,"mad":4.9205000000000325,"samples":[411.008,389.33,414.778,415.941,407.989,421.263,422.929,409.87,366.729,408.083]},"checked":{"median":409.7645,"mad":2.9540000000000077,"samples":[408.023,385.481,421.406,423.398,412.791,411.06,406.883,412.606,365.479,408.469]}}},{"key":"Rust + hand-written MSL","label":"Rust + MSL","source":"apple-m1-metal-equal-n10-2026-09-24.json","values":{"unchecked":{"median":422.8898836185,"mad":4.917061461000003,"samples":[423.881482737,399.146497062,425.492679037,408.785486873,431.83564957,424.828390041,425.447431896,391.941679078,421.8982845,415.658556115]},"checked":{"median":425.18029466,"mad":3.082746880000002,"samples":[427.369074522,422.818454969,431.858961082,426.954335974,428.983948729,427.18763965,423.406253346,394.49644324,415.699584884,416.976497578]}}},{"key":"Mojo native Metal","label":"Mojo","source":"apple-m1-metal-equal-n10-2026-09-24.json","values":{"unchecked":{"median":405.35473148445436,"mad":6.940728439549787,"samples":[410.5174572548698,410.72822113607424,370.9130023552976,387.1916986099818,412.8819157720892,411.7090040759191,403.1201499606958,381.5264874763931,364.99014526607783,407.5893130082129]},"checked":{"median":406.03841479598987,"mad":5.1646767421688935,"samples":[405.03868119405405,380.37999961962,413.97582381188937,401.72742794014255,412.05678142448033,420.35351730805604,405.53144896386715,377.90752602838086,406.54538062811264,407.67239446380887]}}},{"key":"Julia Metal.jl","label":"Julia","source":"apple-m1-metal-equal-n10-2026-09-24.json","values":{"unchecked":{"median":411.3983392233472,"mad":13.385864515612212,"samples":[423.8485417925364,393.68173878515876,425.7198656853824,418.69618301695846,419.3549766909924,406.8106861926032,389.89806855803874,391.63180735631397,380.68318355829336,415.98599225409123]},"checked":{"median":410.4195339578655,"mad":4.743528829557988,"samples":[413.83697873016564,383.6439759350109,376.9453939751272,408.8422354471329,418.212458327481,416.48914684468133,410.46024270801473,413.01905446887037,410.37882520771626,400.95326639084425]}}},{"key":"Taichi native Metal","label":"Taichi","source":"apple-m1-metal-equal-n10-2026-09-24.json","values":{"unchecked":{"median":410.77128661242654,"mad":3.7935077359948366,"samples":[410.7127609426498,417.7902001460351,413.4014425818153,404.4087343215743,418.0634773631382,410.82981228220325,409.03036751234566,384.2191586308377,407.9693381018964,415.5563535738861]},"checked":{"median":342.87227898074946,"mad":2.030316868525432,"samples":[342.2376965836366,321.8948842265505,344.1250016385122,324.4867850919482,343.50686137786226,342.0104224866613,345.2877176031121,340.1261743089119,346.97248076480395,344.51747409543765]}}},{"key":"Halide Metal schedule","label":"Halide","source":"apple-m1-metal-equal-n10-2026-09-24.json","values":{"unchecked":{"median":398.764873568587,"mad":10.40285446133106,"samples":[400.1313791370259,406.56466135491286,411.4097669460351,386.4612946936449,407.267003616307,405.5170595958009,372.8250922024415,380.1239496771389,368.5537620521457,397.39836800014814]},"checked":{"median":293.2910220515238,"mad":5.582957706063553,"samples":[292.90742679815025,299.9656944233573,276.8974093852189,283.72785431391816,294.8906508605277,297.78226509181735,278.79793815542433,278.88249330765683,293.67461730489725,294.8419262506583]}}}]} Metal implementations use a common turn boundary Apple M1 GPU · 500,000 f32 filters × 40 turns · median ± MAD unchecked (upper) checked (lower) GPU 0 100 200 300 400 500 Mech generated Metal Mech, generated Metal, unchecked: 410.4 million filter-turns per second; MAD 4.9; ten samples. 410.4 ± 4.9 Mech, generated Metal, checked: 409.8 million filter-turns per second; MAD 3.0; ten samples. 409.8 ± 3.0 Rust + MSL handwritten Metal Rust + MSL, handwritten Metal, unchecked: 422.9 million filter-turns per second; MAD 4.9; ten samples. 422.9 ± 4.9 Rust + MSL, handwritten Metal, checked: 425.2 million filter-turns per second; MAD 3.1; ten samples. 425.2 ± 3.1 Mojo native Metal Mojo, native Metal, unchecked: 405.4 million filter-turns per second; MAD 6.9; ten samples. 405.4 ± 6.9 Mojo, native Metal, checked: 406.0 million filter-turns per second; MAD 5.2; ten samples. 406.0 ± 5.2 Julia Metal.jl Julia, Metal.jl, unchecked: 411.4 million filter-turns per second; MAD 13.4; ten samples. 411.4 ± 13.4 Julia, Metal.jl, checked: 410.4 million filter-turns per second; MAD 4.7; ten samples. 410.4 ± 4.7 Taichi native Metal Taichi, native Metal, unchecked: 410.8 million filter-turns per second; MAD 3.8; ten samples. 410.8 ± 3.8 Taichi, native Metal, checked: 342.9 million filter-turns per second; MAD 2.0; ten samples. 342.9 ± 2.0 Halide Metal schedule Halide, Metal schedule, unchecked: 398.8 million filter-turns per second; MAD 10.4; ten samples. 398.8 ± 10.4 Halide, Metal schedule, checked: 293.3 million filter-turns per second; MAD 5.6; ten samples. 293.3 ± 5.6 Throughput (million filter-turns/s, linear scale) 2026-09-24 Metal campaign. Resident state; completed publication after every turn. Ten fresh processes per mode. Schedules and fault-status observation differ. Raw samples and provenance
Metal implementations use a common turn boundary Apple M1 GPU · 500,000 f32 filters × 40 turns · median ± MAD Unchecked bars are above checked bars. Diagonal hatching indicates GPU execution. Values and whiskers show the median and unscaled median absolute deviation from ten retained process trials per mode. No samples were removed. Chart uses a linear throughput axis. 2026-09-24 Metal campaign. Resident state; completed publication after every turn. Ten fresh processes per mode. Schedules and fault-status observation differ. {"units":"million filter-turns/s","summary":"median and unscaled MAD","archives":[{"filename":"apple-m1-metal-equal-n10-2026-09-24.json","sha256":"93d75dc4775769d57c30ad1a38f33bca79115fbc18cda8c7e8a6f6e844cde7e9"}],"rows":[{"key":"Mech generated MSL","label":"Mech","source":"apple-m1-metal-equal-n10-2026-09-24.json","values":{"unchecked":{"median":410.43899999999996,"mad":4.9205000000000325,"samples":[411.008,389.33,414.778,415.941,407.989,421.263,422.929,409.87,366.729,408.083]},"checked":{"median":409.7645,"mad":2.9540000000000077,"samples":[408.023,385.481,421.406,423.398,412.791,411.06,406.883,412.606,365.479,408.469]}}},{"key":"Rust + hand-written MSL","label":"Rust + MSL","source":"apple-m1-metal-equal-n10-2026-09-24.json","values":{"unchecked":{"median":422.8898836185,"mad":4.917061461000003,"samples":[423.881482737,399.146497062,425.492679037,408.785486873,431.83564957,424.828390041,425.447431896,391.941679078,421.8982845,415.658556115]},"checked":{"median":425.18029466,"mad":3.082746880000002,"samples":[427.369074522,422.818454969,431.858961082,426.954335974,428.983948729,427.18763965,423.406253346,394.49644324,415.699584884,416.976497578]}}},{"key":"Mojo native Metal","label":"Mojo","source":"apple-m1-metal-equal-n10-2026-09-24.json","values":{"unchecked":{"median":405.35473148445436,"mad":6.940728439549787,"samples":[410.5174572548698,410.72822113607424,370.9130023552976,387.1916986099818,412.8819157720892,411.7090040759191,403.1201499606958,381.5264874763931,364.99014526607783,407.5893130082129]},"checked":{"median":406.03841479598987,"mad":5.1646767421688935,"samples":[405.03868119405405,380.37999961962,413.97582381188937,401.72742794014255,412.05678142448033,420.35351730805604,405.53144896386715,377.90752602838086,406.54538062811264,407.67239446380887]}}},{"key":"Julia Metal.jl","label":"Julia","source":"apple-m1-metal-equal-n10-2026-09-24.json","values":{"unchecked":{"median":411.3983392233472,"mad":13.385864515612212,"samples":[423.8485417925364,393.68173878515876,425.7198656853824,418.69618301695846,419.3549766909924,406.8106861926032,389.89806855803874,391.63180735631397,380.68318355829336,415.98599225409123]},"checked":{"median":410.4195339578655,"mad":4.743528829557988,"samples":[413.83697873016564,383.6439759350109,376.9453939751272,408.8422354471329,418.212458327481,416.48914684468133,410.46024270801473,413.01905446887037,410.37882520771626,400.95326639084425]}}},{"key":"Taichi native Metal","label":"Taichi","source":"apple-m1-metal-equal-n10-2026-09-24.json","values":{"unchecked":{"median":410.77128661242654,"mad":3.7935077359948366,"samples":[410.7127609426498,417.7902001460351,413.4014425818153,404.4087343215743,418.0634773631382,410.82981228220325,409.03036751234566,384.2191586308377,407.9693381018964,415.5563535738861]},"checked":{"median":342.87227898074946,"mad":2.030316868525432,"samples":[342.2376965836366,321.8948842265505,344.1250016385122,324.4867850919482,343.50686137786226,342.0104224866613,345.2877176031121,340.1261743089119,346.97248076480395,344.51747409543765]}}},{"key":"Halide Metal schedule","label":"Halide","source":"apple-m1-metal-equal-n10-2026-09-24.json","values":{"unchecked":{"median":398.764873568587,"mad":10.40285446133106,"samples":[400.1313791370259,406.56466135491286,411.4097669460351,386.4612946936449,407.267003616307,405.5170595958009,372.8250922024415,380.1239496771389,368.5537620521457,397.39836800014814]},"checked":{"median":293.2910220515238,"mad":5.582957706063553,"samples":[292.90742679815025,299.9656944233573,276.8974093852189,283.72785431391816,294.8906508605277,297.78226509181735,278.79793815542433,278.88249330765683,293.67461730489725,294.8419262506583]}}}]} Metal implementations use a common turn boundary Apple M1 GPU · 500,000 f32 filters × 40 turns · median ± MAD unchecked (upper) checked (lower) GPU Mech · generated Metal Mech · generated Metal, unchecked: 410.4 million filter-turns per second; MAD 4.9; ten samples. 410.4 ± 4.9 Mech · generated Metal, checked: 409.8 million filter-turns per second; MAD 3.0; ten samples. 409.8 ± 3.0 Rust + MSL · handwritten Metal Rust + MSL · handwritten Metal, unchecked: 422.9 million filter-turns per second; MAD 4.9; ten samples. 422.9 ± 4.9 Rust + MSL · handwritten Metal, checked: 425.2 million filter-turns per second; MAD 3.1; ten samples. 425.2 ± 3.1 Mojo · native Metal Mojo · native Metal, unchecked: 405.4 million filter-turns per second; MAD 6.9; ten samples. 405.4 ± 6.9 Mojo · native Metal, checked: 406.0 million filter-turns per second; MAD 5.2; ten samples. 406.0 ± 5.2 Julia · Metal.jl Julia · Metal.jl, unchecked: 411.4 million filter-turns per second; MAD 13.4; ten samples. 411.4 ± 13.4 Julia · Metal.jl, checked: 410.4 million filter-turns per second; MAD 4.7; ten samples. 410.4 ± 4.7 Taichi · native Metal Taichi · native Metal, unchecked: 410.8 million filter-turns per second; MAD 3.8; ten samples. 410.8 ± 3.8 Taichi · native Metal, checked: 342.9 million filter-turns per second; MAD 2.0; ten samples. 342.9 ± 2.0 Halide · Metal schedule Halide · Metal schedule, unchecked: 398.8 million filter-turns per second; MAD 10.4; ten samples. 398.8 ± 10.4 Halide · Metal schedule, checked: 293.3 million filter-turns per second; MAD 5.6; ten samples. 293.3 ± 5.6 0 100 200 300 400 500 Throughput (million filter-turns/s) Linear scale 2026-09-24 Metal campaign. Resident state; completed publication after every turn. Ten fresh processes per mode. Schedules and fault-status observation differ. Raw samples and provenance
Cross-system native Metal campaign, collected September 24. GPU hatching distinguishes device measurements from CPU measurements.

The six-system Metal comparison uses resident component-major f32 state, ping-pong publication, and a completed GPU submission after every turn. Rust supplies a host for a hand-written Metal Shading Language kernel, so its label is Rust + MSL rather than Rust alone. Mech generates its shader from the numerical graph; Julia uses Metal.jl, while Mojo, Taichi, and Halide use their respective native Metal paths. Futhark is included in the CPU comparison, not this Metal comparison.

Rust + MSL measures 425.2 ± 3.1 million checked filter-turns/s, Julia 410.4 ± 4.7, Mech 409.8 ± 3.0, and Mojo 406.0 ± 5.2. These retained results place generated Mech execution near the hand-written Metal control for this workload. They come from the earlier cross-system campaign; the 420.9 Mech result in the five-backend chart belongs to a separate collection and must not be substituted into this one.

Matching the GPU layout and host protocol materially changed earlier comparisons. The Julia and Mojo paths now use packed resident state and compact fault status instead of observing a large host-side result each turn. Taichi adds a compact cumulative fault count to the same publication protocol. Halide uses a resident per-lane fault plane because its measured interface does not expose the same device-wide compact status mechanism. Its checked result therefore includes that observation cost. Launch schedules and transcendental lowering also differ.4

CPU and GPU Execution from One Application Source

One application source targets CPU and Metal Apple M1 · 500,000 f32 filters × 40 turns · per-turn publication · median ± MAD Unchecked bars are above checked bars. Diagonal hatching indicates GPU execution. Values and whiskers show the median and unscaled median absolute deviation from ten retained process trials per mode. No samples were removed. CPU · eight workers uses a linear throughput axis. Metal GPU uses a linear throughput axis. 2026-09-24 campaigns. Each system selects CPU and Metal from one application source. Ten process trials per mode. Panel scales differ; schedules and fault transport differ. {"units":"million filter-turns/s","summary":"median and unscaled MAD","archives":[{"filename":"apple-m1-cpu-equal-n10-2026-09-24.json","sha256":"c64ec81408bd951ff4808e6a51484500c9d1decc2a594c448c6d5b6cfb5e81d3"},{"filename":"apple-m1-metal-equal-n10-2026-09-24.json","sha256":"93d75dc4775769d57c30ad1a38f33bca79115fbc18cda8c7e8a6f6e844cde7e9"}],"rows":[{"key":"Mech per-turn SIMD/JIT","label":"Mech","source":"apple-m1-cpu-equal-n10-2026-09-24.json","values":{"unchecked":{"median":166.40699999999998,"mad":4.1340000000000146,"samples":[158.539,167.328,170.726,169.721,169.515,170.356,165.486,116.514,160.3,148.935]},"checked":{"median":141.36200000000002,"mad":3.3785000000000025,"samples":[144.715,137.958,142.785,144.882,142.202,140.74,141.984,122.781,136.632,125.425]}}},{"key":"Taichi LLVM CPU","label":"Taichi","source":"apple-m1-cpu-equal-n10-2026-09-24.json","values":{"unchecked":{"median":95.36294275034837,"mad":0.31645622513980953,"samples":[95.44544313039971,95.5851603609617,95.72383679878901,96.24870668737262,95.25036083223799,95.28044237029702,95.63496115218734,91.38729908413143,92.47786959200346,84.09686279140014]},"checked":{"median":88.3435117148239,"mad":0.42140583062695214,"samples":[88.2376946834078,87.97592706312007,88.81873872437397,87.67786278358169,89.43880478277183,88.45795120250094,88.44932874624001,88.55227575679763,87.5539985575007,77.47457430612607]}}},{"key":"Halide native CPU","label":"Halide","source":"apple-m1-cpu-equal-n10-2026-09-24.json","values":{"unchecked":{"median":23.489834406479122,"mad":0.09191674144838125,"samples":[23.559780793344117,23.577905969542652,23.39407248664589,23.556706366096837,23.568680180495363,23.42296244686141,21.197623322473085,20.43290689005283,20.835269450747045,26.093715338576317]},"checked":{"median":22.525793553789846,"mad":0.6485402816828056,"samples":[23.383487359429232,22.734006824269162,22.907054055493486,22.317580283310527,22.990651752698312,23.092894922420864,20.61251903920411,21.795814359055253,20.37832010965631,20.1774098763377]}}},{"key":"Mech generated MSL","label":"Mech","source":"apple-m1-metal-equal-n10-2026-09-24.json","values":{"unchecked":{"median":410.43899999999996,"mad":4.9205000000000325,"samples":[411.008,389.33,414.778,415.941,407.989,421.263,422.929,409.87,366.729,408.083]},"checked":{"median":409.7645,"mad":2.9540000000000077,"samples":[408.023,385.481,421.406,423.398,412.791,411.06,406.883,412.606,365.479,408.469]}}},{"key":"Taichi native Metal","label":"Taichi","source":"apple-m1-metal-equal-n10-2026-09-24.json","values":{"unchecked":{"median":410.77128661242654,"mad":3.7935077359948366,"samples":[410.7127609426498,417.7902001460351,413.4014425818153,404.4087343215743,418.0634773631382,410.82981228220325,409.03036751234566,384.2191586308377,407.9693381018964,415.5563535738861]},"checked":{"median":342.87227898074946,"mad":2.030316868525432,"samples":[342.2376965836366,321.8948842265505,344.1250016385122,324.4867850919482,343.50686137786226,342.0104224866613,345.2877176031121,340.1261743089119,346.97248076480395,344.51747409543765]}}},{"key":"Halide Metal schedule","label":"Halide","source":"apple-m1-metal-equal-n10-2026-09-24.json","values":{"unchecked":{"median":398.764873568587,"mad":10.40285446133106,"samples":[400.1313791370259,406.56466135491286,411.4097669460351,386.4612946936449,407.267003616307,405.5170595958009,372.8250922024415,380.1239496771389,368.5537620521457,397.39836800014814]},"checked":{"median":293.2910220515238,"mad":5.582957706063553,"samples":[292.90742679815025,299.9656944233573,276.8974093852189,283.72785431391816,294.8906508605277,297.78226509181735,278.79793815542433,278.88249330765683,293.67461730489725,294.8419262506583]}}}]} One application source targets CPU and Metal Apple M1 · 500,000 f32 filters × 40 turns · per-turn publication · median ± MAD unchecked (upper) checked (lower) GPU CPU · eight workers 0 50 100 150 200 Mech SIMD/JIT Mech, SIMD/JIT, unchecked: 166.4 million filter-turns per second; MAD 4.1; ten samples. 166.4 ± 4.1 Mech, SIMD/JIT, checked: 141.4 million filter-turns per second; MAD 3.4; ten samples. 141.4 ± 3.4 Taichi LLVM CPU Taichi, LLVM CPU, unchecked: 95.4 million filter-turns per second; MAD 0.3; ten samples. 95.4 ± 0.3 Taichi, LLVM CPU, checked: 88.3 million filter-turns per second; MAD 0.4; ten samples. 88.3 ± 0.4 Halide native CPU Halide, native CPU, unchecked: 23.5 million filter-turns per second; MAD <0.1; ten samples. 23.5 ± <0.1 Halide, native CPU, checked: 22.5 million filter-turns per second; MAD 0.6; ten samples. 22.5 ± 0.6 Throughput (million filter-turns/s, linear scale) Metal GPU 0 100 200 300 400 500 Mech Mech, unchecked: 410.4 million filter-turns per second; MAD 4.9; ten samples. 410.4 ± 4.9 Mech, checked: 409.8 million filter-turns per second; MAD 3.0; ten samples. 409.8 ± 3.0 Taichi Taichi, unchecked: 410.8 million filter-turns per second; MAD 3.8; ten samples. 410.8 ± 3.8 Taichi, checked: 342.9 million filter-turns per second; MAD 2.0; ten samples. 342.9 ± 2.0 Halide Halide, unchecked: 398.8 million filter-turns per second; MAD 10.4; ten samples. 398.8 ± 10.4 Halide, checked: 293.3 million filter-turns per second; MAD 5.6; ten samples. 293.3 ± 5.6 Throughput (million filter-turns/s, linear scale) 2026-09-24 campaigns. Each system selects CPU and Metal from one application source. Ten process trials per mode. Panel scales differ; schedules and fault transport differ. Raw CPU and Metal records: CPU Raw CPU and Metal records: Metal
One application source targets CPU and Metal Apple M1 · 500,000 f32 filters × 40 turns · per-turn publication · median ± MAD Unchecked bars are above checked bars. Diagonal hatching indicates GPU execution. Values and whiskers show the median and unscaled median absolute deviation from ten retained process trials per mode. No samples were removed. CPU · eight workers uses a linear throughput axis. Metal GPU uses a linear throughput axis. 2026-09-24 campaigns. Each system selects CPU and Metal from one application source. Ten process trials per mode. Panel scales differ; schedules and fault transport differ. {"units":"million filter-turns/s","summary":"median and unscaled MAD","archives":[{"filename":"apple-m1-cpu-equal-n10-2026-09-24.json","sha256":"c64ec81408bd951ff4808e6a51484500c9d1decc2a594c448c6d5b6cfb5e81d3"},{"filename":"apple-m1-metal-equal-n10-2026-09-24.json","sha256":"93d75dc4775769d57c30ad1a38f33bca79115fbc18cda8c7e8a6f6e844cde7e9"}],"rows":[{"key":"Mech per-turn SIMD/JIT","label":"Mech","source":"apple-m1-cpu-equal-n10-2026-09-24.json","values":{"unchecked":{"median":166.40699999999998,"mad":4.1340000000000146,"samples":[158.539,167.328,170.726,169.721,169.515,170.356,165.486,116.514,160.3,148.935]},"checked":{"median":141.36200000000002,"mad":3.3785000000000025,"samples":[144.715,137.958,142.785,144.882,142.202,140.74,141.984,122.781,136.632,125.425]}}},{"key":"Taichi LLVM CPU","label":"Taichi","source":"apple-m1-cpu-equal-n10-2026-09-24.json","values":{"unchecked":{"median":95.36294275034837,"mad":0.31645622513980953,"samples":[95.44544313039971,95.5851603609617,95.72383679878901,96.24870668737262,95.25036083223799,95.28044237029702,95.63496115218734,91.38729908413143,92.47786959200346,84.09686279140014]},"checked":{"median":88.3435117148239,"mad":0.42140583062695214,"samples":[88.2376946834078,87.97592706312007,88.81873872437397,87.67786278358169,89.43880478277183,88.45795120250094,88.44932874624001,88.55227575679763,87.5539985575007,77.47457430612607]}}},{"key":"Halide native CPU","label":"Halide","source":"apple-m1-cpu-equal-n10-2026-09-24.json","values":{"unchecked":{"median":23.489834406479122,"mad":0.09191674144838125,"samples":[23.559780793344117,23.577905969542652,23.39407248664589,23.556706366096837,23.568680180495363,23.42296244686141,21.197623322473085,20.43290689005283,20.835269450747045,26.093715338576317]},"checked":{"median":22.525793553789846,"mad":0.6485402816828056,"samples":[23.383487359429232,22.734006824269162,22.907054055493486,22.317580283310527,22.990651752698312,23.092894922420864,20.61251903920411,21.795814359055253,20.37832010965631,20.1774098763377]}}},{"key":"Mech generated MSL","label":"Mech","source":"apple-m1-metal-equal-n10-2026-09-24.json","values":{"unchecked":{"median":410.43899999999996,"mad":4.9205000000000325,"samples":[411.008,389.33,414.778,415.941,407.989,421.263,422.929,409.87,366.729,408.083]},"checked":{"median":409.7645,"mad":2.9540000000000077,"samples":[408.023,385.481,421.406,423.398,412.791,411.06,406.883,412.606,365.479,408.469]}}},{"key":"Taichi native Metal","label":"Taichi","source":"apple-m1-metal-equal-n10-2026-09-24.json","values":{"unchecked":{"median":410.77128661242654,"mad":3.7935077359948366,"samples":[410.7127609426498,417.7902001460351,413.4014425818153,404.4087343215743,418.0634773631382,410.82981228220325,409.03036751234566,384.2191586308377,407.9693381018964,415.5563535738861]},"checked":{"median":342.87227898074946,"mad":2.030316868525432,"samples":[342.2376965836366,321.8948842265505,344.1250016385122,324.4867850919482,343.50686137786226,342.0104224866613,345.2877176031121,340.1261743089119,346.97248076480395,344.51747409543765]}}},{"key":"Halide Metal schedule","label":"Halide","source":"apple-m1-metal-equal-n10-2026-09-24.json","values":{"unchecked":{"median":398.764873568587,"mad":10.40285446133106,"samples":[400.1313791370259,406.56466135491286,411.4097669460351,386.4612946936449,407.267003616307,405.5170595958009,372.8250922024415,380.1239496771389,368.5537620521457,397.39836800014814]},"checked":{"median":293.2910220515238,"mad":5.582957706063553,"samples":[292.90742679815025,299.9656944233573,276.8974093852189,283.72785431391816,294.8906508605277,297.78226509181735,278.79793815542433,278.88249330765683,293.67461730489725,294.8419262506583]}}}]} One application source targets CPU and Metal Apple M1 · 500,000 f32 filters × 40 turns · per-turn publication · median ± MAD unchecked (upper) checked (lower) GPU CPU · eight workers Mech · SIMD/JIT Mech · SIMD/JIT, unchecked: 166.4 million filter-turns per second; MAD 4.1; ten samples. 166.4 ± 4.1 Mech · SIMD/JIT, checked: 141.4 million filter-turns per second; MAD 3.4; ten samples. 141.4 ± 3.4 Taichi · LLVM CPU Taichi · LLVM CPU, unchecked: 95.4 million filter-turns per second; MAD 0.3; ten samples. 95.4 ± 0.3 Taichi · LLVM CPU, checked: 88.3 million filter-turns per second; MAD 0.4; ten samples. 88.3 ± 0.4 Halide · native CPU Halide · native CPU, unchecked: 23.5 million filter-turns per second; MAD <0.1; ten samples. 23.5 ± <0.1 Halide · native CPU, checked: 22.5 million filter-turns per second; MAD 0.6; ten samples. 22.5 ± 0.6 0 50 100 150 200 Throughput (million filter-turns/s) Linear scale Metal GPU Mech Mech, unchecked: 410.4 million filter-turns per second; MAD 4.9; ten samples. 410.4 ± 4.9 Mech, checked: 409.8 million filter-turns per second; MAD 3.0; ten samples. 409.8 ± 3.0 Taichi Taichi, unchecked: 410.8 million filter-turns per second; MAD 3.8; ten samples. 410.8 ± 3.8 Taichi, checked: 342.9 million filter-turns per second; MAD 2.0; ten samples. 342.9 ± 2.0 Halide Halide, unchecked: 398.8 million filter-turns per second; MAD 10.4; ten samples. 398.8 ± 10.4 Halide, checked: 293.3 million filter-turns per second; MAD 5.6; ten samples. 293.3 ± 5.6 0 100 200 300 400 500 Throughput (million filter-turns/s) Linear scale 2026-09-24 campaigns. Each system selects CPU and Metal from one application source. Ten process trials per mode. Panel scales differ; schedules and fault transport differ. Raw CPU and Metal records: CPU Raw CPU and Metal records: Metal
Application-source reuse in Mech, Taichi, and Halide. Each system selects CPU or Metal using its own backend options and schedules.

Mech, Taichi, and Halide additionally select CPU and Metal execution from one application source in their respective systems. Backend options and schedules change while the equations and publication contract are retained. In this archived comparison, Mech has the highest CPU and checked-Metal medians. Unchecked Metal places Taichi and Mech within 0.08%, far below either MAD. The useful result is that all three demonstrate this source reuse, with different measured execution costs; it is not evidence that Mech is universally faster on CPU or GPU.

Status and Future Work

This work presents a snapshot of Mech v0.4-beta. Many features are implemented and working, but further validation and hardening are needed. Research-hardware deployments have been limited. Over the next year we plan to test on a wider range of robot platforms, collect deployment feedback, and extend host integrations, standard-library coverage, and language features. The development plan targets a fuller v0.4 release in 2027 and a v1.0 release candidate in 2028; these are planned milestones rather than completed validation claims.

The EKF results motivate that work by connecting compact numerical source with usable Rust embedding, explicit validation boundaries, and several execution strategies. Future evaluations should include other numerical kernels, changing live inputs, complete robot workloads, and deployment costs beyond steady-state throughput. The source, raw measurements, and reproduction instructions are public. Contributions, independently optimized comparison implementations, and bug reports are welcome through mech-lang/mech.

More about Mech

The poster concentrates on embedded numerical kernels, but Mech is a functional language with user-defined types, pattern matching, and first-class state machines. The following examples extend the case study with facilities introduced in the version 0.3 article.[14] They run in this document's Rust interpreter compiled to WebAssembly. The numerical GPU backend supports a more restricted set of operations.

State Machines and Named Events

The behavior around the filter distinguishes paused operation, active patrol, and a latched fault. Modes and events are named atoms with declared types, so the host interface carries their meaning directly. Dispatch patterns match those values; a wildcard ignores an argument that does not affect a transition.

--

Robot behavior

mode:=:paused|:patrol|:faultevent:=:pause|:run|:rejected|:reset
#Robot(mode mode , event event ) ⇒ mode :=
├
:Dispatch(mode mode , event event )
├
:Paused()
├
:Patrol()
└
:Fault()
.
# Robot ( mode, event ) → :Dispatch(mode, event)
:Dispatch(*, :reset) → :Paused
:Dispatch(*, :rejected) → :Fault
:Dispatch(:fault, *) → :Fault
:Dispatch(*, :pause) → :Paused
:Dispatch(*, :run) → :Patrol
:Paused ⇒ :paused
:Patrol ⇒ :patrol
:Fault ⇒ :fault
.
--

Example invocation: run from paused.

#Robot(:paused, :run)

Download the complete behavior.mec.

The browser host supplies the current mode and an event to this Mech state machine. Its result determines whether JavaScript schedules another measurement update or stops the simulation. Reset takes precedence over a fault, while a run event cannot resume a faulted episode. The last accepted EKF state stays visible until reset. This behavior policy is separate from the kernel's rollback mechanism and can be changed independently.

Functions and Pattern Matching

A user-defined function can package a numerical expression without changing its matrix notation. This example wraps an angle and calls the function on a heading; adding a scalar to the heading vector demonstrates broadcasting. Further calls can be entered in the REPL.

--

Heading normalization

+> math/*
wrap-angle(theta f64 ) = wrapped f64 :=
wrapped:=atan2(sin(theta), cos(theta)).
headings:=[
0.0
3.0
7.0
]
+0.25
wrap-angle(headings[3])

Download the complete functions.mec.

Pattern matching can also select behavior from structured observations. Here a match arm binds the distance and checks its guard before choosing correction or prediction. A different distance or an occluded observation exercises the other branch.

--

Sensor decision

reading:=("visible",2.4)decision:=reading?
├ ("visible", distance), distance≤3.6 ⇒ "correct"
├ ("visible", distance) ⇒ "predict"
├ ("occluded", *) ⇒ "predict"
└ * ⇒ "predict".
decision

Download the complete matching.mec.

Literate Documents

This article is itself a Mechdown document: prose explains the code, and executable blocks supply results alongside it. Download the complete .mec document to inspect the prose and expanded examples together. The live camera EKF blocks share a namespace and compile as one numerical kernel. The bearing-only reference listing has a separate namespace, while the language examples execute in the document REPL.

The Output pane presents the running EKF application. Its camera simulation and drawing tables are written in Mech, while the browser host supplies UI events and measured filter state. The article shows selected scene rows alongside the numerical kernel; the source link provides the complete supporting program. Mechdown also supports hidden executable blocks, so a document can present selected calculations without displaying every helper.