use super::blas::{TypedPtr, gpu_gemm_forward_dispatch, gpu_gemm_typed_forward_raw};
use super::buffers::{DtypedBuf, GpuBuffer};
use super::context::GpuCtx;
use super::device::GpuDevice;
use super::dtype::WeightDtype;
use super::launch::{grid_1d, grid_norm};
use super::weights::{
GpuMambaMixedWeights, GpuMambaWeights, MambaLayerWeightsView, MambaWeightsView,
};
use crate::config::MambaConfig;
use crate::weights::MambaWeights;
use cudarc::driver::PushKernelArg;
use std::sync::Arc;
pub struct GpuInferenceState {
pub conv: GpuBuffer,
pub ssm: GpuBuffer,
batch: usize,
d_inner: usize,
d_conv: usize,
d_state: usize,
}
impl GpuInferenceState {
pub fn zeros(
stream: &Arc<cudarc::driver::CudaStream>,
batch: usize,
cfg: &MambaConfig,
) -> Result<Self, String> {
let di = cfg.d_inner();
let conv_len = cfg.n_layers * batch * di * cfg.d_conv;
let ssm_len = cfg.n_layers * batch * di * cfg.d_state;
Ok(Self {
conv: GpuBuffer::zeros(stream, conv_len)?,
ssm: GpuBuffer::zeros(stream, ssm_len)?,
batch,
d_inner: di,
d_conv: cfg.d_conv,
d_state: cfg.d_state,
})
}
pub fn reset(&mut self, stream: &Arc<cudarc::driver::CudaStream>) -> Result<(), String> {
self.conv.zero(stream)?;
self.ssm.zero(stream)
}
pub fn conv_offset(&self, layer: usize) -> usize {
layer * self.batch * self.d_inner * self.d_conv
}
pub fn ssm_offset(&self, layer: usize) -> usize {
layer * self.batch * self.d_inner * self.d_state
}
pub fn batch(&self) -> usize {
self.batch
}
}
pub struct GpuInferenceScratch {
pub gpu_input: GpuBuffer,
pub temporal: GpuBuffer,
pub residual: GpuBuffer,
pub proj: GpuBuffer,
pub x_branch: GpuBuffer,
pub gate_silu: GpuBuffer,
pub u: GpuBuffer,
pub xdbl: GpuBuffer,
pub dt_gather: GpuBuffer,
pub delta: GpuBuffer,
pub b_buf: GpuBuffer,
pub c_buf: GpuBuffer,
pub y: GpuBuffer,
pub rms_buf: GpuBuffer,
}
impl GpuInferenceScratch {
pub fn new(
stream: &Arc<cudarc::driver::CudaStream>,
batch: usize,
cfg: &MambaConfig,
input_dim: usize,
) -> Result<Self, String> {
let dm = cfg.d_model;
let di = cfg.d_inner();
let ds = cfg.d_state;
let dt_rank = cfg.dt_rank();
let xdbl_dim = cfg.xdbl_dim();
Ok(Self {
gpu_input: GpuBuffer::zeros(stream, batch * input_dim)?,
temporal: GpuBuffer::zeros(stream, batch * dm)?,
residual: GpuBuffer::zeros(stream, batch * dm)?,
proj: GpuBuffer::zeros(stream, batch * 2 * di)?,
x_branch: GpuBuffer::zeros(stream, batch * di)?,
gate_silu: GpuBuffer::zeros(stream, batch * di)?,
u: GpuBuffer::zeros(stream, batch * di)?,
xdbl: GpuBuffer::zeros(stream, batch * xdbl_dim)?,
dt_gather: GpuBuffer::zeros(stream, batch * dt_rank)?,
delta: GpuBuffer::zeros(stream, batch * di)?,
b_buf: GpuBuffer::zeros(stream, batch * ds)?,
c_buf: GpuBuffer::zeros(stream, batch * ds)?,
y: GpuBuffer::zeros(stream, batch * di)?,
rms_buf: GpuBuffer::zeros(stream, batch)?,
})
}
}
pub struct GpuInferenceMixedScratch {
pub gpu_input: GpuBuffer,
pub temporal: DtypedBuf,
pub residual: GpuBuffer,
pub proj: DtypedBuf,
pub x_branch: DtypedBuf,
pub gate_silu: DtypedBuf,
pub u: DtypedBuf,
pub xdbl: DtypedBuf,
pub dt_gather: DtypedBuf,
pub delta: DtypedBuf,
pub b_buf: DtypedBuf,
pub c_buf: DtypedBuf,
pub y: DtypedBuf,
pub rms_buf: GpuBuffer,
pub dtype: WeightDtype,
}
impl GpuInferenceMixedScratch {
pub fn new(
stream: &Arc<cudarc::driver::CudaStream>,
batch: usize,
cfg: &MambaConfig,
input_dim: usize,
dtype: WeightDtype,
) -> Result<Self, String> {
if matches!(dtype, WeightDtype::F32) {
return Err("GpuInferenceMixedScratch requires bf16 or f16 dtype (use \
GpuInferenceScratch for f32)"
.to_string());
}
let dm = cfg.d_model;
let di = cfg.d_inner();
let ds = cfg.d_state;
let dt_rank = cfg.dt_rank();
let xdbl_dim = cfg.xdbl_dim();
Ok(Self {
gpu_input: GpuBuffer::zeros(stream, batch * input_dim)?,
temporal: DtypedBuf::zeros(stream, batch * dm, dtype)?,
residual: GpuBuffer::zeros(stream, batch * dm)?,
proj: DtypedBuf::zeros(stream, batch * 2 * di, dtype)?,
x_branch: DtypedBuf::zeros(stream, batch * di, dtype)?,
gate_silu: DtypedBuf::zeros(stream, batch * di, dtype)?,
u: DtypedBuf::zeros(stream, batch * di, dtype)?,
xdbl: DtypedBuf::zeros(stream, batch * xdbl_dim, dtype)?,
dt_gather: DtypedBuf::zeros(stream, batch * dt_rank, dtype)?,
delta: DtypedBuf::zeros(stream, batch * di, dtype)?,
b_buf: DtypedBuf::zeros(stream, batch * ds, dtype)?,
c_buf: DtypedBuf::zeros(stream, batch * ds, dtype)?,
y: DtypedBuf::zeros(stream, batch * di, dtype)?,
rms_buf: GpuBuffer::zeros(stream, batch)?,
dtype,
})
}
}
pub struct GpuMambaInference {
pub(super) ctx: GpuCtx,
pub(super) weights: GpuMambaWeights,
pub(super) a_neg_all: GpuBuffer,
pub(super) cfg: MambaConfig,
pub(super) input_dim: usize,
pub(super) batch: usize,
pub(super) identity_proj: bool,
graph: Option<cudarc::driver::CudaGraph>,
captured_state_ptr: u64,
captured_scratch_ptr: u64,
}
impl GpuMambaInference {
pub fn new(
device: &GpuDevice,
cpu_weights: &MambaWeights,
cfg: MambaConfig,
input_dim: usize,
batch: usize,
) -> Result<Self, String> {
cfg.validate()?;
let ctx = GpuCtx::new(device)?;
let weights = GpuMambaWeights::from_cpu(&ctx.stream, cpu_weights, &cfg)?;
let di = cfg.d_inner();
let ds = cfg.d_state;
let total_aneg = cfg.n_layers * di * ds;
let a_neg_all = GpuBuffer::zeros(&ctx.stream, total_aneg)?;
for (layer_idx, lw) in weights.layers.iter().enumerate() {
let offset = layer_idx * di * ds;
let dst_ptr = a_neg_all.raw_ptr_at(&ctx.stream, offset);
let src_ptr = lw.a_log.ptr();
let n_i = (di * ds) as i32;
let mut builder = ctx.stream.launch_builder(&ctx.kernels.exp_negate);
builder.arg(&dst_ptr);
builder.arg(&src_ptr);
builder.arg(&n_i);
unsafe { builder.launch(grid_1d(di * ds)) }
.map_err(|e| format!("exp_negate layer {layer_idx}: {e:?}"))?;
}
let identity_proj = cpu_weights.input_proj_w.is_empty();
Ok(Self {
ctx,
weights,
a_neg_all,
cfg,
input_dim,
batch,
identity_proj,
graph: None,
captured_state_ptr: 0,
captured_scratch_ptr: 0,
})
}
pub fn capture_graph(
&mut self,
state: &mut GpuInferenceState,
scratch: &mut GpuInferenceScratch,
) -> Result<(), String> {
let snap_state = state.conv.cached_ptr();
let snap_scratch = scratch.gpu_input.cached_ptr();
let stream = self.ctx.stream.clone();
let graph = crate::mamba_ssm::gpu::graph_capture::capture_into_graph(&stream, || {
self.step_kernels(state, scratch)
})?;
self.graph = Some(graph);
self.captured_state_ptr = snap_state;
self.captured_scratch_ptr = snap_scratch;
Ok(())
}
pub fn has_graph(&self) -> bool {
self.graph.is_some()
}
pub fn alloc_state(&self) -> Result<GpuInferenceState, String> {
GpuInferenceState::zeros(&self.ctx.stream, self.batch, &self.cfg)
}
pub fn alloc_scratch(&self) -> Result<GpuInferenceScratch, String> {
GpuInferenceScratch::new(&self.ctx.stream, self.batch, &self.cfg, self.input_dim)
}
pub fn step(
&self,
input: &[f32],
output: &mut [f32],
state: &mut GpuInferenceState,
scratch: &mut GpuInferenceScratch,
) -> Result<(), String> {
scratch.gpu_input.upload(&self.ctx.stream, input)?;
if let Some(ref g) = self.graph {
assert_eq!(
state.conv.cached_ptr(),
self.captured_state_ptr,
"CUDA Graph replay requires the same state buffers used during capture"
);
assert_eq!(
scratch.gpu_input.cached_ptr(),
self.captured_scratch_ptr,
"CUDA Graph replay requires the same scratch buffers used during capture"
);
g.launch().map_err(|e| format!("graph launch: {e:?}"))?;
} else {
self.step_kernels(state, scratch)?;
}
self.ctx
.stream
.synchronize()
.map_err(|e| format!("sync: {e:?}"))?;
scratch.temporal.download(&self.ctx.stream, output)?;
Ok(())
}
pub fn step_gpu_only(
&self,
input: &[f32],
state: &mut GpuInferenceState,
scratch: &mut GpuInferenceScratch,
) -> Result<(), String> {
scratch.gpu_input.upload(&self.ctx.stream, input)?;
if let Some(ref g) = self.graph {
assert_eq!(state.conv.cached_ptr(), self.captured_state_ptr);
assert_eq!(scratch.gpu_input.cached_ptr(), self.captured_scratch_ptr);
g.launch().map_err(|e| format!("graph launch: {e:?}"))?;
} else {
self.step_kernels(state, scratch)?;
}
Ok(())
}
pub fn temporal_buffer<'a>(&self, scratch: &'a GpuInferenceScratch) -> &'a GpuBuffer {
&scratch.temporal
}
fn step_kernels(
&self,
state: &mut GpuInferenceState,
scratch: &mut GpuInferenceScratch,
) -> Result<(), String> {
self.step_kernels_generic(&self.weights, state, scratch)
}
#[doc(hidden)]
pub fn step_kernels_f32_debug(
&self,
state: &mut GpuInferenceState,
scratch: &mut GpuInferenceScratch,
stop_after_layer: usize,
) -> Result<(), String> {
self.step_kernels_generic_impl(&self.weights, state, scratch, Some(stop_after_layer))
}
pub(super) fn step_kernels_generic<W: MambaWeightsView>(
&self,
weights: &W,
state: &mut GpuInferenceState,
scratch: &mut GpuInferenceScratch,
) -> Result<(), String> {
self.step_kernels_generic_impl(weights, state, scratch, None)
}
fn step_kernels_generic_impl<W: MambaWeightsView>(
&self,
weights: &W,
state: &mut GpuInferenceState,
scratch: &mut GpuInferenceScratch,
stop_after_layer: Option<usize>,
) -> Result<(), String> {
let b = self.batch;
let cfg = &self.cfg;
let dm = cfg.d_model;
let di = cfg.d_inner();
let ds = cfg.d_state;
let dt_rank = cfg.dt_rank();
let xdbl_dim = cfg.xdbl_dim();
let d_conv = cfg.d_conv;
let k = &self.ctx.kernels;
if self.identity_proj {
debug_assert_eq!(
self.input_dim, dm,
"identity_proj requires input_dim == d_model"
);
scratch
.temporal
.copy_from_raw(&scratch.gpu_input, &self.ctx.stream)?;
} else {
let (ipw_ptr, ipw_dtype) = weights.input_proj_w();
gpu_gemm_forward_dispatch(
&self.ctx,
&mut scratch.temporal,
&scratch.gpu_input,
ipw_ptr,
ipw_dtype,
Some(weights.input_proj_b()),
(b, self.input_dim, dm),
)?;
}
let f32_sz = std::mem::size_of::<f32>() as u64;
let layer_limit = stop_after_layer
.map(|n| n.min(weights.n_layers()))
.unwrap_or(weights.n_layers());
for layer_idx in 0..layer_limit {
let lw = weights.layer(layer_idx);
let conv_ptr = state.conv.cached_ptr() + (state.conv_offset(layer_idx) as u64) * f32_sz;
let ssm_ptr = state.ssm.cached_ptr() + (state.ssm_offset(layer_idx) as u64) * f32_sz;
let aneg_ptr = self.a_neg_all.cached_ptr() + (layer_idx * di * ds) as u64 * f32_sz;
scratch
.residual
.copy_from_raw(&scratch.temporal, &self.ctx.stream)?;
{
let b_i = b as i32;
let dm_i = dm as i32;
let eps: f32 = cfg.rms_norm_eps;
let mut bld = self.ctx.stream.launch_builder(&k.rmsnorm_fwd);
let t_ptr = scratch.temporal.cached_ptr();
let rms_ptr = scratch.rms_buf.cached_ptr();
let res_ptr = scratch.residual.cached_ptr();
bld.arg(&t_ptr); bld.arg(&rms_ptr);
bld.arg(&res_ptr); let nw = lw.norm_weight();
bld.arg(&nw);
bld.arg(&b_i);
bld.arg(&dm_i);
bld.arg(&eps);
unsafe { bld.launch(grid_norm(b, dm)) }
.map_err(|e| format!("rmsnorm_fwd L{layer_idx}: {e:?}"))?;
}
let (ipw, ipw_dt) = lw.in_proj_w();
gpu_gemm_forward_dispatch(
&self.ctx,
&mut scratch.proj,
&scratch.temporal,
ipw,
ipw_dt,
None,
(b, dm, 2 * di),
)?;
{
let b_i = b as i32;
let di_i = di as i32;
let mut bld = self.ctx.stream.launch_builder(&k.split_gate_silu);
let xb_ptr = scratch.x_branch.cached_ptr();
bld.arg(&xb_ptr);
let g_ptr = scratch.gate_silu.cached_ptr();
let p_ptr = scratch.proj.cached_ptr();
bld.arg(&g_ptr); bld.arg(&g_ptr); bld.arg(&p_ptr);
bld.arg(&b_i);
bld.arg(&di_i);
unsafe { bld.launch(grid_1d(b * di)) }
.map_err(|e| format!("split_gate_silu L{layer_idx}: {e:?}"))?;
}
{
let b_i = b as i32;
let di_i = di as i32;
let dc_i = d_conv as i32;
let mut bld = self.ctx.stream.launch_builder(&k.conv1d_step_fwd);
let u_ptr = scratch.u.cached_ptr();
let xb_ptr2 = scratch.x_branch.cached_ptr();
bld.arg(&u_ptr);
bld.arg(&conv_ptr); bld.arg(&xb_ptr2);
let cw = lw.conv1d_weight();
let cb = lw.conv1d_bias();
bld.arg(&cw);
bld.arg(&cb);
bld.arg(&b_i);
bld.arg(&di_i);
bld.arg(&dc_i);
unsafe { bld.launch(grid_1d(b * di)) }
.map_err(|e| format!("conv1d_step L{layer_idx}: {e:?}"))?;
}
{
let n = (b * di) as i32;
let mut bld = self.ctx.stream.launch_builder(&k.silu_fwd);
let u_silu_ptr = scratch.u.cached_ptr();
bld.arg(&u_silu_ptr); bld.arg(&n);
unsafe { bld.launch(grid_1d(b * di)) }
.map_err(|e| format!("silu_fwd L{layer_idx}: {e:?}"))?;
}
let (xpw, xpw_dt) = lw.x_proj_w();
gpu_gemm_forward_dispatch(
&self.ctx,
&mut scratch.xdbl,
&scratch.u,
xpw,
xpw_dt,
None,
(b, di, xdbl_dim),
)?;
{
let b_i = b as i32;
let xdbl_i = xdbl_dim as i32;
let dt_i = dt_rank as i32;
let offset: i32 = 0;
let mut bld = self.ctx.stream.launch_builder(&k.gather_cols);
let dtg_ptr = scratch.dt_gather.cached_ptr();
let xdbl_ptr = scratch.xdbl.cached_ptr();
bld.arg(&dtg_ptr);
bld.arg(&xdbl_ptr);
bld.arg(&b_i);
bld.arg(&xdbl_i);
bld.arg(&dt_i);
bld.arg(&offset);
unsafe { bld.launch(grid_1d(b * dt_rank)) }
.map_err(|e| format!("gather_cols dt L{layer_idx}: {e:?}"))?;
}
let (dpw, dpw_dt) = lw.dt_proj_w();
gpu_gemm_forward_dispatch(
&self.ctx,
&mut scratch.delta,
&scratch.dt_gather,
dpw,
dpw_dt,
Some(lw.dt_proj_b()),
(b, dt_rank, di),
)?;
{
let n = (b * di) as i32;
let mut bld = self.ctx.stream.launch_builder(&k.softplus_copy);
let d_ptr = scratch.delta.cached_ptr();
bld.arg(&d_ptr); bld.arg(&d_ptr); bld.arg(&n);
unsafe { bld.launch(grid_1d(b * di)) }
.map_err(|e| format!("softplus L{layer_idx}: {e:?}"))?;
}
{
let b_i = b as i32;
let xdbl_i = xdbl_dim as i32;
let ds_i = ds as i32;
let b_off = dt_rank as i32;
let c_off = (dt_rank + ds) as i32;
let mut bld = self.ctx.stream.launch_builder(&k.gather_bc_cols);
let bb_ptr = scratch.b_buf.cached_ptr();
let cb_ptr = scratch.c_buf.cached_ptr();
let xdbl_bc_ptr = scratch.xdbl.cached_ptr();
bld.arg(&bb_ptr);
bld.arg(&cb_ptr);
bld.arg(&xdbl_bc_ptr);
bld.arg(&b_i);
bld.arg(&xdbl_i);
bld.arg(&ds_i);
bld.arg(&b_off);
bld.arg(&c_off);
unsafe { bld.launch(grid_1d(b * ds)) }
.map_err(|e| format!("gather_bc L{layer_idx}: {e:?}"))?;
}
{
let b_i = b as i32;
let di_i = di as i32;
let ds_i = ds as i32;
let dp = lw.d_param();
let mut bld = self.ctx.stream.launch_builder(&k.ssm_step_fwd);
let y_ssm_ptr = scratch.y.cached_ptr();
let delta_ssm_ptr = scratch.delta.cached_ptr();
let u_ssm_ptr = scratch.u.cached_ptr();
let b_ssm_ptr = scratch.b_buf.cached_ptr();
let c_ssm_ptr = scratch.c_buf.cached_ptr();
bld.arg(&ssm_ptr);
bld.arg(&y_ssm_ptr);
bld.arg(&delta_ssm_ptr);
bld.arg(&u_ssm_ptr);
bld.arg(&b_ssm_ptr);
bld.arg(&c_ssm_ptr);
bld.arg(&aneg_ptr);
bld.arg(&dp);
bld.arg(&b_i);
bld.arg(&di_i);
bld.arg(&ds_i);
unsafe { bld.launch(grid_1d(b * di)) }
.map_err(|e| format!("ssm_step L{layer_idx}: {e:?}"))?;
}
{
let n = (b * di) as i32;
let mut bld = self.ctx.stream.launch_builder(&k.elementwise_mul);
let y_ptr = scratch.y.cached_ptr();
let gs_ptr = scratch.gate_silu.cached_ptr();
bld.arg(&y_ptr);
bld.arg(&y_ptr);
bld.arg(&gs_ptr);
bld.arg(&n);
unsafe { bld.launch(grid_1d(b * di)) }
.map_err(|e| format!("gating L{layer_idx}: {e:?}"))?;
}
let (opw, opw_dt) = lw.out_proj_w();
gpu_gemm_forward_dispatch(
&self.ctx,
&mut scratch.temporal,
&scratch.y,
opw,
opw_dt,
None,
(b, di, dm),
)?;
{
let n = (b * dm) as i32;
let mut bld = self.ctx.stream.launch_builder(&k.residual_add);
let t_ptr = scratch.temporal.cached_ptr();
let r_ptr = scratch.residual.cached_ptr();
bld.arg(&t_ptr);
bld.arg(&r_ptr);
bld.arg(&t_ptr); bld.arg(&n);
unsafe { bld.launch(grid_1d(b * dm)) }
.map_err(|e| format!("residual L{layer_idx}: {e:?}"))?;
}
}
if stop_after_layer.is_none() {
let b_i = b as i32;
let dm_i = dm as i32;
let eps: f32 = cfg.rms_norm_eps;
let mut bld = self.ctx.stream.launch_builder(&k.rmsnorm_fwd);
let t_ptr = scratch.temporal.cached_ptr();
let rms_ptr = scratch.rms_buf.cached_ptr();
bld.arg(&t_ptr);
bld.arg(&rms_ptr);
bld.arg(&t_ptr);
let nfw = weights.norm_f_weight();
bld.arg(&nfw);
bld.arg(&b_i);
bld.arg(&dm_i);
bld.arg(&eps);
unsafe { bld.launch(grid_norm(b, dm)) }.map_err(|e| format!("norm_f: {e:?}"))?;
}
Ok(())
}
pub fn config(&self) -> &MambaConfig {
&self.cfg
}
pub fn batch(&self) -> usize {
self.batch
}
pub fn ctx(&self) -> &GpuCtx {
&self.ctx
}
}
pub struct GpuMambaInferenceMixed {
engine: GpuMambaInference, mixed_weights: GpuMambaMixedWeights,
a_neg_all: GpuBuffer,
graph: Option<cudarc::driver::CudaGraph>,
captured_state_ptr: u64,
captured_scratch_ptr: u64,
}
impl GpuMambaInferenceMixed {
pub fn new(
device: &GpuDevice,
cpu_weights: &MambaWeights,
cfg: MambaConfig,
input_dim: usize,
batch: usize,
bulk_dtype: WeightDtype,
) -> Result<Self, String> {
cfg.validate()?;
let engine = GpuMambaInference::new(device, cpu_weights, cfg, input_dim, batch)?;
let mixed_weights =
GpuMambaMixedWeights::from_cpu(&engine.ctx.stream, cpu_weights, &cfg, bulk_dtype)?;
engine
.ctx
.presize_half_staging_for_step(&cfg, batch, bulk_dtype)?;
let di = cfg.d_inner();
let ds = cfg.d_state;
let total_aneg = cfg.n_layers * di * ds;
let a_neg_all = GpuBuffer::zeros(&engine.ctx.stream, total_aneg)?;
for (layer_idx, lw) in mixed_weights.layers.iter().enumerate() {
let offset = layer_idx * di * ds;
let dst_ptr = a_neg_all.raw_ptr_at(&engine.ctx.stream, offset);
let src_ptr = lw.a_log.ptr();
let n_i = (di * ds) as i32;
let mut builder = engine
.ctx
.stream
.launch_builder(&engine.ctx.kernels.exp_negate);
builder.arg(&dst_ptr);
builder.arg(&src_ptr);
builder.arg(&n_i);
unsafe { builder.launch(grid_1d(di * ds)) }
.map_err(|e| format!("exp_negate mixed L{layer_idx}: {e:?}"))?;
}
Ok(Self {
engine,
mixed_weights,
a_neg_all,
graph: None,
captured_state_ptr: 0,
captured_scratch_ptr: 0,
})
}
pub fn step(
&self,
input: &[f32],
output: &mut [f32],
state: &mut GpuInferenceState,
scratch: &mut GpuInferenceScratch,
) -> Result<(), String> {
scratch.gpu_input.upload(&self.engine.ctx.stream, input)?;
if let Some(ref g) = self.graph {
assert_eq!(state.conv.cached_ptr(), self.captured_state_ptr);
assert_eq!(scratch.gpu_input.cached_ptr(), self.captured_scratch_ptr);
g.launch()
.map_err(|e| format!("graph launch mixed: {e:?}"))?;
} else {
self.step_kernels_mixed(state, scratch)?;
}
self.engine
.ctx
.stream
.synchronize()
.map_err(|e| format!("sync: {e:?}"))?;
scratch.temporal.download(&self.engine.ctx.stream, output)?;
Ok(())
}
pub fn step_gpu_only(
&self,
input: &[f32],
state: &mut GpuInferenceState,
scratch: &mut GpuInferenceScratch,
) -> Result<(), String> {
scratch.gpu_input.upload(&self.engine.ctx.stream, input)?;
if let Some(ref g) = self.graph {
assert_eq!(state.conv.cached_ptr(), self.captured_state_ptr);
assert_eq!(scratch.gpu_input.cached_ptr(), self.captured_scratch_ptr);
g.launch()
.map_err(|e| format!("graph launch mixed: {e:?}"))?;
} else {
self.step_kernels_mixed(state, scratch)?;
}
Ok(())
}
fn step_kernels_mixed(
&self,
state: &mut GpuInferenceState,
scratch: &mut GpuInferenceScratch,
) -> Result<(), String> {
self.engine
.step_kernels_generic(&self.mixed_weights, state, scratch)
}
#[doc(hidden)]
pub fn step_kernels_mixed_native_debug(
&self,
state: &mut GpuInferenceState,
scratch: &mut GpuInferenceMixedScratch,
stop_after_layer: usize,
) -> Result<(), String> {
self.step_kernels_mixed_native_impl(state, scratch, Some(stop_after_layer))
}
pub(super) fn step_kernels_mixed_native(
&self,
state: &mut GpuInferenceState,
scratch: &mut GpuInferenceMixedScratch,
) -> Result<(), String> {
self.step_kernels_mixed_native_impl(state, scratch, None)
}
fn step_kernels_mixed_native_impl(
&self,
state: &mut GpuInferenceState,
scratch: &mut GpuInferenceMixedScratch,
stop_after_layer: Option<usize>,
) -> Result<(), String> {
let engine = &self.engine;
assert!(
engine.identity_proj,
"step_kernels_mixed_native requires identity_proj=true (LLM path)"
);
assert_eq!(
scratch.dtype, self.mixed_weights.bulk_dtype,
"mixed scratch dtype must match mixed weights bulk_dtype"
);
let dt = scratch.dtype;
let b = engine.batch;
let cfg = &engine.cfg;
let dm = cfg.d_model;
let di = cfg.d_inner();
let ds = cfg.d_state;
let dt_rank = cfg.dt_rank();
let xdbl_dim = cfg.xdbl_dim();
let d_conv = cfg.d_conv;
let k = &engine.ctx.kernels;
let w = &self.mixed_weights;
scratch
.residual
.copy_from_raw(&scratch.gpu_input, &engine.ctx.stream)?;
let f32_sz = std::mem::size_of::<f32>() as u64;
let layer_limit = stop_after_layer
.map(|n| n.min(w.n_layers()))
.unwrap_or(w.n_layers());
for layer_idx in 0..layer_limit {
let lw = w.layer(layer_idx);
let conv_ptr = state.conv.cached_ptr() + (state.conv_offset(layer_idx) as u64) * f32_sz;
let ssm_ptr = state.ssm.cached_ptr() + (state.ssm_offset(layer_idx) as u64) * f32_sz;
let aneg_ptr = self.a_neg_all.cached_ptr() + (layer_idx * di * ds) as u64 * f32_sz;
{
let b_i = b as i32;
let dm_i = dm as i32;
let eps: f32 = cfg.rms_norm_eps;
let mut bld = engine
.ctx
.stream
.launch_builder(k.rmsnorm_fwd_f32in_typed.get(dt));
let t_ptr = scratch.temporal.cached_ptr();
let rms_ptr = scratch.rms_buf.cached_ptr();
let res_ptr = scratch.residual.cached_ptr();
bld.arg(&t_ptr);
bld.arg(&rms_ptr);
bld.arg(&res_ptr);
let nw = lw.norm_weight();
bld.arg(&nw);
bld.arg(&b_i);
bld.arg(&dm_i);
bld.arg(&eps);
unsafe { bld.launch(grid_norm(b, dm)) }
.map_err(|e| format!("rmsnorm_f32in L{layer_idx}: {e:?}"))?;
}
let (ipw, ipw_dt) = lw.in_proj_w();
gpu_gemm_typed_forward_raw(
&engine.ctx,
TypedPtr {
ptr: scratch.proj.cached_ptr(),
dtype: dt,
},
TypedPtr {
ptr: scratch.temporal.cached_ptr(),
dtype: dt,
},
TypedPtr {
ptr: ipw,
dtype: ipw_dt,
},
None,
(b, dm, 2 * di),
)?;
{
let b_i = b as i32;
let di_i = di as i32;
let mut bld = engine
.ctx
.stream
.launch_builder(k.split_gate_silu_typed.get(dt));
let xb_ptr = scratch.x_branch.cached_ptr();
bld.arg(&xb_ptr);
let g_ptr = scratch.gate_silu.cached_ptr();
let p_ptr = scratch.proj.cached_ptr();
bld.arg(&g_ptr); bld.arg(&g_ptr); bld.arg(&p_ptr);
bld.arg(&b_i);
bld.arg(&di_i);
unsafe { bld.launch(grid_1d(b * di)) }
.map_err(|e| format!("split_gate_silu L{layer_idx}: {e:?}"))?;
}
{
let b_i = b as i32;
let di_i = di as i32;
let dc_i = d_conv as i32;
let mut bld = engine
.ctx
.stream
.launch_builder(k.conv1d_step_fwd_silu_typed.get(dt));
let u_ptr = scratch.u.cached_ptr();
let xb_ptr2 = scratch.x_branch.cached_ptr();
bld.arg(&u_ptr);
bld.arg(&conv_ptr);
bld.arg(&xb_ptr2);
let cw = lw.conv1d_weight();
let cb = lw.conv1d_bias();
bld.arg(&cw);
bld.arg(&cb);
bld.arg(&b_i);
bld.arg(&di_i);
bld.arg(&dc_i);
unsafe { bld.launch(grid_1d(b * di)) }
.map_err(|e| format!("conv1d_step+silu L{layer_idx}: {e:?}"))?;
}
let (xpw, xpw_dt) = lw.x_proj_w();
gpu_gemm_typed_forward_raw(
&engine.ctx,
TypedPtr {
ptr: scratch.xdbl.cached_ptr(),
dtype: dt,
},
TypedPtr {
ptr: scratch.u.cached_ptr(),
dtype: dt,
},
TypedPtr {
ptr: xpw,
dtype: xpw_dt,
},
None,
(b, di, xdbl_dim),
)?;
{
let b_i = b as i32;
let xdbl_i = xdbl_dim as i32;
let dt_i = dt_rank as i32;
let offset: i32 = 0;
let mut bld = engine
.ctx
.stream
.launch_builder(k.gather_cols_typed.get(dt));
let dtg_ptr = scratch.dt_gather.cached_ptr();
let xdbl_ptr = scratch.xdbl.cached_ptr();
bld.arg(&dtg_ptr);
bld.arg(&xdbl_ptr);
bld.arg(&b_i);
bld.arg(&xdbl_i);
bld.arg(&dt_i);
bld.arg(&offset);
unsafe { bld.launch(grid_1d(b * dt_rank)) }
.map_err(|e| format!("gather_cols dt L{layer_idx}: {e:?}"))?;
}
let (dpw, dpw_dt) = lw.dt_proj_w();
gpu_gemm_typed_forward_raw(
&engine.ctx,
TypedPtr {
ptr: scratch.delta.cached_ptr(),
dtype: dt,
},
TypedPtr {
ptr: scratch.dt_gather.cached_ptr(),
dtype: dt,
},
TypedPtr {
ptr: dpw,
dtype: dpw_dt,
},
Some(lw.dt_proj_b()),
(b, dt_rank, di),
)?;
{
let n = (b * di) as i32;
let mut bld = engine
.ctx
.stream
.launch_builder(k.softplus_fwd_typed.get(dt));
let d_ptr = scratch.delta.cached_ptr();
bld.arg(&d_ptr);
bld.arg(&n);
unsafe { bld.launch(grid_1d(b * di)) }
.map_err(|e| format!("softplus L{layer_idx}: {e:?}"))?;
}
assert!(
ds <= 64,
"ssm_step_fwd_gather_gate_typed requires d_state <= 64 (got {ds}); \
the fused kernel uses on-register arrays sized 64. For d_state > 64 \
route through the unfused ssm_step_fwd_typed + elementwise_mul path."
);
{
let b_i = b as i32;
let di_i = di as i32;
let ds_i = ds as i32;
let xdbl_stride_i = xdbl_dim as i32;
let b_off = dt_rank as i32;
let c_off = (dt_rank + ds) as i32;
let dp = lw.d_param();
let mut bld = engine
.ctx
.stream
.launch_builder(k.ssm_step_fwd_gather_gate_typed.get(dt));
let y_ssm_ptr = scratch.y.cached_ptr();
let delta_ssm_ptr = scratch.delta.cached_ptr();
let u_ssm_ptr = scratch.u.cached_ptr();
let xdbl_ssm_ptr = scratch.xdbl.cached_ptr();
let gs_ptr = scratch.gate_silu.cached_ptr();
bld.arg(&ssm_ptr);
bld.arg(&y_ssm_ptr);
bld.arg(&delta_ssm_ptr);
bld.arg(&u_ssm_ptr);
bld.arg(&xdbl_ssm_ptr);
bld.arg(&gs_ptr);
bld.arg(&aneg_ptr);
bld.arg(&dp);
bld.arg(&b_i);
bld.arg(&di_i);
bld.arg(&ds_i);
bld.arg(&xdbl_stride_i);
bld.arg(&b_off);
bld.arg(&c_off);
unsafe { bld.launch(grid_1d(b * di)) }
.map_err(|e| format!("ssm_step+gather+gate L{layer_idx}: {e:?}"))?;
}
let (opw, opw_dt) = lw.out_proj_w();
gpu_gemm_typed_forward_raw(
&engine.ctx,
TypedPtr {
ptr: scratch.temporal.cached_ptr(),
dtype: dt,
},
TypedPtr {
ptr: scratch.y.cached_ptr(),
dtype: dt,
},
TypedPtr {
ptr: opw,
dtype: opw_dt,
},
None,
(b, di, dm),
)?;
{
let n = (b * dm) as i32;
let mut bld = engine
.ctx
.stream
.launch_builder(k.residual_add_f32_typed.get(dt));
let r_ptr = scratch.residual.cached_ptr();
let t_ptr = scratch.temporal.cached_ptr();
bld.arg(&r_ptr); bld.arg(&r_ptr); bld.arg(&t_ptr); bld.arg(&n);
unsafe { bld.launch(grid_1d(b * dm)) }
.map_err(|e| format!("residual_add_f32 L{layer_idx}: {e:?}"))?;
}
}
if stop_after_layer.is_none() {
let b_i = b as i32;
let dm_i = dm as i32;
let eps: f32 = cfg.rms_norm_eps;
let mut bld = engine
.ctx
.stream
.launch_builder(k.rmsnorm_fwd_f32in_typed.get(dt));
let t_ptr = scratch.temporal.cached_ptr();
let rms_ptr = scratch.rms_buf.cached_ptr();
let res_ptr = scratch.residual.cached_ptr();
bld.arg(&t_ptr);
bld.arg(&rms_ptr);
bld.arg(&res_ptr);
let nfw = w.norm_f_weight();
bld.arg(&nfw);
bld.arg(&b_i);
bld.arg(&dm_i);
bld.arg(&eps);
unsafe { bld.launch(grid_norm(b, dm)) }.map_err(|e| format!("norm_f_mixed: {e:?}"))?;
}
Ok(())
}
pub fn step_mixed_native(
&self,
input: &[f32],
output: &mut [f32],
state: &mut GpuInferenceState,
scratch: &mut GpuInferenceMixedScratch,
) -> Result<(), String> {
scratch.gpu_input.upload(&self.engine.ctx.stream, input)?;
if let Some(ref g) = self.graph {
assert_eq!(state.conv.cached_ptr(), self.captured_state_ptr);
assert_eq!(scratch.gpu_input.cached_ptr(), self.captured_scratch_ptr);
g.launch()
.map_err(|e| format!("graph launch mixed_native: {e:?}"))?;
} else {
self.step_kernels_mixed_native(state, scratch)?;
}
self.engine
.ctx
.stream
.synchronize()
.map_err(|e| format!("sync: {e:?}"))?;
scratch
.temporal
.download_f32(&self.engine.ctx.stream, output)?;
Ok(())
}
pub fn step_gpu_only_mixed_native(
&self,
input: &[f32],
state: &mut GpuInferenceState,
scratch: &mut GpuInferenceMixedScratch,
) -> Result<(), String> {
scratch.gpu_input.upload(&self.engine.ctx.stream, input)?;
if let Some(ref g) = self.graph {
assert_eq!(state.conv.cached_ptr(), self.captured_state_ptr);
assert_eq!(scratch.gpu_input.cached_ptr(), self.captured_scratch_ptr);
g.launch()
.map_err(|e| format!("graph launch mixed_native: {e:?}"))?;
Ok(())
} else {
self.step_kernels_mixed_native(state, scratch)
}
}
pub fn alloc_mixed_scratch(&self) -> Result<GpuInferenceMixedScratch, String> {
GpuInferenceMixedScratch::new(
&self.engine.ctx.stream,
self.engine.batch,
&self.engine.cfg,
self.engine.input_dim,
self.mixed_weights.bulk_dtype,
)
}
pub fn capture_graph_mixed_native(
&mut self,
state: &mut GpuInferenceState,
scratch: &mut GpuInferenceMixedScratch,
) -> Result<(), String> {
let snap_state = state.conv.cached_ptr();
let snap_scratch = scratch.gpu_input.cached_ptr();
let stream = self.engine.ctx.stream.clone();
let graph = crate::mamba_ssm::gpu::graph_capture::capture_into_graph(&stream, || {
self.step_kernels_mixed_native(state, scratch)
})?;
self.graph = Some(graph);
self.captured_state_ptr = snap_state;
self.captured_scratch_ptr = snap_scratch;
Ok(())
}
pub fn capture_graph(
&mut self,
state: &mut GpuInferenceState,
scratch: &mut GpuInferenceScratch,
) -> Result<(), String> {
let snap_state = state.conv.cached_ptr();
let snap_scratch = scratch.gpu_input.cached_ptr();
let stream = self.engine.ctx.stream.clone();
let graph = crate::mamba_ssm::gpu::graph_capture::capture_into_graph(&stream, || {
self.step_kernels_mixed(state, scratch)
})?;
self.graph = Some(graph);
self.captured_state_ptr = snap_state;
self.captured_scratch_ptr = snap_scratch;
Ok(())
}
pub fn alloc_state(&self) -> Result<GpuInferenceState, String> {
self.engine.alloc_state()
}
pub fn alloc_scratch(&self) -> Result<GpuInferenceScratch, String> {
self.engine.alloc_scratch()
}
pub fn config(&self) -> &MambaConfig {
&self.engine.cfg
}
pub fn batch(&self) -> usize {
self.engine.batch
}
pub fn ctx(&self) -> &GpuCtx {
&self.engine.ctx
}
pub fn stream(&self) -> &Arc<cudarc::driver::CudaStream> {
&self.engine.ctx.stream
}
pub fn bulk_dtype(&self) -> WeightDtype {
self.mixed_weights.bulk_dtype
}
pub fn has_graph(&self) -> bool {
self.graph.is_some()
}
pub fn engine_ref(&self) -> &GpuMambaInference {
&self.engine
}
pub fn weights_mixed_ref(&self) -> &GpuMambaMixedWeights {
&self.mixed_weights
}
pub fn a_neg_all_ref(&self) -> &GpuBuffer {
&self.a_neg_all
}
}
enum BackboneEngine {
F32(Box<GpuMambaInference>),
Mixed(Box<GpuMambaInferenceMixed>),
}
enum BackboneScratch {
F32(GpuInferenceScratch),
Mixed(GpuInferenceMixedScratch),
}
impl BackboneScratch {
fn temporal_ptr(&self) -> cudarc::driver::sys::CUdeviceptr {
match self {
BackboneScratch::F32(s) => s.temporal.cached_ptr(),
BackboneScratch::Mixed(s) => s.temporal.cached_ptr(),
}
}
fn temporal_dtype(&self) -> WeightDtype {
match self {
BackboneScratch::F32(_) => WeightDtype::F32,
BackboneScratch::Mixed(s) => s.dtype,
}
}
}
pub struct GpuMambaBackbone {
engine: BackboneEngine,
state: GpuInferenceState,
scratch: BackboneScratch,
}
impl GpuMambaBackbone {
pub fn new(
gpu_ordinal: usize,
cpu_weights: &MambaWeights,
cfg: MambaConfig,
input_dim: usize,
batch: usize,
) -> Result<Self, String> {
Self::new_with_dtype(
gpu_ordinal,
cpu_weights,
cfg,
input_dim,
batch,
WeightDtype::F32,
)
}
pub fn new_with_dtype(
gpu_ordinal: usize,
cpu_weights: &MambaWeights,
cfg: MambaConfig,
input_dim: usize,
batch: usize,
dtype: WeightDtype,
) -> Result<Self, String> {
let device = GpuDevice::new(gpu_ordinal)?;
let (engine, state, scratch) = match dtype {
WeightDtype::F32 => {
let e = GpuMambaInference::new(&device, cpu_weights, cfg, input_dim, batch)?;
let s = e.alloc_state()?;
let sc = BackboneScratch::F32(e.alloc_scratch()?);
(BackboneEngine::F32(Box::new(e)), s, sc)
}
WeightDtype::Bf16 | WeightDtype::F16 => {
let e = GpuMambaInferenceMixed::new(
&device,
cpu_weights,
cfg,
input_dim,
batch,
dtype,
)?;
let s = e.alloc_state()?;
let sc = BackboneScratch::Mixed(e.alloc_mixed_scratch()?);
(BackboneEngine::Mixed(Box::new(e)), s, sc)
}
};
Ok(Self {
engine,
state,
scratch,
})
}
pub fn dtype(&self) -> WeightDtype {
match &self.engine {
BackboneEngine::F32(_) => WeightDtype::F32,
BackboneEngine::Mixed(e) => e.bulk_dtype(),
}
}
pub fn step(&mut self, input: &[f32], output: &mut [f32]) -> Result<(), String> {
match (&self.engine, &mut self.scratch) {
(BackboneEngine::F32(e), BackboneScratch::F32(sc)) => {
e.step(input, output, &mut self.state, sc)
}
(BackboneEngine::Mixed(e), BackboneScratch::Mixed(sc)) => {
e.step_mixed_native(input, output, &mut self.state, sc)
}
_ => Err("engine/scratch dtype mismatch (internal invariant)".to_string()),
}
}
pub fn reset(&mut self) -> Result<(), String> {
let stream = match &self.engine {
BackboneEngine::F32(e) => e.ctx.stream.clone(),
BackboneEngine::Mixed(e) => e.stream().clone(),
};
self.state.reset(&stream)
}
pub fn capture_graph(&mut self) -> Result<(), String> {
let (input_dim, batch, d_model) = match &self.engine {
BackboneEngine::F32(e) => (e.input_dim, e.batch, e.cfg.d_model),
BackboneEngine::Mixed(e) => (
e.engine_ref().input_dim,
e.engine_ref().batch,
e.engine_ref().cfg.d_model,
),
};
let input = vec![0.0f32; batch * input_dim];
let mut output = vec![0.0f32; batch * d_model];
self.step(&input, &mut output)?;
self.reset()?;
match (&mut self.engine, &mut self.scratch) {
(BackboneEngine::F32(e), BackboneScratch::F32(sc)) => {
e.capture_graph(&mut self.state, sc)
}
(BackboneEngine::Mixed(e), BackboneScratch::Mixed(sc)) => {
e.capture_graph_mixed_native(&mut self.state, sc)
}
_ => Err("engine/scratch dtype mismatch".to_string()),
}
}
pub fn config(&self) -> &MambaConfig {
match &self.engine {
BackboneEngine::F32(e) => e.config(),
BackboneEngine::Mixed(e) => e.config(),
}
}
pub fn batch(&self) -> usize {
match &self.engine {
BackboneEngine::F32(e) => e.batch(),
BackboneEngine::Mixed(e) => e.batch(),
}
}
pub fn has_graph(&self) -> bool {
match &self.engine {
BackboneEngine::F32(e) => e.has_graph(),
BackboneEngine::Mixed(e) => e.has_graph(),
}
}
pub fn ctx(&self) -> &GpuCtx {
match &self.engine {
BackboneEngine::F32(e) => &e.ctx,
BackboneEngine::Mixed(e) => e.ctx(),
}
}
pub fn stream(&self) -> &std::sync::Arc<cudarc::driver::CudaStream> {
match &self.engine {
BackboneEngine::F32(e) => &e.ctx.stream,
BackboneEngine::Mixed(e) => e.stream(),
}
}
pub fn step_gpu_only(&mut self, input: &[f32]) -> Result<(), String> {
match (&self.engine, &mut self.scratch) {
(BackboneEngine::F32(e), BackboneScratch::F32(sc)) => {
e.step_gpu_only(input, &mut self.state, sc)
}
(BackboneEngine::Mixed(e), BackboneScratch::Mixed(sc)) => {
e.step_gpu_only_mixed_native(input, &mut self.state, sc)
}
_ => Err("engine/scratch dtype mismatch".to_string()),
}
}
#[doc(hidden)]
pub fn debug_step_partial(
&mut self,
input: &[f32],
layer_limit: usize,
out: &mut [f32],
) -> Result<(), String> {
match (&self.engine, &mut self.scratch) {
(BackboneEngine::F32(e), BackboneScratch::F32(sc)) => {
sc.gpu_input.upload(&e.ctx.stream, input)?;
e.step_kernels_f32_debug(&mut self.state, sc, layer_limit)?;
e.ctx
.stream
.synchronize()
.map_err(|err| format!("sync: {err:?}"))?;
sc.temporal.download(&e.ctx.stream, out)
}
(BackboneEngine::Mixed(e), BackboneScratch::Mixed(sc)) => {
sc.gpu_input.upload(e.stream(), input)?;
e.step_kernels_mixed_native_debug(&mut self.state, sc, layer_limit)?;
e.stream()
.synchronize()
.map_err(|err| format!("sync: {err:?}"))?;
sc.residual.download(e.stream(), out)
}
_ => Err("engine/scratch dtype mismatch".to_string()),
}
}
pub fn temporal_ptr(&self) -> cudarc::driver::sys::CUdeviceptr {
self.scratch.temporal_ptr()
}
pub fn temporal_dtype(&self) -> WeightDtype {
self.scratch.temporal_dtype()
}
pub fn download_temporal(&self, output: &mut [f32]) -> Result<(), String> {
self.stream()
.synchronize()
.map_err(|e| format!("sync: {e:?}"))?;
match &self.scratch {
BackboneScratch::F32(s) => s.temporal.download(self.stream(), output),
BackboneScratch::Mixed(s) => s.temporal.download_f32(self.stream(), output),
}
}
pub fn prefill_sequence(
&mut self,
ip_out_flat: &GpuBuffer,
prefill_scratch: &mut super::backward::GpuMambaTargetScratch,
) -> Result<(), String> {
use super::prefill::{PrefillInputs, gpu_forward_inference_prefill};
match (&self.engine, &mut self.scratch) {
(BackboneEngine::F32(e), BackboneScratch::F32(sc)) => gpu_forward_inference_prefill(
&e.ctx,
&mut sc.temporal,
PrefillInputs {
ip_out_flat,
weights: &e.weights,
a_neg_all: &e.a_neg_all,
},
&mut self.state,
prefill_scratch,
),
(BackboneEngine::Mixed(_), _) => {
Err("mixed backbone: use prefill_sequence_mixed with a \
GpuMambaTargetMixedScratch (native bf16/f16 prefill)"
.to_string())
}
_ => Err("engine/scratch dtype mismatch".to_string()),
}
}
pub fn prefill_sequence_mixed(
&mut self,
ip_out_flat: &GpuBuffer,
prefill_scratch: &mut super::backward::GpuMambaTargetMixedScratch,
) -> Result<(), String> {
use super::prefill::{PrefillInputs, gpu_forward_inference_prefill_mixed};
match (&self.engine, &mut self.scratch) {
(BackboneEngine::Mixed(e), BackboneScratch::Mixed(sc)) => {
gpu_forward_inference_prefill_mixed(
e.ctx(),
&sc.temporal,
PrefillInputs {
ip_out_flat,
weights: e.weights_mixed_ref(),
a_neg_all: e.a_neg_all_ref(),
},
&mut self.state,
prefill_scratch,
)
}
_ => Err("prefill_sequence_mixed requires Mixed backbone + Mixed scratch".to_string()),
}
}
pub fn alloc_prefill_mixed_scratch(
&self,
seq_len: usize,
) -> Result<super::backward::GpuMambaTargetMixedScratch, String> {
let dtype = match &self.engine {
BackboneEngine::Mixed(e) => e.bulk_dtype(),
BackboneEngine::F32(_) => {
return Err("alloc_prefill_mixed_scratch: backbone is F32".to_string());
}
};
let cfg = self.config();
let dims = super::forward::GpuMambaDims {
batch: self.batch(),
seq_len,
n_layers: cfg.n_layers,
d_model: cfg.d_model,
d_inner: cfg.d_inner(),
d_state: cfg.d_state,
d_conv: cfg.d_conv,
dt_rank: cfg.dt_rank(),
xdbl_dim: cfg.xdbl_dim(),
mamba_input_dim: cfg.d_model,
scan_mode: cfg.scan_mode,
rms_norm_eps: cfg.rms_norm_eps,
};
super::backward::GpuMambaTargetMixedScratch::new(self.stream(), &dims, dtype)
}
pub fn alloc_prefill_scratch(
&self,
seq_len: usize,
) -> Result<super::backward::GpuMambaTargetScratch, String> {
let cfg = self.config();
let dims = super::forward::GpuMambaDims {
batch: self.batch(),
seq_len,
n_layers: cfg.n_layers,
d_model: cfg.d_model,
d_inner: cfg.d_inner(),
d_state: cfg.d_state,
d_conv: cfg.d_conv,
dt_rank: cfg.dt_rank(),
xdbl_dim: cfg.xdbl_dim(),
mamba_input_dim: cfg.d_model, scan_mode: cfg.scan_mode,
rms_norm_eps: cfg.rms_norm_eps,
};
super::backward::GpuMambaTargetScratch::new(self.stream(), &dims)
}
}