Repository navigation
perf(cuda): audit L=1 depthwise conv1d dispatch across SSM/hybrid decode paths and apply the #751 elementwise step where confirmed #752
Description
Summary
Issue #748 / PR #751 found that MLX 0.32.1's CUDA backend dispatches a single-output-position (L=1) bf16 depthwise conv1d to cuDNN's generic per-channel engine (convolve_common_engine_float_NHWC, one kernel launch per channel). On lfm2-350m-8bit that was ~1024 launches per conv op, 88.6% of decode GPU time, and a 10x decode regression; the fix replaced the L=1 step with a broadcast multiply plus axis sum against a decode weight precomputed at load (39.84 -> 393.84 tok/s, greedy byte-identical). The same call pattern (rolling-window pad + depthwise conv1d executed every decode step) exists in 12 other model files. This issue audits each family at L=1 on CUDA and applies the #751 elementwise decode step where measurement confirms the slow engine.
Background (from #748 / PR #751)
- Mechanism: at L=1 with bf16 activations the CUDA conv dispatch falls off the fast
conv1d_c1_k1_nhwckernel into the per-channel grouped-conv engine; f32 activations keep the fast kernel (why the f32 lfm2-8b sibling never regressed); prefill (L > 1) keeps the fast kernel too. - Detection:
nsyskernel histogram on a warm decode (look forconvolve_common_engine_float_NHWCinstances), or a per-token latency A/B against an elementwise prototype. - Fix pattern: precompute a time-major
[1, L_cache, hidden]decode weight at load (skip on Metal), compute the L=1 step as broadcast multiply + axis sum, keepconv1dfor multi-position outputs (prefill, speculative verify) and Metal. Equivalence pinned by channel- and tap-asymmetric kernel unit tests (f32 + bf16), seesrc/models/lfm2_tests.rs. - The fix(cuda): restore lfm2-350m-8bit decode via elementwise short conv #751 helpers
build_conv_decode_weight/short_conv_decode_steplive insrc/models/lfm2.rsaspub(crate)and are the natural extraction seed for a shared utility.
Candidate call sites (decode-path depthwise conv1d)
| Call site | Family | Local validation checkpoint(s) under models/ |
|---|---|---|
src/models/mamba2.rs:508 |
mamba2 | mamba2-130m (bf16), mamba2-1.3b-4bit |
src/models/mamba.rs:263 |
mamba | falcon-mamba-7b-4bit |
src/models/falcon_h1.rs:352 |
falcon-h1 | falcon-h1-tiny-90m-instruct-4bit |
src/models/granitemoehybrid.rs:390 |
granite-4.0-h | granite-4.0-h-350m-4bit, granite-4.0-h-tiny-4bit |
src/models/nemotron_h.rs:580 |
nemotron-h | nemotron-h-30b-4bit |
src/models/jamba.rs:735 |
jamba | jamba-v0.1-4bit |
src/models/plamo2.rs:319,329 |
plamo2 | plamo-2-1b |
src/models/qwen3_next.rs:382 |
qwen3-next | checkpoint no longer on disk; unit parity only |
src/models/qwen3_5.rs:374,558,683 |
qwen3.5 linear-attention conv (three call sites) | qwen3.5-0.8b-4bit and family |
src/models/gated_delta.rs |
gated delta net | via qwen3-next/qwen3.5 checkpoints |
src/models/kimi_linear.rs:271 |
kimi ShortConv1d (distinct struct from lfm2's) | kimi-vl-a3b-thinking-4bit |
src/models/recurrent_gemma.rs:359 |
recurrent gemma | check whether a checkpoint exists locally |
What the 2026-07-12 sweep did and did not show
lfm2-350m-8bitwas the only 10x case (fixed by fix(cuda): restore lfm2-350m-8bit decode via elementwise short conv #751).mamba2-130m(bf16) decode moved -10.4% (181.05 -> 162.23) and is a live candidate; its 4-bit 1.3b sibling improved (+4.9%), so magnitude clearly depends on dtype/size mix.- PR fix(cuda): restore lfm2-350m-8bit decode via elementwise short conv #751's body hypothesized
gemma-4-26b-a4b-it(-qat)(-10 to -14%) andglm4-flash-4bit(-14%) as the same pattern, but code inspection refutes that: neither is a conv architecture (gemma4MoE andglm4_moe_literespectively; neither file calls conv1d). Their drops need separate attribution and are OUT OF SCOPE here; this issue corrects the PR fix(cuda): restore lfm2-350m-8bit decode via elementwise short conv #751 hypothesis on the record. - The SSM/hybrid cluster also carried an unattributed environmental 2-3x sweep-over-sweep uplift on this host, so sweep deltas alone cannot confirm or refute the dispatch cost; only per-family kernel-level measurement can.
Plan
- Extract the fix(cuda): restore lfm2-350m-8bit decode via elementwise short conv #751 decode-step helpers from
src/models/lfm2.rsinto a shared utility; switch lfm2 to it with behavior unchanged (existing lfm2 tests must pass untouched). - For each candidate family with a local checkpoint: measure a warm L=1 decode on GB10 (nsys kernel histogram or per-token A/B) and record confirmed/refuted in the PR.
- Apply the elementwise decode step to confirmed families with the same gating as fix(cuda): restore lfm2-350m-8bit decode via elementwise short conv #751 (single-position output only, off Metal).
- Add channel- and tap-asymmetric parity unit tests (f32 + bf16, checkpoint-free) per adapted family.
- Re-bench affected models with
./scripts/bench_decode.sh models/<name> --cooldown 0singles and refresh the corresponding rows/notes indocs/benchmark_results/model_tests_gb10.md.
Acceptance Criteria
- Shared decode-step helper extracted (
src/models/conv_decode.rs); lfm2 switched to it with unchanged behavior (existing 11 lfm2 tests pass unmodified; greedy output byte-identical). - Every candidate family with a local checkpoint measured on GB10 under nsys; all REFUTED (fast
conv1d_c1_k1_nhwcengine). Verdict table + kernel counts in the PR. - N/A: no family was CONFIRMED, so none is adapted (adapting a refuted family would add the elementwise path for no benefit). Generalized f32+bf16 asymmetric parity tests still added in
conv_decode_tests.rsso the shared helper is guarded for future adopters. - Refuted families documented with per-family kernel evidence (no code change).
-
docs/benchmark_results/model_tests_gb10.mdrefreshed with the audit outcome and the mamba2-130m annotation. No singles CSVs committed because no model's behavior changed (nothing to re-bench). - gemma-4-26b-a4b / glm4-flash sweep drops recorded as unrelated to conv dispatch (PR fix(cuda): restore lfm2-350m-8bit decode via elementwise short conv #751 hypothesis corrected).
References
- Issue perf(cuda): lfm2-350m-8bit decode regressed ~10x on GB10 (409 -> ~40 tok/s) between 0.3.1 and 0.4.0-rc.1 #748, PR fix(cuda): restore lfm2-350m-8bit decode via elementwise short conv #751 (
3fa2789), PR fix: bind gemma-4 NVFP4 vision tower so image input works #750 (3fa7cc1) TECHNICAL_REPORTS/751-lfm2-elementwise-short-conv-decode-20260712.en.md(local)benchmarks/cuda_gb10_2026-07-12.csv(sweep evidence),benchmarks/cuda_gb10_2026-07-12_single_lfm2-350m-8bit_fix748.csv
Activity
docs: correct bf16 tolerance rationale and pin the #752 audit to MLX …
refactor(models): share the short-conv decode helper and audit L=1 co…