MoeLayer

Struct MoeLayer 

Source
pub struct MoeLayer {
    pub weights: MoeWeights,
    pub pre_expert_norm: Option<DenseWeight>,
    pub is_dflash_capture_layer: bool,
    /* private fields */
}
Expand description

MoE feed-forward network component.

Not a TransformerLayer — used as a component inside layers for the FFN/MoE block after post-attention norm.

Fields§

§weights: MoeWeights§pre_expert_norm: Option<DenseWeight>

Pre-expert norm: applied to input AFTER routing but BEFORE expert dispatch. Gemma-4 26B: router sees raw residual, experts see pre_feedforward_layernorm_2(residual).

§is_dflash_capture_layer: bool

Implementations§

Source§

impl MoeLayer

Source

pub fn fp32_routing_active(&self, levers: &ModelLevers) -> bool

True when the ATLAS_FP32_ROUTING path is active: the SSM-side MoE-input norm should emit an FP32 router_in (residual_add_rms_norm_gatef32) which the gate GEMM then consumes at full precision. Requires the f32 kernels to be present and the softmax-routed dense-gate config (NVFP4 gate / sigmoid+bias stay BF16). Default off → BF16 routing unchanged. The lever is the LAST term on purpose: the four preconditions are properties of this layer’s weights and kernels, and only the final one is configuration. levers is passed rather than read because this is called once per layer per DECODE TOKEN from six sites.

Source

pub fn apply_zero_expert( &self, out: DevicePtr, x: DevicePtr, n: u32, ctx: &ForwardContext<'_>, stream: u64, ) -> Result<()>

Forward pass: gate → top-K routing → batched expert FFN → blend.

All expert dispatch stays on device — zero D2H synchronization. 9 kernel launches per MoE layer (down from 58).

When gelu_activation is true, falls back to the sorted prefill path (which uses separate activation kernel) to avoid fused SiLU decode kernels. LongCat zero-computation experts: out[t,:] += zero_accum[t] * x[t,:] where zero_accum was written by the softmax+bias router kernels (the folded weights of selected identity experts). MUST run after the routed blend for the SAME tokens whose routing wrote zero_accum. No-op (no launch) when the model has no zero-experts.

Source

pub fn forward( &self, input: DevicePtr, ctx: &ForwardContext<'_>, stream: u64, ) -> Result<DevicePtr>

Source§

impl MoeLayer

Source

pub fn forward_atomic_c4_decode( &self, input: DevicePtr, num_tokens: usize, ctx: &ForwardContext<'_>, stream: u64, ) -> Result<()>

C=4 NVFP4 routed MoE decode with FP32 atomic accumulation.

Gate/top-K remain batched. Gate+up reuses the token-major kernel, then routed down projections atomic-add weighted FP32 contributions into a tiny [4,H] scratch accumulator. Finalization casts routed output to BF16 and optionally blends shared expert output.

Source§

impl MoeLayer

Source

pub fn forward_batched( &self, input: DevicePtr, num_tokens: usize, ctx: &ForwardContext<'_>, stream: u64, ) -> Result<()>

Batched forward: GEMM gate for N tokens, per-token expert dispatch.

Gate projection reads weights once for N tokens (GEMM M=N). Expert dispatch remains per-token (data-dependent routing).

Source§

impl MoeLayer

Source

pub fn forward_ep_dispatch( &self, input: DevicePtr, ctx: &ForwardContext<'_>, stream: u64, ) -> Result<DevicePtr>

  1. Computes local experts on local + received tokens
  2. Sends results back (combine)
  3. Weighted sum into output

Currently scaffolding only — builds routing table and logs statistics. Expert compute and actual dispatch use the existing per-token path. The all-reduce fallback is used for the actual output until dispatch kernels are implemented.

Source§

impl MoeLayer

Source

pub fn forward_k2( &self, input: DevicePtr, ctx: &ForwardContext<'_>, stream: u64, ) -> Result<()>

Fused K=2 forward: process 2 tokens through MoE in 5 kernel launches.

Gate GEMV batch2 → batched topK → fused expert gate+up → fused silu+down → fused wsum+blend. Expert buffers sized for 2*top_k slots. Shared expert buffers reuse logits/ssm_qkvz (sized for 2 tokens). Output at moe_output() [2, H].

Source§

impl MoeLayer

Source

pub fn forward_k3( &self, input: DevicePtr, ctx: &ForwardContext<'_>, stream: u64, ) -> Result<()>

Fused K=3 forward: process 3 tokens through MoE in 5 kernel launches.

Gate GEMV batch3 → batched topK → fused expert gate+up → fused silu+down → fused wsum+blend. Expert buffers sized for 3*top_k slots. Output at moe_output() [3, H].

Source§

impl MoeLayer

Source

pub fn forward_prefill( &self, input: DevicePtr, num_tokens: usize, ctx: &ForwardContext<'_>, stream: u64, ) -> Result<()>

N-token prefill via grouped GEMM: sort-by-expert → tensor-core GEMM per expert.

Each expert’s weight matrix is loaded once (not per-token), cutting LPDDR5X reads from ~6 GB (GEMV) to ~150 MB (grouped GEMM) at N=1024.

Pipeline: gate → topK → sort → grouped gate/up GEMM → SiLU → grouped down GEMM → unpermute + weighted reduce → shared expert blend. Shared expert uses checkpoint-native BF16 when installed, otherwise W4A16.

Source§

impl MoeLayer

Source

pub fn forward_token_major_decode( &self, input: DevicePtr, num_tokens: usize, ctx: &ForwardContext<'_>, stream: u64, ) -> Result<()>

Token-major fused decode for small N>=4.

This reuses the generic moe_prefill kernels without the sorted/grouped GEMM path. It batches gate/top-k and processes all (token, expert-slot) routes in three token-major kernels:

gate GEMM -> batched topK -> gate+up -> silu+down -> wsum/blend.

First pass is NVFP4 + shared-expert only, matching Holo’s current decode path. FP8/BF16/unified-layout variants deliberately fall back to the existing implementation until they have equivalent generic kernels.

Source§

impl MoeLayer

Source

pub fn set_pre_expert_norm(&mut self, norm: DenseWeight)

Transpose MoE weights for coalesced prefill GEMM reads.

Transposes per-expert routed weights [N, K/2] → [K/2, N] to enable the cp.async pipelined FP8-MMA K64 kernels. This doubles expert memory (~17 GB for 35B, ~30 GB for 122B) but eliminates the catastrophic uncoalesced B reads in the fallback grouped GEMM, cutting MoE prefill time by ~2x. Set pre-expert norm (Gemma-4 26B: pre_feedforward_layernorm_2). Applied to input AFTER routing but BEFORE expert dispatch.

Source

pub fn set_gelu_activation(&mut self, gpu: &dyn GpuBackend) -> Result<()>

Set GeGLU activation for MoE experts (Gemma-4 26B). Replaces SiLU with GELU in the sorted/unfused path and forces decode to use the sorted path (avoiding fused SiLU kernels).

Source

pub fn transpose_for_prefill( &mut self, gpu: &dyn GpuBackend, config: &ModelConfig, ) -> Result<()>

Source

pub fn transpose_gate_up_for_prefill( &mut self, gpu: &dyn GpuBackend, config: &ModelConfig, ) -> Result<()>

Transpose only the gate+up routed weights, leaving the down projection in its original layout. Cuts the transpose memory cost from ~3× (gate+up+down) to ~2× per expert. Used by MiniMax M2.7-NVFP4 EP=2 when the full transpose doesn’t fit but gate+up does — the fused moe_w4a16_fused_gate_up_k64_n128 kernel still runs (capturing the dominant gate+up bandwidth savings), while down stays on the uncoalesced grouped-GEMM path.

Source

pub fn transpose_for_prefill_unified( &mut self, gpu: &dyn GpuBackend, config: &ModelConfig, ) -> Result<()>

Phase 8a unified-layout transpose pass: build persistent transposed gate/up/down for all experts, freeing the untransposed copies between phases so the entire pass fits in tight memory budgets that the non-unified transpose_for_prefill_impl(true) would reject.

Phased flow (memory math for MiniMax M2.7-NVFP4 EP=2 ≈ 47 GB free): A. Transpose gate+up (allocs +39 GB; free ≈ 8 GB) B. Free gate+up untransposed (frees 39 GB; free ≈ 47 GB) C. Transpose down (allocs +20 GB; free ≈ 27 GB) D. Free down untransposed (frees 20 GB; free ≈ 47 GB)

Net memory: same as starting point, but layout is now unified (transposed-only) — the [N, K/2] decode kernels can no longer run; dispatch must use the _t decode kernels (which do).

Caller responsibilities:

  1. Set ATLAS_UNIFIED_MOE_LAYOUT=1 so MoeLayer::use_t_layout_for_decode() returns true at dispatch time.
  2. Call this method INSTEAD of transpose_for_prefill / transpose_gate_up_for_prefill.
Source

pub fn transpose_for_prefill_hybrid( &mut self, gpu: &dyn GpuBackend, config: &ModelConfig, ) -> Result<()>

Hybrid-layout transpose pass — analogue of transpose_for_prefill_unified that keeps the untransposed originals so decode + MTP verify dispatch can continue using the warp-reduction kernels. Allocates ~58 GB transposed alongside the existing ~58 GB originals on MiniMax M2.7-NVFP4 EP=2; fits in 122 GB GB10 with KV-cache headroom up to ~32K context. Caller is responsible for memory-fit gating (factory checks free memory before invoking this).

Source

pub fn build_cutlass_grouped_sfb( &mut self, gpu: &dyn GpuBackend, config: &ModelConfig, stream: u64, ) -> Result<()>

Build per-expert swizzled SFB weight-scale tables for the CUTLASS grouped NVFP4 path (ATLAS_HOLO_MOE_GROUPED_CUTLASS). For each expert, swizzle the [K/16,N] gate_ptrs_t/up_ptrs_t scale into the CUTLASS SFB atom via pack_weight_sfb, then upload the per-expert pointer arrays. The grouped kernel pairs these with gate_ptrs.packed ([N,K/2]) + the real per-expert scale2. Requires FAST_MOE=full (gate_ptrs_t/up_ptrs_t present); no-op else.

Source§

impl MoeLayer

Source

pub fn set_down_transpose_scratch( &mut self, scratch_packed: DevicePtr, scratch_scale: DevicePtr, packed_ptrs_t: DevicePtr, scale_ptrs_t: DevicePtr, )

Wire a shared per-prefill down_proj scratch + transposed pointer table.

Called by the factory after the persistent MoE transpose pass falls back to gate+up only. The scratch and pointer tables are shared across all MoE layers — one allocation reused layer-by-layer during the sequential forward. The same scale2_vals buffer is reused from the existing untransposed down_ptrs (transpose preserves per-tensor scales).

Source§

impl MoeLayer

Source

pub fn predequant_for_prefill( &mut self, gpu: &dyn GpuBackend, config: &ModelConfig, stream: u64, ) -> Result<()>

Pre-dequant dense (non-expert) NVFP4 weights to FP8 for zero-overhead prefill.

Only affects gate GEMM and shared expert GEMMs. Expert weights stay NVFP4 (they’re bandwidth-bound so FP8 wouldn’t help).

Source

pub fn set_fp8_experts( &mut self, experts: &[Fp8ExpertWeight], shared_expert: Fp8ExpertWeight, gpu: &dyn GpuBackend, ) -> Result<()>

Set FP8 expert weights for native FP8 dispatch.

Builds device-side pointer tables from FP8 expert weights so the fused FP8 MoE kernel can index by expert_id at dispatch time. Also stores the shared expert FP8 weights for direct pointer passing.

Source

pub fn set_bf16_experts( &mut self, gate_experts: &[DenseWeight], up_experts: &[DenseWeight], down_experts: &[DenseWeight], shared_gate: DevicePtr, shared_up: DevicePtr, shared_down: DevicePtr, gpu: &dyn GpuBackend, ) -> Result<()>

Set BF16 expert weights for the FP8-dequant-on-load MoE path.

Activated by ATLAS_FP8_DEQUANT_MOE_TO_BF16=1. Eliminates the per-layer 0.989 FP8 cosine ceiling (measured in bench/fp8_dgx2_drift/cosine_run.py) by serving experts as BF16 throughout, matching vLLM-BF16 reference numerics. Memory cost: 2× expert weights vs native FP8.

shared_* are the shared expert’s BF16 gate/up/down DevicePtrs (or DevicePtr::NULL when the model has no shared expert).

Source

pub fn set_bf16_shared_expert( &mut self, gate_proj: DenseWeight, up_proj: DenseWeight, down_proj: DenseWeight, ) -> Result<()>

Install checkpoint-native BF16 shared-expert weights independently of routed-expert precision.

Source§

impl MoeLayer

Source

pub fn new( weights: MoeWeights, num_experts: usize, gate_nvfp4: Option<QuantizedWeight>, gpu: &dyn GpuBackend, config: &ModelConfig, ) -> Result<Self>

Source

pub fn new_with_hash( weights: MoeWeights, num_experts: usize, gate_nvfp4: Option<QuantizedWeight>, tid2eid_dev: Option<DevicePtr>, gpu: &dyn GpuBackend, config: &ModelConfig, ) -> Result<Self>

Like MoeLayer::new but with an optional DeepSeek-V4 hash-routing tid2eid table ([vocab_size, top_k] i64). Some marks this as a hash-routed layer.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more