Skip to content

feat(ffai,mlx): Step-3 / DeepSeek-V3 decoder kernels — clamped SwiGLU, head-wise attn gate, sigmoid+bias router#242

Merged
TheTom merged 1 commit into
0xClandestine:devfrom
TheTom:tom/wip/step3-kernels
Jun 3, 2026
Merged

feat(ffai,mlx): Step-3 / DeepSeek-V3 decoder kernels — clamped SwiGLU, head-wise attn gate, sigmoid+bias router#242
TheTom merged 1 commit into
0xClandestine:devfrom
TheTom:tom/wip/step3-kernels

Conversation

@TheTom

@TheTom TheTom commented May 30, 2026

Copy link
Copy Markdown
Collaborator

Summary

Three #[kernel]-DSL ports covering the architectural primitives the Step-3.5-Flash / Step-3.7-Flash decoder needs that aren't already in the metaltile-std surface. Each lands as a self-contained file: kernel body + #[test_kernel] (mod kernel_tests) + #[bench] (mod kernel_benches).

All three are validated on M5 via tile test — GPU output vs CPU oracle, across f32/f16/bf16.

Kernels

Kernel File Role
mt_clamped_swiglu mlx/clamped_swiglu.rs Drop-in for mt_swiglu with one-sided silu output clip + symmetric up clip — gpt-oss + Step-3 activation pattern
ffai_attn_head_gate ffai/attn_head_gate.rs out[h, d] = attn[h, d] * sigmoid(gate[h]) — head-wise sigmoid attention gate, applied between SDPA and o_proj
ffai_moe_router_sigmoid_bias ffai/moe_router_sigmoid_bias.rs DeepSeek-V3 routing score path: sigmoid(logits) + bias; top-k selection stays in the shared selection pipeline

1. mt_clamped_swiglu

    out[i] = clip(silu(gate[i]), max=L) * clip(up[i], -L, L)

Per-layer limit passed as an f32 constexpr. limit <= 0 collapses to plain SwiGLU — validated by test_mt_clamped_swiglu_zero_limit_equals_plain, the equivalence the per-layer dispatch ships on (most layers are limit=0; only a small subset carry non-zero limits). silu is g * sigmoid(g); clipping happens on the f32 intermediates before the cast back to T.

2. ffai_attn_head_gate

gate is [n_heads] — produced upstream by a Linear(hidden, n_heads). Sigmoid runs in f32 so the bf16 small-magnitude tail doesn't underflow. 1D elementwise over the flat [n_heads * head_dim] buffer; the owning head is idx / head_dim. Tests cover both full-attn (n_heads=64) and SWA (n_heads=96) shapes — the kernel doesn't bake the head count in.

3. ffai_moe_router_sigmoid_bias

Score-producer kernel only. The sigmoid+bias factorisation lets each expert score be evaluated independently (no softmax denominator coupling) — the sister of ffai_moe_router_sqrtsoftplus. Tested at both n_experts=288 and n_experts=256.

Why these three

The rest of the Step-3 decoder is a recombination of existing primitives — GQA SDPA, RoPE-llama-many with partial-rotary, per-head RMSNorm, MoE top-k routing, conv2d patch embedding, 2D RoPE, sdpa_bidirectional for the vision tower. The three above are the genuinely-new activation/gating/routing choices that don't fit any existing kernel.

Verification

  • tile test (clamped_swiglu / attn_head_gate / router_sigmoid_bias) — all pass on M5, f32/f16/bf16
  • full tile test — no regressions
  • cargo clippy --all-targets --all-features -- -D warnings — clean
  • cargo fmt --check — clean

@github-actions github-actions Bot added the feature New feature label May 30, 2026
TheTom added a commit to TheTom/metaltile that referenced this pull request May 30, 2026
…rejects mixed-dtype param sets

Same fix as Step-3 PR 0xClandestine#242: CI's `cannot find spec in crate` for all
three new GGUF dequant kernels traces to the legacy `bench(...)`
proc-macro path emitting `crate::spec::BenchDispatch::Generic` for a
`class=GenericEmpty` shape it can't pattern-match against. These
dequant kernels mix concrete-dtype packed-byte inputs (`Tensor<u8>`,
`Tensor<u32>`, `Tensor<f32>`) with a single generic `Tensor<T>` output;
the legacy GenericEmpty path expects all-T-generic params.

Switching to bare `#[kernel]` keeps the codegen / MSL-emit identical
and lets the declarative `#[bench]` attribute on `kernel_benches::*`
handle `tile bench` registration directly. 111/111 metaltile-std lib
tests pass, workspace clippy + fmt clean.
@TheTom
TheTom force-pushed the tom/wip/step3-kernels branch from be70cda to 3abf4a9 Compare June 3, 2026 18:03
@TheTom TheTom changed the title feat(step3): WIP kernels — clamped SwiGLU + head-wise attn gate + sigmoid+bias router feat(ffai,mlx): Step-3 / DeepSeek-V3 decoder kernels — clamped SwiGLU, head-wise attn gate, sigmoid+bias router Jun 3, 2026
…, head-wise attn gate, sigmoid+bias router

Three #[kernel]-DSL ports for the architectural primitives the
Step-3.5-Flash / Step-3.7-Flash decoders need that aren't already in
the metaltile-std surface. Each is a self-contained file: kernel body +
#[test_kernel] (GPU-vs-CPU-oracle) + #[bench].

- mt_clamped_swiglu (mlx/clamped_swiglu.rs):
    out[i] = clip(silu(gate[i]), max=L) * clip(up[i], -L, L)
  Per-layer f32 `limit` constexpr; `limit <= 0` collapses to plain
  SwiGLU (shipped on an equivalence test). gpt-oss + Step-3 activation
  clip pattern.

- ffai_attn_head_gate (ffai/attn_head_gate.rs):
    out[h, d] = attn[h, d] * sigmoid(gate[h])
  Per-head sigmoid gate applied between SDPA and o_proj. 1D elementwise;
  owning head = idx / head_dim. Tested at full-attn (64h) + SWA (96h).

- ffai_moe_router_sigmoid_bias (ffai/moe_router_sigmoid_bias.rs):
    scores[e] = sigmoid(logits[e]) + bias[e]
  DeepSeek-V3 routing score path; the sister of moe_router_sqrtsoftplus.
  Top-k selection stays in the shared pipeline. Tested at 288 + 256
  experts.

All three use the proven DSL idioms (free-function `exp`, f32-suffixed
literals, `select` over ambiguous min/max, `#[constexpr]` scalars,
linear `tid`) — verified on M5 via `tile test`. Transcendental tols
match the sqrtsoftplus sibling (2e-4) for cross-GPU fast-math headroom.
@TheTom
TheTom force-pushed the tom/wip/step3-kernels branch from 3abf4a9 to 1fe6c08 Compare June 3, 2026 18:07
@TheTom
TheTom enabled auto-merge (squash) June 3, 2026 18:12
@TheTom
TheTom merged commit 09d6305 into 0xClandestine:dev Jun 3, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant