Skip to main content

ferrum_kernels/
marlin_expert_stack.rs

1//! `MarlinExpertStack<B>` — abstraction for "N MoE experts' Marlin
2//! GPTQ-INT4 tiles stored contiguously, dispatched as bucketed batched
3//! GEMM or vLLM fused MoE kernel".
4//!
5//! Phase C sibling to `StackedExpertGgufLinear<B>` (GGUF) and `Linear<B>`
6//! (single-tensor). Same goal: drop `type GptqStore` from the `Backend`
7//! trait by routing dispatch through a `Box<dyn MarlinExpertStack<B>>`
8//! returned by the loader — so future backends only need to implement
9//! this trait, not edit the `Backend` supertrait stack.
10//!
11//! Concrete impls (added in Phase C step 2):
12//!   - `quant_linear::cuda_marlin_stack::CudaMarlinExpertStack` wraps
13//!     `Arc<GptqStoreCuda>` and dispatches to `marlin_gemm_with_offset_strided`
14//!     (bucketed) or `marlin_moe_wna16` (vLLM fused).
15//!   - CPU dequant path stays per-Linear (no batched MoE Marlin kernel).
16//!
17//! The trait surface is intentionally small — three GEMM methods + a
18//! workspace zero + an expert-view constructor. Each maps 1:1 to an
19//! existing `Backend::moe_gemm_phase_*` method that Phase C step 3
20//! will delete from the trait.
21
22use crate::backend::Backend;
23use crate::Linear;
24use ferrum_types::Result;
25use std::sync::Arc;
26
27/// MoE-stacked Marlin INT4 expert tile: holds N experts' weights for one
28/// matmul role (gate_up / down) in one contiguous repacked Marlin buffer,
29/// dispatches per-expert column-slice GEMMs in a single fused launch
30/// (vLLM marlin_moe_wna16) or as a bucketed batched call.
31pub trait MarlinExpertStack<B: Backend>: Send + Sync {
32    /// Per-expert output width (N tile cols).
33    fn n_per_expert(&self) -> usize;
34    /// Input width (K), common across experts.
35    fn k(&self) -> usize;
36    /// Number of experts packed into the tile.
37    fn num_experts(&self) -> usize;
38
39    /// True when the packed representation requires the vLLM MoE
40    /// dispatcher. For CUDA this covers vLLM Marlin-MoE tile layout and
41    /// asymmetric GPTQ qzeros.
42    fn requires_vllm_moe(&self) -> bool {
43        false
44    }
45
46    /// Downcast hook — used at FFN dispatch boundaries where the
47    /// caller needs to reach into the concrete store to e.g. share
48    /// workspace memory across phases. Standard `dyn Any` pattern.
49    fn as_any(&self) -> &dyn std::any::Any;
50
51    /// Bulk-zero the per-expert Marlin workspace mutex slots. Call ONCE
52    /// before a batch of bucketed `gemm_phase_batched` calls — saves
53    /// the per-call cuMemsetD32Async (one launch each → one launch
54    /// total). At c=32 with 128 active experts × 2 phases × 48 layers
55    /// that's ~12k memset launches/token reduced to ~96.
56    fn zero_workspace(&self, ctx: &mut B::Context) -> Result<()>;
57
58    /// Batched per-expert offset GEMM. `dispatches[i] =
59    /// (expert_idx, in_row_offset, out_row_offset, m)`. Runs each
60    /// expert's `(m × K) @ tile[expert] = m × n_per_expert` slice;
61    /// CUDA backend overlaps via multi-stream round-robin.
62    #[allow(clippy::too_many_arguments)]
63    fn gemm_phase_batched(
64        &self,
65        ctx: &mut B::Context,
66        input: &B::Buffer,
67        dispatches: &[(usize, usize, usize, usize)],
68        output: &mut B::Buffer,
69        k: usize,
70    ) -> Result<()>;
71
72    /// vLLM `marlin_moe_wna16` fused GEMM (single launch, per-block
73    /// expert routing inside the kernel). Caller responsibilities:
74    /// - `output` MUST be pre-zeroed (atomic-add path doesn't self-zero).
75    /// - `sorted_token_ids` / `expert_ids` / `num_tokens_past_padded`
76    ///   come from `moe_align_block_size`.
77    /// - `prob_m` is the unique-token count (top_k=1 with pre-gathered
78    ///   rows ⇒ equals `total_pairs`).
79    /// Backends without vLLM Marlin return `Err(unsupported)`.
80    #[allow(clippy::too_many_arguments)]
81    fn gemm_phase_vllm(
82        &self,
83        _ctx: &mut B::Context,
84        _input: &B::Buffer,
85        _sorted_token_ids: &B::Buffer,
86        _expert_ids: &B::Buffer,
87        _num_tokens_past_padded: &B::Buffer,
88        _output: &mut B::Buffer,
89        _prob_m: usize,
90        _moe_block_size: usize,
91        _top_k: usize,
92    ) -> Result<()> {
93        Err(ferrum_types::FerrumError::unsupported(
94            "MarlinExpertStack::gemm_phase_vllm not implemented for this backend",
95        ))
96    }
97
98    /// Build a single-expert `Linear<B>` view onto this stack's
99    /// `[expert_offset .. expert_offset + expert_n)` column slice.
100    /// Used for per-expert dispatch outside the MoE phase batching
101    /// (e.g. shared-experts code paths). `expert_offset` and `expert_n`
102    /// MUST be multiples of the backend's Marlin N tile (64 on CUDA).
103    fn make_expert_linear(
104        self: Arc<Self>,
105        expert_offset: usize,
106        expert_n: usize,
107        bias_host: Option<&[f32]>,
108    ) -> Result<Box<dyn Linear<B> + Send + Sync>>;
109}