Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
rlx-opscope
A data-pattern recording harness. Run existing op graphs with varied data, record cheap sketches of every tensor flowing through the ops we care about (matmuls first), then mine those sketches offline for exploitable structure — sparsity, per-channel outliers, quantization headroom, low rank, sequence structure — that justifies building a specialized kernel to cut compute.
This is the MVP slice of a larger loop:
drive data ──▶ inject stat taps ──▶ record sketches ──▶ mine patterns ──▶ (specialize + guard)
The load-bearing idea: rewrite the graph, don't tap the executor
rlx has two CPU execution paths and opaque GPU backends, so a per-op host
callback can only see CPU intermediates on the slow path. Instead we rewrite
the graph: for each op-site, [inject_matmul_stats] appends reduction /
histogram nodes on the op's lhs, rhs, and out tensors and marks them as
extra graph outputs. The stats are then computed by the backend's own kernels
— so this works identically on CPU / Metal / CUDA / …, reuses peak kernels, and
costs nothing when the pass isn't applied. The primary output keeps index 0, so
the injected graph is a drop-in (a correctness gate in opscope-sweep asserts
injected_output == matmul_output).
This is exactly why Op::Histogram was added to the core: value histograms are
part of the sketch set, native O(n) on CPU and decomposing to
Compare + Reduce::Sum everywhere else.
Sketches recorded per tensor
| sketch | how | reveals |
|---|---|---|
min/max/mean |
Reduce |
range, bias |
l1 / sumsq |
abs/x² → Reduce::Sum |
magnitude, L2 |
nnz |
(x!=0) → Reduce::Sum |
density → sparse GEMM |
hist |
Op::Histogram (32 bins) |
distribution shape → quantize/LUT |
chan_maxabs |
`max | x |
pos_sumsq |
x² summed over last axis |
per-position energy → sequence structure |
adj_sumsq |
Σ(rowₜ − rowₜ₋₁)² (Narrow+Sub+Reduce) |
adjacent-row coherence → delta-compute |
The sequence/temporal tracking has three axes: per-channel (feature) outliers,
per-position (token) energy, and adjacent-row coherence within a call; plus a
step-indexed time series across calls (the miner's cross-step section
separates a stationary weight — precompute/prepack — from drifting activations).
Usage
# Synthetic sweep (shapes × 7 distributions) → tidy CSV, then mine it
# Decode-like stepped sequence (fixed weight, drifting activations) → temporal
# Real MNIST MLP on real MNIST pixels (reads ~/.cache/torchvision-mnist)
# Recurring op-subsequence motifs (linear chains) on a synthetic MLP stack
# Repeated DATAFLOW sub-DAGs (branching cones) → decomposition/fusion candidates
# Same analysis on a graph dumped from ANOTHER workspace (real rlx-models model):
# the model repo walks its graph → edge-list (no opscope dep); opscope loads it.
The CSV is tidy/long
(run_id,step,backend,dist,M,K,N,numel,site,role,stat,idx,value) so scalars,
per-channel vectors, and histograms share one flat schema that pandas/polars can
groupby. numel is each tapped tensor's own element count, so multi-site
graphs (different shapes per matmul) work without decoding M/K/N. No Arrow/Parquet
dependency yet — a drop-in swap for the [Recorder] sink later.
Real MNIST result (what the harness actually finds)
Running opscope-mnist (untrained MLP, real pixels) the miner reports, per site:
mnist fc1 lhs density 0.188 → sparse-GEMM (skip 81% zeros) # MNIST border sparsity
mnist fc2 lhs density 0.500 → sparse-GEMM (skip 50% zeros) # post-ReLU activation sparsity
... fc1/rhs, fc2/rhs → STATIONARY across steps → precompute/prepack (weights fixed)
... all activations → drifting across steps → temporal coherence
Both sparsities are real and training-independent; the stationarity falls out of
the fixed weights streamed across decode steps. (The histogram is scale-
normalized by default — x / (max|x| + eps) over [-1,1] — so tightly-scaled
weights no longer false-flag as "spiky/quantizable".)
Dataflow decomposition (repeated patterns → fusion candidates)
opscope-flow/opscope-graph mine repeated dataflow sub-DAGs (each node's
branching input cone, merkle-hashed) — the units that can be decomposed once and
shared or fused. On a transformer stack the repeated attention cone (with
Softmax) recurs per layer → FusedAttentionBlock; the FFN cone (Silu) →
FusedMatMulBiasAct. On a MoE stack the GroupedMatMul+TopK-gating expert
block recurs → fuse grouped-matmul + gating. opscope-graph runs this on a
graph dumped from another workspace (a real rlx-models model), so no shared
build or runtime dependency is needed — the model repo just walks its graph.
Inference-optimization tiers
Beyond the value sketches, opscope mines four tiers of signal, each pointing at a specific kernel:
- Tier 1 — inference dynamics (
opscope-infer): tapsSoftmax(per-query peak / per-key received attention mass → sparse/windowed attention, KV eviction) andTopK(per-expert load → drop cold / prefetch hot experts). These only exist at run time.inject_infer_stats. - Tier 2 — host probes (
opscope-probe,probe.rs): on a deep-dumped tensor, the signals the CSV sketches can't see — effective rank (→ factored matmul), 2:4 feasibility (→ sparse tensor cores), quant error @ bits (→ int kernel), value cardinality (→ LUT).save_tensor/load_tensorcapture. - Tier 3 — workload/roofline (
opscope-shapes,shapes.rs): per-op FLOPs/bytes/intensity → compute- vs memory-bound split (fusion strategy) + a hot-GEMM-shape histogram (autotune/dispatch-table targets). - Tier 4 — actuation (
opscope-plan,plan.rs): mined CSV → per-site exploit + a synthesized runtime guard predicate + guard stability (cross-step σ) + estimated and measured (A/B micro-bench) speedup, plus stationary-weight prepack items. This is the half that makes mining decide. - Cross-cutting (
online.rs): reservoir / t-digest-lite quantiles / HLL cardinality — bounded one-pass sketches for a sampled production-inference recorder (near-zero overhead), deterministic, unit-tested.
Scope / what's next
- Now: matmul taps + CPU exec on synthetic + real (MNIST) data; tidy-CSV; rule-based miner; within-call adjacency + cross-call time-series; adaptive (scale-normalized) histogram; linear op-subsequence motifs; repeated dataflow sub-DAG mining (transformer/MoE fusion candidates) + a dump-loader bridge to profile real rlx-models graphs structurally.
- Live on a real rlx-models model:
rlx-vision-bench/examples/opscope_mnist.rsruns opscope on the realbuild_eval_graphMLP + real MNIST via link-local (opscope added as a dev-dep, likerlx-collectives). Two cross-workspace gotchas: Cargo finds the link-local[patch]in.cargo/config.tomlfrom the current dir, socd rlx-modelsfirst (don't use--manifest-pathfrom elsewhere); and aCargo.tomlchange stales the lock, so the first build may fail — retry orcargo update. - Next: the same driver on real LLM/MoE runners (not just the MLP); Parquet; an effective-rank probe (SVD — not sketch-observable today); and the specialization + runtime-guard half of the loop.
Low rank is intentionally flagged as not sketch-observable by the miner (these reductions can't see singular values) — it's a deep-dump candidate, which is the honest signal the miner should give.
License
MIT OR Apache-2.0.