Focused work to make RSScript viable as the runtime for the (already-built) ML framework. Driven by two measured findings:
- VM is fast on small loads but slow on big matrix calculations.
- AOT-compiled code runs fast, but the build takes ~3 minutes.
Both findings trace to one missing layer: there are no native tensor
kernels in the tree (no matmul/gemm/ndarray in crates/runtime/src or
stdlib). So the framework's heavy math (matmul, large reductions) is written in
RSScript and:
- Interpreted element-by-element by the reg-VM → an n×n matmul is n³ bytecode multiply-adds → fine on small inputs, falls off a cliff on big matrices.
- The only way to get speed is to AOT-compile the whole port → the ~3-min build is on the dev hot path.
The fix is one layer that removes both: back the hot ops with native kernels so the VM orchestrates and native code computes (like Python→C++/CUDA in real frameworks). Both backends call the same kernel ⇒ VM↔compiled parity is automatic.
Mechanism: a packed buffer value (Vec<f32> + shape/strides + dtype) plus a
family of runtime intrinsics wired the same way as existing native intrinsics
(runtime_abi maps Ns.method → rsscript_runtime::fn, dispatched in the reg-VM
and lowered by rust_lower). The framework's existing RSScript Tensor API is
re-pointed at these kernels — additive, the surface stays the same.
- Slice 1 —
matmulkernel (the headline). DONE: nativeRssTensor(Rc<Vec<f32>>+ shape) runtime-backed value type mirroringChannel, working in both reg-VM and AOT; hand-writtenikjf32 matmul (no new deps — optimizedmatrixmultiply/rayondeferred to P3) +from/to_f32_slice+shape/rank. Both backends call the identicalrsscript_runtime::tensor_*kernels, so parity is bit-identical. (packages/ml/interface/tensor.rssi.) - Slice 2 — elementwise kernels. DONE:
Tensor.add/sub/mul/div(same-shape) andneg/exp/log/sqrt/reluas native intrinsics, parity-tested (incl. 256×256 bulk). Broadcasting handled viabroadcast_to(slice 4). - Slice 3 — reductions. DONE:
sum_all(global→scalar) +sum_axis/max_axis/mean_axis/argmax_axisover an axis, native, parity-tested. - Slice 4 — movement ops. DONE:
reshape(true zero-copy viaRc::clone)transpose/permute/broadcast_to(materializing, contiguous-preserving).RssTensordeliberately stays row-major contiguous (nostridesfield) so the matmul/elementwise/reduce kernels stay simple; true zero-copy strided views are a future optimization. Parity-tested.
- Slice 5 — no per-op marshaling. SATISFIED BY DESIGN (slice 1): tensors
flow as
VmValue::Nativehandles (an id into the executor'stensorsmap), and buffers areRc<Vec<f32>>. A chain of ops passes handles +Rc-clones — the full buffer is only converted to/fromVmValueat thefrom_f32_slice/to_f32_sliceboundary, never per-op.
Why this is the priority: it removes the big-matrix VM cliff directly, and it demotes the 3-min AOT off the dev hot loop (you iterate on the now-fast VM; AOT becomes a ship/verify step).
Confirmed: the generated Cargo.toml has no profile tuning (only
[profile.release] overflow-checks = true).
- Tuned generated build profile. DONE: generated
Cargo.tomlnow sets[profile.release]codegen-units=256, lto=false, incremental=true(keepingoverflow-checks=true) and a[profile.dev]opt-level=1, incremental=true, codegen-units=256. (crates/rsscript/src/rust_lower/mod.rs.) - Per-module split of the generated package. The port lowers to one huge
generated
lib.rs; any edit recompiles the whole crate. Emit one module/file per source unit so rustc's incremental units are small → a one-file edit recompiles one module, not everything. - (optional) Cranelift codegen backend for dev builds — 2–5× faster debug compiles; nightly/component dependency, evaluate.
- Done: unchanged-source run cache (
fix/aotcache) — repeatedrss runof unchanged source skips re-lowering (~46× on a warm run).
- Parallelism + SIMD in kernels (
rayonover rows/tiles; explicit SIMD or rely on autovectorization) once the kernel layer exists. - Kernel fusion — fuse elementwise chains into single loops, and eventually a fused-kernel codegen/JIT (tinygrad-style scheduler). This is the real long-term AOT story for ML: generate fast kernels, not compile the crate faster.
- GPU/accelerator backend for kernels (far future).
List.fold/mapnumeric recognizer extensions in the reg-VM. They speed up boxed-List<Float>closures, but ML tensor math should go through the native Tensor kernels above, not per-element list closures. The existingList.foldfast path stays (harmless), but don't invest further there.
- Hold VM↔compiled parity (
vm_eval_parity): both tiers call the same kernel, so results must be bit-identical. - Add a benchmark proving the win (big matmul / big elementwise, VM before vs after) — a perf claim without a measurement doesn't land.
- Build/test in the Docker dev container; merge each slice only when green.