use super::dtype::WeightDtype;
use super::gemm_bi_triad::GemmBiKernels;
use cudarc::driver::{CudaContext, CudaFunction, CudaModule};
use std::ops::Deref;
use std::sync::Arc;
#[derive(Clone)]
pub(crate) struct CudaModuleAnchors {
_modules: Arc<[Arc<CudaModule>]>,
}
impl CudaModuleAnchors {
pub(crate) fn new(modules: Vec<Arc<CudaModule>>) -> Self {
Self {
_modules: modules.into(),
}
}
}
pub struct TypedKernel {
pub f32: CudaFunction,
pub bf16: CudaFunction,
pub f16: CudaFunction,
}
impl TypedKernel {
pub fn get(&self, dt: WeightDtype) -> &CudaFunction {
match dt {
WeightDtype::F32 => &self.f32,
WeightDtype::Bf16 => &self.bf16,
WeightDtype::F16 => &self.f16,
}
}
}
pub struct HalfKernel {
pub bf16: CudaFunction,
pub f16: CudaFunction,
}
impl HalfKernel {
pub fn get(&self, dt: WeightDtype) -> &CudaFunction {
match dt {
WeightDtype::Bf16 => &self.bf16,
WeightDtype::F16 => &self.f16,
WeightDtype::F32 => {
panic!("HalfKernel has no f32 variant (use the TypedKernel f32 path instead)")
}
}
}
}
pub struct FixedTf32Kernels {
pub m128n64_s2: CudaFunction,
pub m128n64_s3: CudaFunction,
pub m64n64_s2: CudaFunction,
pub m64n64_s3: CudaFunction,
pub m16n32_s4: CudaFunction,
}
pub struct FixedSm120Tf32Kernels {
pub m128n64_s2: CudaFunction,
pub m128n64_s3: CudaFunction,
pub m64n128_s2: CudaFunction,
pub m64n128_s3: CudaFunction,
pub m64n64_s2_producer_warp: CudaFunction,
pub m64n64_s2: CudaFunction,
pub(crate) m64n64_s2_pair_store: CudaFunction,
}
pub struct FixedSm120HalfKernels {
pub m64n64_bk64_s2: HalfKernel,
pub m64n128_bk64_s2: HalfKernel,
pub m128n64_bk32_s3: HalfKernel,
pub m128n128_bk32_s2: HalfKernel,
pub m128n128_bk32_s3: HalfKernel,
}
pub struct FixedSm120FmaPostbiasKernels {
pub m128n64: CudaFunction,
pub m64n128: CudaFunction,
pub m128n96: CudaFunction,
pub m128n64_k4: Option<CudaFunction>,
pub m128n64_k4_rejection: Option<String>,
pub m128n64_t256: Option<CudaFunction>,
pub m128n64_t256_rejection: Option<String>,
pub nobias_m128n64_t256: Option<CudaFunction>,
pub nobias_m128n64_t256_rejection: Option<String>,
}
pub struct MambaKernels {
_modules: CudaModuleAnchors,
compiler_identity: super::kernel_identity::CompilerIdentity,
triad: GemmBiKernels,
pub state_cap: usize,
pub ssm_step_fwd: CudaFunction,
pub ssm_burnin_fwd: CudaFunction,
pub ssm_burnin_fwd_nosave: CudaFunction,
pub ssm_backward_local: CudaFunction,
pub ssm_reduce_d_bc_typed: TypedKernel,
pub ssm_reduce_d_bc_tmajor_typed: TypedKernel,
pub ssm_reduce_d_d: CudaFunction,
pub ssm_reduce_d_a_log: CudaFunction,
pub ssm_reduce_d_a_log_chunks: CudaFunction,
pub conv1d_burnin_fwd_nosave_tiled: CudaFunction,
pub conv1d_burnin_nosave_tiled_typed: TypedKernel,
pub softplus_fwd: CudaFunction,
pub softplus_bwd: CudaFunction,
pub rmsnorm_fwd: CudaFunction,
pub rmsnorm_bwd: CudaFunction,
pub bias_broadcast: CudaFunction,
pub colsum_accumulate: CudaFunction,
pub reduce_sum_axis0: CudaFunction,
pub vec_add_inplace: CudaFunction,
pub exp_negate: CudaFunction,
pub exp_negate2: CudaFunction,
pub gather_cols: CudaFunction,
pub gather_bc_cols: CudaFunction,
pub gather_bc_cols_tmajor: CudaFunction,
pub gather_bc_cols_tmajor_tiled: CudaFunction,
pub gate_mul_silu: CudaFunction,
pub gating_backward: CudaFunction,
pub residual_add: CudaFunction,
pub gather_last_timestep: CudaFunction,
pub cast_f32_to_bf16: CudaFunction,
pub cast_bf16_to_f32: CudaFunction,
pub cast_f16_to_f32: CudaFunction,
pub cast_f32_to_f16: CudaFunction,
pub ssm_burnin_fwd_bf16: CudaFunction,
pub ssm_burnin_fwd_f16: CudaFunction,
pub gating_bwd_typed: TypedKernel,
pub rmsnorm_bwd_typed: TypedKernel,
pub conv1d_burnin_fwd_tiled_typed: TypedKernel,
pub conv1d_bwd_tiled_typed: TypedKernel,
pub conv1d_burnin_bwd_typed: TypedKernel,
pub ssm_backward_local_typed: TypedKernel,
pub pack_xdbl_cols_typed: TypedKernel,
pub softplus_fwd_typed: TypedKernel,
pub rmsnorm_fwd_resadd_typed: TypedKernel,
pub bias_broadcast_typed: TypedKernel,
pub elementwise_mul_typed: TypedKernel,
pub residual_add_typed: TypedKernel,
pub gather_cols_typed: TypedKernel,
pub gather_bc_cols_typed: TypedKernel,
pub gather_bc_cols_tmajor_typed: TypedKernel,
pub gather_bc_cols_tmajor_tiled_typed: TypedKernel,
pub gate_mul_silu_typed: TypedKernel,
pub gate_mul_silu_v_typed: TypedKernel,
pub elementwise_mul_v_typed: TypedKernel,
pub softplus_copy_typed: TypedKernel,
pub ssm_step_fwd_fused_typed: TypedKernel,
pub conv1d_step_fwd_silu_typed: TypedKernel,
pub ssm_burnin_nosave_typed: TypedKernel,
pub softplus_bwd_typed: TypedKernel,
pub gather_last_timestep_typed: TypedKernel,
pub vec_cast_zplus_typed: TypedKernel,
pub concat_halves_typed: TypedKernel,
pub scatter_add_cols_typed: TypedKernel,
pub reduce_bias_typed: TypedKernel,
pub rmsnorm_fwd_f32in_typed: HalfKernel,
pub residual_add_f32_typed: HalfKernel,
pub rmsnorm_bwd_f32in_typed: HalfKernel,
pub ssm_parallel_fwd: CudaFunction,
pub ssm_parallel_fwd_nosave: CudaFunction,
pub ssm_parallel_fwd_typed: TypedKernel,
pub ssm_parallel_fwd_nosave_typed: TypedKernel,
pub ssm_parallel_bwd_typed: TypedKernel,
pub ssm_parallel_bwd_fold_typed: TypedKernel,
pub check_inf_nan_f32: CudaFunction,
pub scale_grads_f32: CudaFunction,
pub scale_grads_skip_f32: CudaFunction,
pub grad_sumsq_partial_f32: CudaFunction,
pub grad_clip_coef_f32: CudaFunction,
pub scale_grads_dev_f32: CudaFunction,
pub grad_region_sumsq_partial_f32: CudaFunction,
pub grad_region_scale_dev_f32: CudaFunction,
pub adamw_step_f32: CudaFunction,
pub adamw_step_f32_capturable: CudaFunction,
pub adamw_step_multi: TypedKernel,
pub gemm_bi_bf16_bf16: CudaFunction,
pub gemm_bi_f16_f16: CudaFunction,
pub gemm_bi_bf16_f32: CudaFunction,
pub gemm_bi_f16_f32: CudaFunction,
pub gemm_bi_f32_f32: CudaFunction,
pub gemm_bi_f32_f32_s2: CudaFunction,
pub gemm_bi_f32_f32_n128_s2: CudaFunction,
pub gemm_bi_nn_tf32: FixedTf32Kernels,
pub gemm_bi_nn_tf32_sm120: Option<FixedSm120Tf32Kernels>,
pub gemm_bi_nn_half_sm120: Option<FixedSm120HalfKernels>,
pub gemm_bi_nn_half_sm120_f32out: Option<FixedSm120HalfKernels>,
pub fixed_sm89_half_pipeline: Option<HalfKernel>,
pub fixed_sm89_half_pipeline_rejection: Option<String>,
pub fixed_sm89_half_swizzle: Option<HalfKernel>,
pub fixed_sm89_half_swizzle_rejection: Option<String>,
pub fixed_sm89_half_s3: Option<HalfKernel>,
pub fixed_sm89_half_s3_rejection: Option<String>,
pub fixed_sm89_tf32_rna_wide: Option<CudaFunction>,
pub fixed_sm89_tf32_rna_wide_rejection: Option<String>,
pub fixed_sm89_tf32_rna_n96: Option<CudaFunction>,
pub fixed_sm89_tf32_rna_n96_rejection: Option<String>,
pub fixed_sm89_half_m64n64_s3_f16: Option<CudaFunction>,
pub fixed_sm89_half_m64n64_s3_f16_rejection: Option<String>,
pub fixed_sm89_half_m128n64_s2_f16: Option<CudaFunction>,
pub fixed_sm89_half_m128n64_s2_f16_rejection: Option<String>,
pub fixed_sm89_f32_n64_copyplan: Option<CudaFunction>,
pub fixed_sm89_f32_n64_copyplan_rejection: Option<String>,
pub fixed_sm120_f32_n64_copyplan: Option<CudaFunction>,
pub fixed_sm120_f32_n64_copyplan_rejection: Option<String>,
pub fixed_sm120_f32_n64_copyplan_t256: Option<CudaFunction>,
pub fixed_sm120_f32_n64_copyplan_t256_rejection: Option<String>,
pub fixed_sm120_f32_m128n64_copyplan_t256: Option<CudaFunction>,
pub fixed_sm120_f32_m128n64_copyplan_t256_rejection: Option<String>,
pub fixed_sm120_f32_n64_sliced: Option<CudaFunction>,
pub fixed_sm120_f32_n64_sliced_rejection: Option<String>,
pub fixed_sm120_fma_postbias: Option<FixedSm120FmaPostbiasKernels>,
pub fixed_sm120_fma_postbias_rejection: Option<String>,
pub matvec_bi_bf16_bf16: CudaFunction,
pub matvec_bi_f16_f16: CudaFunction,
pub matvec_bi_bf16_f32: CudaFunction,
pub matvec_bi_f16_f32: CudaFunction,
pub matvec_bi_f32_f32: CudaFunction,
pub gemm_bi_nn_tc128_typed: HalfKernel,
pub gemm_bi_nn_tc128_f32out: HalfKernel,
pub gemm_bi_nn_tc64_f32out: HalfKernel,
pub gemm_bi_nn_tc16_f32out: HalfKernel,
pub gemm_bi_nn_tc64_typed: HalfKernel,
pub gemm_bi_nn_tc16_typed: HalfKernel,
pub gemm_bi_nn_tcw64_typed: HalfKernel,
pub gemm_bi_nn_tcwn64_typed: HalfKernel,
pub gemm_bi_nn_sm90_typed: Option<HalfKernel>,
pub gemm_bi_nn_sm100_typed: Option<HalfKernel>,
}
impl Deref for MambaKernels {
type Target = GemmBiKernels;
fn deref(&self) -> &Self::Target {
&self.triad
}
}
pub(crate) fn nvrtc_version() -> (i32, i32) {
let mut major: core::ffi::c_int = 0;
let mut minor: core::ffi::c_int = 0;
let rc = unsafe { cudarc::nvrtc::sys::nvrtcVersion(&mut major, &mut minor) };
if rc == cudarc::nvrtc::sys::nvrtcResult::NVRTC_SUCCESS {
(major, minor)
} else {
(0, 0)
}
}
pub(crate) fn kernel_cache_dir() -> Option<std::path::PathBuf> {
let path = match std::env::var("MAMBA_RS_KERNEL_CACHE") {
Ok(v) if matches!(v.trim(), "0" | "off" | "OFF") => None,
Ok(v) if !v.trim().is_empty() => Some(std::path::PathBuf::from(v.trim())),
_ => {
let base = std::env::var("XDG_CACHE_HOME")
.map(std::path::PathBuf::from)
.or_else(|_| {
std::env::var("HOME").map(|home| std::path::PathBuf::from(home).join(".cache"))
})
.ok()?;
Some(base.join("mamba-rs").join("kernels"))
}
}?;
super::kernel_identity::prepare_private_cache_dir(&path)
}
pub fn state_capacity(d_state: usize) -> Result<usize, String> {
if d_state == 0 {
return Err("d_state must be positive".into());
}
if d_state > 256 {
return Err(format!(
"d_state {d_state} exceeds the supported range (reference \
implementations go to 256)"
));
}
Ok(d_state.div_ceil(16) * 16)
}
impl MambaKernels {
pub(in crate::mamba_ssm::gpu) fn inference_terminal_function(
&self,
symbol: &str,
) -> Option<&CudaFunction> {
match symbol {
"gemm_bi_f32_f32_s2" => Some(&self.gemm_bi_f32_f32_s2),
"gemm_bi_f32_f32_n128_s2" => Some(&self.gemm_bi_f32_f32_n128_s2),
"gemm_bi_nn_fixed_sm89_f32_n64_copyplan_v1" => {
self.fixed_sm89_f32_n64_copyplan.as_ref()
}
"gemm_bi_nn_fixed_sm120_f32_n64_copyplan_v1" => {
self.fixed_sm120_f32_n64_copyplan.as_ref()
}
"gemm_bi_nn_fixed_sm120_f32_n64_copyplan_t256_v1" => {
self.fixed_sm120_f32_n64_copyplan_t256.as_ref()
}
"gemm_bi_nn_fixed_sm120_f32_n64_copyplan_m128n64_t256_v1" => {
self.fixed_sm120_f32_m128n64_copyplan_t256.as_ref()
}
"gemm_bi_nn_fixed_sm120_f32_n64_sliced_v1" => self.fixed_sm120_f32_n64_sliced.as_ref(),
"gemm_bi_bf16_bf16" => Some(&self.gemm_bi_bf16_bf16),
"matvec_bi_bf16_bf16" => Some(&self.matvec_bi_bf16_bf16),
"gemm_bi_bf16_f32" => Some(&self.gemm_bi_bf16_f32),
"matvec_bi_bf16_f32" => Some(&self.matvec_bi_bf16_f32),
"gemm_bi_nn_tc128_bf16" => Some(&self.gemm_bi_nn_tc128_typed.bf16),
"gemm_bi_nn_tc128_f32out_bf16" => Some(&self.gemm_bi_nn_tc128_f32out.bf16),
"gemm_bi_nn_tcw64_bf16" => Some(&self.gemm_bi_nn_tcw64_typed.bf16),
"gemm_bi_nn_tcwn64_bf16" => Some(&self.gemm_bi_nn_tcwn64_typed.bf16),
"gemm_bi_nn_tc64_bf16" => Some(&self.gemm_bi_nn_tc64_typed.bf16),
"gemm_bi_nn_tc64_f32out_bf16" => Some(&self.gemm_bi_nn_tc64_f32out.bf16),
"gemm_bi_nn_tc16_bf16" => Some(&self.gemm_bi_nn_tc16_typed.bf16),
"gemm_bi_nn_tc16_f32out_bf16" => Some(&self.gemm_bi_nn_tc16_f32out.bf16),
"gemm_bi_nn_fixed_sm89_tc128_pipeline_v1_bf16" => {
Some(&self.fixed_sm89_half_pipeline.as_ref()?.bf16)
}
"gemm_bi_nn_fixed_sm89_tc128_swizzle_v1_bf16" => {
Some(&self.fixed_sm89_half_swizzle.as_ref()?.bf16)
}
"gemm_bi_nn_fixed_sm89_tc128_s3_v1_bf16" => {
Some(&self.fixed_sm89_half_s3.as_ref()?.bf16)
}
"gemm_bi_nn_sm120_tma_64x64_bk64_s2_bf16" => {
Some(&self.gemm_bi_nn_half_sm120.as_ref()?.m64n64_bk64_s2.bf16)
}
"gemm_bi_nn_sm120_tma_64x64_bk64_s2_f32out_bf16" => Some(
&self
.gemm_bi_nn_half_sm120_f32out
.as_ref()?
.m64n64_bk64_s2
.bf16,
),
"gemm_bi_nn_sm120_tma_64x128_bk64_s2_bf16" => {
Some(&self.gemm_bi_nn_half_sm120.as_ref()?.m64n128_bk64_s2.bf16)
}
"gemm_bi_nn_sm120_tma_64x128_bk64_s2_f32out_bf16" => Some(
&self
.gemm_bi_nn_half_sm120_f32out
.as_ref()?
.m64n128_bk64_s2
.bf16,
),
"gemm_bi_nn_sm120_tma_128x64_bk32_s3_bf16" => {
Some(&self.gemm_bi_nn_half_sm120.as_ref()?.m128n64_bk32_s3.bf16)
}
"gemm_bi_nn_sm120_tma_128x64_bk32_s3_f32out_bf16" => Some(
&self
.gemm_bi_nn_half_sm120_f32out
.as_ref()?
.m128n64_bk32_s3
.bf16,
),
"gemm_bi_nn_sm120_tma_128x128_bk32_s2_bf16" => {
Some(&self.gemm_bi_nn_half_sm120.as_ref()?.m128n128_bk32_s2.bf16)
}
"gemm_bi_nn_sm120_tma_128x128_bk32_s2_f32out_bf16" => Some(
&self
.gemm_bi_nn_half_sm120_f32out
.as_ref()?
.m128n128_bk32_s2
.bf16,
),
"gemm_bi_nn_sm120_tma_128x128_bk32_s3_bf16" => {
Some(&self.gemm_bi_nn_half_sm120.as_ref()?.m128n128_bk32_s3.bf16)
}
"gemm_bi_nn_sm120_tma_128x128_bk32_s3_f32out_bf16" => Some(
&self
.gemm_bi_nn_half_sm120_f32out
.as_ref()?
.m128n128_bk32_s3
.bf16,
),
"gemm_bi_nn_sm90a_wgmma_wg1_bf16" => Some(&self.gemm_bi_nn_sm90_typed.as_ref()?.bf16),
"gemm_bi_nn_sm100_tcgen_c4_bf16" => Some(&self.gemm_bi_nn_sm100_typed.as_ref()?.bf16),
"gemm_bi_f16_f16" => Some(&self.gemm_bi_f16_f16),
"matvec_bi_f16_f16" => Some(&self.matvec_bi_f16_f16),
"gemm_bi_f16_f32" => Some(&self.gemm_bi_f16_f32),
"matvec_bi_f16_f32" => Some(&self.matvec_bi_f16_f32),
"gemm_bi_nn_tc128_f16" => Some(&self.gemm_bi_nn_tc128_typed.f16),
"gemm_bi_nn_tc128_f32out_f16" => Some(&self.gemm_bi_nn_tc128_f32out.f16),
"gemm_bi_nn_tcw64_f16" => Some(&self.gemm_bi_nn_tcw64_typed.f16),
"gemm_bi_nn_tcwn64_f16" => Some(&self.gemm_bi_nn_tcwn64_typed.f16),
"gemm_bi_nn_tc64_f16" => Some(&self.gemm_bi_nn_tc64_typed.f16),
"gemm_bi_nn_tc64_f32out_f16" => Some(&self.gemm_bi_nn_tc64_f32out.f16),
"gemm_bi_nn_tc16_f16" => Some(&self.gemm_bi_nn_tc16_typed.f16),
"gemm_bi_nn_tc16_f32out_f16" => Some(&self.gemm_bi_nn_tc16_f32out.f16),
"gemm_bi_nn_fixed_sm89_tc128_pipeline_v1_f16" => {
Some(&self.fixed_sm89_half_pipeline.as_ref()?.f16)
}
"gemm_bi_nn_fixed_sm89_tc128_swizzle_v1_f16" => {
Some(&self.fixed_sm89_half_swizzle.as_ref()?.f16)
}
"gemm_bi_nn_fixed_sm89_tc128_s3_v1_f16" => Some(&self.fixed_sm89_half_s3.as_ref()?.f16),
"gemm_bi_nn_sm120_tma_64x64_bk64_s2_f16" => {
Some(&self.gemm_bi_nn_half_sm120.as_ref()?.m64n64_bk64_s2.f16)
}
"gemm_bi_nn_sm120_tma_64x64_bk64_s2_f32out_f16" => Some(
&self
.gemm_bi_nn_half_sm120_f32out
.as_ref()?
.m64n64_bk64_s2
.f16,
),
"gemm_bi_nn_sm120_tma_64x128_bk64_s2_f16" => {
Some(&self.gemm_bi_nn_half_sm120.as_ref()?.m64n128_bk64_s2.f16)
}
"gemm_bi_nn_sm120_tma_64x128_bk64_s2_f32out_f16" => Some(
&self
.gemm_bi_nn_half_sm120_f32out
.as_ref()?
.m64n128_bk64_s2
.f16,
),
"gemm_bi_nn_sm120_tma_128x64_bk32_s3_f16" => {
Some(&self.gemm_bi_nn_half_sm120.as_ref()?.m128n64_bk32_s3.f16)
}
"gemm_bi_nn_sm120_tma_128x64_bk32_s3_f32out_f16" => Some(
&self
.gemm_bi_nn_half_sm120_f32out
.as_ref()?
.m128n64_bk32_s3
.f16,
),
"gemm_bi_nn_sm120_tma_128x128_bk32_s2_f16" => {
Some(&self.gemm_bi_nn_half_sm120.as_ref()?.m128n128_bk32_s2.f16)
}
"gemm_bi_nn_sm120_tma_128x128_bk32_s2_f32out_f16" => Some(
&self
.gemm_bi_nn_half_sm120_f32out
.as_ref()?
.m128n128_bk32_s2
.f16,
),
"gemm_bi_nn_sm120_tma_128x128_bk32_s3_f16" => {
Some(&self.gemm_bi_nn_half_sm120.as_ref()?.m128n128_bk32_s3.f16)
}
"gemm_bi_nn_sm120_tma_128x128_bk32_s3_f32out_f16" => Some(
&self
.gemm_bi_nn_half_sm120_f32out
.as_ref()?
.m128n128_bk32_s3
.f16,
),
"gemm_bi_nn_sm90a_wgmma_wg1_f16" => Some(&self.gemm_bi_nn_sm90_typed.as_ref()?.f16),
"gemm_bi_nn_sm100_tcgen_c4_f16" => Some(&self.gemm_bi_nn_sm100_typed.as_ref()?.f16),
"matvec_bi_f32_f32" => Some(&self.matvec_bi_f32_f32),
"gemm_bi_nn_fixed_sm89_m64n64_bk64_s3_v1_f16" => {
self.fixed_sm89_half_m64n64_s3_f16.as_ref()
}
"gemm_bi_nn_fixed_sm89_m128n64_bk64_s2_v1_f16" => {
self.fixed_sm89_half_m128n64_s2_f16.as_ref()
}
"gemm_bi_nn_tf32_v1_m128n64_bk32_s2" => Some(&self.gemm_bi_nn_tf32.m128n64_s2),
"gemm_bi_nn_tf32_v1_m128n64_bk32_s3" => Some(&self.gemm_bi_nn_tf32.m128n64_s3),
"gemm_bi_nn_tf32_v1_m64n64_bk32_s2" => Some(&self.gemm_bi_nn_tf32.m64n64_s2),
"gemm_bi_nn_tf32_v1_m64n64_bk32_s3" => Some(&self.gemm_bi_nn_tf32.m64n64_s3),
"gemm_bi_nn_tf32_v1_m16n32_bk32_s4" => Some(&self.gemm_bi_nn_tf32.m16n32_s4),
"gemm_bi_nn_fixed_rna_wide_tf32_v1_m128n128_bk32_s3" => {
self.fixed_sm89_tf32_rna_wide.as_ref()
}
"gemm_bi_nn_fixed_sm89_rna_tf32_v1_m128n96_bk32_s3" => {
self.fixed_sm89_tf32_rna_n96.as_ref()
}
"gemm_bi_nn_sm80_mma_tf32_v1_m128n128_bk32_s3" => {
self.triad_kernels().tf32_function(symbol)
}
"gemm_bi_nn_sm120_tma_tf32_v1_m128n64_bk32_s2" => {
Some(&self.gemm_bi_nn_tf32_sm120.as_ref()?.m128n64_s2)
}
"gemm_bi_nn_sm120_tma_tf32_v1_m128n64_bk32_s3" => {
Some(&self.gemm_bi_nn_tf32_sm120.as_ref()?.m128n64_s3)
}
"gemm_bi_nn_sm120_tma_tf32_v1_m64n128_bk32_s2" => {
Some(&self.gemm_bi_nn_tf32_sm120.as_ref()?.m64n128_s2)
}
"gemm_bi_nn_sm120_tma_tf32_v1_m64n128_bk32_s3" => {
Some(&self.gemm_bi_nn_tf32_sm120.as_ref()?.m64n128_s3)
}
"gemm_bi_nn_sm120_tma_tf32_v1_m64n64_bk32_s2_producer_warp" => {
Some(&self.gemm_bi_nn_tf32_sm120.as_ref()?.m64n64_s2_producer_warp)
}
"gemm_bi_nn_sm120_tma_tf32_v1_m64n64_bk32_s2" => {
Some(&self.gemm_bi_nn_tf32_sm120.as_ref()?.m64n64_s2)
}
"gemm_bi_nn_sm120_tma_tf32_v1_m64n64_bk32_s2_pair_store" => {
Some(&self.gemm_bi_nn_tf32_sm120.as_ref()?.m64n64_s2_pair_store)
}
"gemm_bi_nn_sm120_tma_fma_v1_fixed_postbias_m128n64_bk16_s2" => {
Some(&self.fixed_sm120_fma_postbias.as_ref()?.m128n64)
}
"gemm_bi_nn_sm120_tma_fma_v1_fixed_postbias_m64n128_bk16_s2" => {
Some(&self.fixed_sm120_fma_postbias.as_ref()?.m64n128)
}
"gemm_bi_nn_sm120_tma_fma_v1_fixed_postbias_m128n96_bk16_s2" => {
Some(&self.fixed_sm120_fma_postbias.as_ref()?.m128n96)
}
"gemm_bi_nn_sm120_tma_fma_v1_fixed_postbias_m128n64_bk16_s2_k4" => {
self.fixed_sm120_fma_postbias.as_ref()?.m128n64_k4.as_ref()
}
"gemm_bi_nn_sm120_tma_fma_v1_fixed_postbias_m128n64_t256_bk16_s2" => self
.fixed_sm120_fma_postbias
.as_ref()?
.m128n64_t256
.as_ref(),
"gemm_bi_nn_sm120_tma_fma_v1_fixed_nobias_m128n64_t256_bk16_s2" => self
.fixed_sm120_fma_postbias
.as_ref()?
.nobias_m128n64_t256
.as_ref(),
_ => None,
}
}
pub fn compile(ctx: &Arc<CudaContext>, arch: &'static str) -> Result<Self, String> {
Self::compile_with_state_cap(ctx, arch, 64)
}
pub fn compile_with_state_cap(
ctx: &Arc<CudaContext>,
arch: &'static str,
state_cap: usize,
) -> Result<Self, String> {
let device_cc = match ctx.compute_capability() {
Ok(device_cc) => Some(device_cc),
Err(error) => {
static UNKNOWN_CC: std::sync::Once = std::sync::Once::new();
super::diagnostics::warn_once(&UNKNOWN_CC, || {
format!(
"the driver did not report a compute capability ({error:?}); only the \
portable kernels are compiled"
)
});
None
}
};
let sm120_board = device_cc.is_some_and(|(major, minor)| {
u32::try_from(major)
.and_then(|major| Ok((major, u32::try_from(minor)?)))
.is_ok_and(super::device::is_sm120_family)
});
if !sm120_board && matches!(arch, "compute_120" | "compute_121") {
static UNQUALIFIED: std::sync::Once = std::sync::Once::new();
super::diagnostics::warn_once(&UNQUALIFIED, || {
format!(
"compute capability {device_cc:?} is not a qualified family; only the \
portable kernels serve it"
)
});
}
let sm120_artifacts = match device_cc {
Some(device_cc) if sm120_board => {
super::gemm_bi_triad::modules::compile_sm120_artifact_set(
ctx,
state_cap,
device_cc,
nvrtc_version(),
)
}
_ => None,
};
let compile = |module_kind| {
super::gemm_bi_triad::modules::compile_module(
super::gemm_bi_triad::modules::CompileModuleRequest {
ctx,
arch,
state_cap,
module_kind,
},
)
};
let (
fixed,
scalar,
sm80,
finalist,
finalist_rejection,
sm89_half,
sm89_half_rejection,
sm89_exact_f32,
sm89_exact_f32_rejection,
sm89_exact_f32_d128,
sm89_exact_f32_d128_rejection,
sm89_tf32_joint,
sm89_tf32_joint_rejection,
specialized,
) = if let Some(artifacts) = sm120_artifacts {
(
artifacts.fixed,
artifacts.scalar,
artifacts.sm80,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
artifacts.specialized,
)
} else {
let fixed = compile(super::kernel_identity::ModuleKind::Fixed)?;
let scalar = compile(super::kernel_identity::ModuleKind::TriadScalar)?;
let sm80 = compile(super::kernel_identity::ModuleKind::TriadSm80)?;
let (finalist, finalist_rejection) =
if matches!((arch, device_cc), ("sm_89", Some((8, 9)))) {
match compile(super::kernel_identity::ModuleKind::TriadSm89Finalist) {
Ok(module) => (Some(module), None),
Err(error) => (None, Some(error)),
}
} else {
(None, None)
};
let (sm89_half, sm89_half_rejection) =
if matches!((arch, device_cc), ("sm_89", Some((8, 9)))) {
match compile(super::kernel_identity::ModuleKind::TriadSm89Half) {
Ok(module) => (Some(module), None),
Err(error) => (None, Some(error)),
}
} else {
(None, None)
};
let (sm89_exact_f32, sm89_exact_f32_rejection) =
if matches!((arch, device_cc), ("sm_89", Some((8, 9)))) {
match compile(super::kernel_identity::ModuleKind::TriadSm89ExactF32) {
Ok(module) => (Some(module), None),
Err(error) => (None, Some(error)),
}
} else {
(None, None)
};
let (sm89_exact_f32_d128, sm89_exact_f32_d128_rejection) =
if matches!((arch, device_cc), ("sm_89", Some((8, 9)))) {
match compile(super::kernel_identity::ModuleKind::TriadSm89ExactF32D128) {
Ok(module) => (Some(module), None),
Err(error) => (None, Some(error)),
}
} else {
(None, None)
};
let (sm89_tf32_joint, sm89_tf32_joint_rejection) =
if matches!((arch, device_cc), ("sm_89", Some((8, 9)))) {
match compile(super::kernel_identity::ModuleKind::TriadSm89Tf32Joint) {
Ok(module) => (Some(module), None),
Err(error) => (None, Some(error)),
}
} else {
(None, None)
};
let specialized = match (arch, device_cc) {
("sm_90a", Some((9, 0))) => compile(super::kernel_identity::ModuleKind::TriadSm90a)
.ok()
.and_then(|module| {
super::gemm_bi_triad::modules::qualify_specialized_module(module).ok()
}),
("sm_100a", Some(device_cc @ (10, 0))) | ("sm_103a", Some(device_cc @ (10, 3))) => {
super::gemm_bi_triad::modules::compile_sm100_optional(ctx, state_cap, device_cc)
}
("sm_110a", Some(device_cc @ (11, 0))) => {
super::gemm_bi_triad::modules::compile_sm100_optional(ctx, state_cap, device_cc)
}
_ => None,
};
(
fixed,
scalar,
sm80,
finalist,
finalist_rejection,
sm89_half,
sm89_half_rejection,
sm89_exact_f32,
sm89_exact_f32_rejection,
sm89_exact_f32_d128,
sm89_exact_f32_d128_rejection,
sm89_tf32_joint,
sm89_tf32_joint_rejection,
specialized,
)
};
let compiler_identity = fixed.compiler_identity;
let (fixed_sm89_half_pipeline, fixed_sm89_half_pipeline_rejection) =
super::gemm_bi_triad::modules::load_fixed_sm89_half_pipeline(ctx, &fixed);
let (fixed_sm89_half_swizzle, fixed_sm89_half_swizzle_rejection) =
super::gemm_bi_triad::modules::load_fixed_sm89_half_swizzle(ctx, &fixed);
let (fixed_sm89_half_s3, fixed_sm89_half_s3_rejection) =
super::gemm_bi_triad::modules::load_fixed_sm89_half_s3(ctx, &fixed);
let (fixed_sm89_tf32_rna_wide, fixed_sm89_tf32_rna_wide_rejection) =
super::gemm_bi_triad::modules::load_fixed_sm89_rna_wide(ctx, &fixed);
let (fixed_sm89_tf32_rna_n96, fixed_sm89_tf32_rna_n96_rejection) =
super::gemm_bi_triad::modules::load_fixed_sm89_rna_n96(ctx, &fixed);
let (fixed_sm89_half_m64n64_s3_f16, fixed_sm89_half_m64n64_s3_f16_rejection) =
super::gemm_bi_triad::modules::load_fixed_sm89_half_m64n64_s3(ctx, &fixed);
let (fixed_sm89_half_m128n64_s2_f16, fixed_sm89_half_m128n64_s2_f16_rejection) =
super::gemm_bi_triad::modules::load_fixed_sm89_half_m128n64_s2(ctx, &fixed);
let (fixed_sm89_f32_n64_copyplan, fixed_sm89_f32_n64_copyplan_rejection) =
super::gemm_bi_triad::modules::load_fixed_sm89_f32_n64_copyplan(ctx, &fixed);
let (fixed_sm120_f32_n64_copyplan, fixed_sm120_f32_n64_copyplan_rejection) =
super::gemm_bi_triad::modules::load_fixed_sm120_f32_n64_copyplan(ctx, &fixed);
let (fixed_sm120_f32_n64_copyplan_t256, fixed_sm120_f32_n64_copyplan_t256_rejection) =
super::gemm_bi_triad::modules::load_fixed_sm120_f32_n64_copyplan_t256(ctx, &fixed);
let (
fixed_sm120_f32_m128n64_copyplan_t256,
fixed_sm120_f32_m128n64_copyplan_t256_rejection,
) = super::gemm_bi_triad::modules::load_fixed_sm120_f32_m128n64_copyplan_t256(ctx, &fixed);
let (fixed_sm120_f32_n64_sliced, fixed_sm120_f32_n64_sliced_rejection) =
super::gemm_bi_triad::modules::load_fixed_sm120_f32_n64_sliced(ctx, &fixed);
let (fixed_sm120_fma_postbias, fixed_sm120_fma_postbias_rejection) =
super::gemm_bi_triad::modules::load_fixed_sm120_fma_postbias(ctx, &fixed);
let triad = GemmBiKernels::load(
ctx,
super::gemm_bi_triad::modules::GemmBiModuleSet {
fixed_artifact: fixed.artifact_identity,
scalar,
sm80,
finalist,
finalist_compile_rejection: finalist_rejection,
sm89_half,
sm89_half_compile_rejection: sm89_half_rejection,
sm89_exact_f32,
sm89_exact_f32_compile_rejection: sm89_exact_f32_rejection,
sm89_exact_f32_d128,
sm89_exact_f32_d128_compile_rejection: sm89_exact_f32_d128_rejection,
sm89_tf32_joint,
sm89_tf32_joint_compile_rejection: sm89_tf32_joint_rejection,
specialized,
},
)?;
if let Some(reason) = triad.portable_tf32_rejection() {
static PORTABLE: std::sync::Once = std::sync::Once::new();
super::diagnostics::warn_once(&PORTABLE, || {
format!(
"the portable TF32 routes are not bound on this board ({reason}); the \
exact f32 kernels serve every TF32 request"
)
});
}
let excluded = triad.tf32_excluded_symbols();
if !excluded.is_empty() {
static EXCLUDED: std::sync::Once = std::sync::Once::new();
super::diagnostics::warn_once(&EXCLUDED, || {
format!(
"{} TF32 route(s) are excluded on this toolkit and decline to the exact \
family: {}",
excluded.len(),
excluded
.iter()
.map(|exclusion| exclusion.reason.as_str())
.collect::<Vec<_>>()
.join("; ")
)
});
}
if let Some(reason) = triad.specialized_tf32_rejection() {
static SPECIALIZED: std::sync::Once = std::sync::Once::new();
super::diagnostics::warn_once(&SPECIALIZED, || {
format!(
"the specialized TF32 module is not bound on this board ({reason}); the \
portable or exact kernels serve every TF32 request"
)
});
}
if let Some(reason) = triad.finalist_tf32_rejection() {
static FINALIST: std::sync::Once = std::sync::Once::new();
super::diagnostics::warn_once(&FINALIST, || {
format!(
"the optional Ada TF32 finalist is not bound ({reason}); the portable or exact kernels remain available"
)
});
}
if let Some(reason) = triad.sm89_half_rejection() {
static HALF_MODULE: std::sync::Once = std::sync::Once::new();
super::diagnostics::warn_once(&HALF_MODULE, || {
format!(
"the optional Ada half-Triad module is not bound ({reason}); the existing typed kernels remain available"
)
});
}
let half_excluded = triad.sm89_half_exclusions();
if !half_excluded.is_empty() {
static HALF_EXCLUDED: std::sync::Once = std::sync::Once::new();
super::diagnostics::warn_once(&HALF_EXCLUDED, || {
format!(
"{} Ada half-Triad symbol(s) failed resource admission while their siblings remain bound: {}",
half_excluded.len(),
half_excluded
.iter()
.map(|exclusion| format!("{}: {}", exclusion.symbol, exclusion.reason))
.collect::<Vec<_>>()
.join("; ")
)
});
}
if let Some(reason) = triad.sm89_exact_f32_rejection() {
static EXACT_F32_MODULE: std::sync::Once = std::sync::Once::new();
super::diagnostics::warn_once(&EXACT_F32_MODULE, || {
format!(
"the optional Ada exact-F32 Triad module is not bound ({reason}); the existing exact kernels remain available"
)
});
}
for exclusion in triad.sm89_exact_f32_exclusions() {
static D768_IN: std::sync::Once = std::sync::Once::new();
static D768_OUT: std::sync::Once = std::sync::Once::new();
static PRISM: std::sync::Once = std::sync::Once::new();
let once = match exclusion.symbol {
super::gemm_bi_triad::D768_IN_FUSED_SYMBOL => &D768_IN,
super::gemm_bi_triad::D768_OUT_RAW_SYMBOL => &D768_OUT,
super::gemm_bi_triad::PRISM_RAW_SYMBOL => &PRISM,
_ => continue,
};
super::diagnostics::warn_once(once, || {
format!(
"Ada exact-F32 Triad symbol {} is excluded while its siblings remain available: {}",
exclusion.symbol, exclusion.reason
)
});
}
if let Some(reason) = triad.sm89_exact_f32_d128_rejection() {
static EXACT_F32_D128_MODULE: std::sync::Once = std::sync::Once::new();
super::diagnostics::warn_once(&EXACT_F32_D128_MODULE, || {
format!(
"the optional Ada exact-F32 d128 Triad module is not bound ({reason}); the SplitM64 kernels remain available"
)
});
}
for exclusion in triad.sm89_exact_f32_d128_exclusions() {
static D128_IN: std::sync::Once = std::sync::Once::new();
static D128_OUT: std::sync::Once = std::sync::Once::new();
let once = match exclusion.symbol {
super::gemm_bi_triad::D128_IN_SYMBOL => &D128_IN,
super::gemm_bi_triad::D128_OUT_SYMBOL => &D128_OUT,
_ => continue,
};
super::diagnostics::warn_once(once, || {
format!(
"Ada exact-F32 d128 Triad symbol {} is excluded while its sibling remains available: {}",
exclusion.symbol, exclusion.reason
)
});
}
if let Some(reason) = triad.sm89_tf32_joint_rejection() {
static TF32_JOINT_MODULE: std::sync::Once = std::sync::Once::new();
super::diagnostics::warn_once(&TF32_JOINT_MODULE, || {
format!(
"the optional Ada TF32 joint module is not bound ({reason}); the existing deterministic TF32 kernels remain available"
)
});
}
for exclusion in triad.sm89_tf32_joint_exclusions() {
static TRANSPOSE: std::sync::Once = std::sync::Once::new();
static TN_N96: std::sync::Once = std::sync::Once::new();
static TN_M64N64: std::sync::Once = std::sync::Once::new();
static TN_M64N96_S2: std::sync::Once = std::sync::Once::new();
static NN_N96: std::sync::Once = std::sync::Once::new();
static NN_N96_BASELINE: std::sync::Once = std::sync::Once::new();
static NT_A_LDMATRIX_N96: std::sync::Once = std::sync::Once::new();
let once = match exclusion.symbol {
super::gemm_bi_triad::TN_PRE_RNA_TRANSPOSE_SYMBOL => &TRANSPOSE,
super::gemm_bi_triad::TN_PRE_RNA_N96_SYMBOL => &TN_N96,
super::gemm_bi_triad::TN_PRE_RNA_M64N64_SYMBOL => &TN_M64N64,
super::gemm_bi_triad::TN_PRE_RNA_M64N96_S2_SYMBOL => &TN_M64N96_S2,
super::gemm_bi_triad::NN_ADD_HALF_DIRECT_N96_SYMBOL => &NN_N96,
super::gemm_bi_triad::NN_ADD_HALF_N96_SYMBOL => &NN_N96_BASELINE,
super::gemm_bi_triad::NT_A_LDMATRIX_N96_SYMBOL => &NT_A_LDMATRIX_N96,
_ => continue,
};
super::diagnostics::warn_once(once, || {
format!(
"Ada TF32 joint symbol {} is excluded while its siblings remain available: {}",
exclusion.symbol, exclusion.reason
)
});
}
let module = fixed.module;
let get = |name: &str| -> Result<CudaFunction, String> {
module
.load_function(name)
.map_err(|e| format!("Kernel '{name}' not found: {e:?}"))
};
let load_typed = |base: &str| -> Result<TypedKernel, String> {
Ok(TypedKernel {
f32: get(&format!("{base}_f32"))?,
bf16: get(&format!("{base}_bf16"))?,
f16: get(&format!("{base}_f16"))?,
})
};
let load_half = |base: &str| -> Result<HalfKernel, String> {
Ok(HalfKernel {
bf16: get(&format!("{base}_bf16"))?,
f16: get(&format!("{base}_f16"))?,
})
};
let load_half_dynsmem = |base: &str, bytes: i32| -> Result<HalfKernel, String> {
let k = load_half(base)?;
for f in [&k.bf16, &k.f16] {
f.set_attribute(
cudarc::driver::sys::CUfunction_attribute_enum::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
bytes,
)
.map_err(|e| format!("set MAX_DYNAMIC_SHARED for {base}: {e:?}"))?;
}
Ok(k)
};
let load_sm120_half = |suffix: &str| -> Result<FixedSm120HalfKernels, String> {
let kernels = FixedSm120HalfKernels {
m64n64_bk64_s2: load_half(&format!("gemm_bi_nn_sm120_tma_64x64_bk64_s2{suffix}"))?,
m64n128_bk64_s2: load_half(&format!(
"gemm_bi_nn_sm120_tma_64x128_bk64_s2{suffix}"
))?,
m128n64_bk32_s3: load_half(&format!(
"gemm_bi_nn_sm120_tma_128x64_bk32_s3{suffix}"
))?,
m128n128_bk32_s2: load_half(&format!(
"gemm_bi_nn_sm120_tma_128x128_bk32_s2{suffix}"
))?,
m128n128_bk32_s3: load_half(&format!(
"gemm_bi_nn_sm120_tma_128x128_bk32_s3{suffix}"
))?,
};
for (tile, bytes) in [
(&kernels.m64n64_bk64_s2, 32_896),
(&kernels.m64n128_bk64_s2, 49_280),
(&kernels.m128n64_bk32_s3, 36_992),
(&kernels.m128n128_bk32_s2, 32_896),
(&kernels.m128n128_bk32_s3, 49_280),
] {
for function in [&tile.bf16, &tile.f16] {
function
.set_attribute(
cudarc::driver::sys::CUfunction_attribute_enum::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
bytes,
)
.map_err(|error| {
format!(
"set MAX_DYNAMIC_SHARED for Fixed SM120 half{suffix}: {error:?}"
)
})?;
}
}
Ok(kernels)
};
Ok(Self {
state_cap,
compiler_identity,
triad,
ssm_step_fwd: get("ssm_step_forward")?,
ssm_burnin_fwd: get("ssm_burnin_forward")?,
ssm_burnin_fwd_nosave: get("ssm_burnin_forward_nosave")?,
ssm_backward_local: get("ssm_backward_local")?,
ssm_reduce_d_bc_typed: TypedKernel {
f32: get("ssm_reduce_d_BC_f32")?,
bf16: get("ssm_reduce_d_BC_bf16")?,
f16: get("ssm_reduce_d_BC_f16")?,
},
ssm_reduce_d_bc_tmajor_typed: TypedKernel {
f32: get("ssm_reduce_d_BC_tmajor_f32")?,
bf16: get("ssm_reduce_d_BC_tmajor_bf16")?,
f16: get("ssm_reduce_d_BC_tmajor_f16")?,
},
ssm_reduce_d_d: get("ssm_reduce_d_D")?,
ssm_reduce_d_a_log: get("ssm_reduce_d_a_log")?,
ssm_reduce_d_a_log_chunks: get("ssm_reduce_d_a_log_chunks")?,
conv1d_burnin_fwd_nosave_tiled: get("conv1d_burnin_forward_nosave_tiled_f32")?,
conv1d_burnin_nosave_tiled_typed: load_typed("conv1d_burnin_forward_nosave_tiled")?,
softplus_fwd: get("softplus_forward")?,
softplus_bwd: get("softplus_backward")?,
rmsnorm_fwd: get("rmsnorm_forward")?,
rmsnorm_bwd: get("rmsnorm_backward")?,
bias_broadcast: get("bias_broadcast")?,
colsum_accumulate: get("colsum_accumulate")?,
reduce_sum_axis0: get("reduce_sum_axis0")?,
vec_add_inplace: get("vec_add_inplace")?,
exp_negate: get("exp_negate")?,
exp_negate2: get("exp_negate2")?,
gather_cols: get("gather_cols")?,
gather_bc_cols: get("gather_bc_cols")?,
gather_bc_cols_tmajor: get("gather_bc_cols_tmajor")?,
gather_bc_cols_tmajor_tiled: get("gather_bc_cols_tmajor_tiled")?,
gate_mul_silu: get("gate_mul_silu")?,
gating_backward: get("gating_backward")?,
residual_add: get("residual_add")?,
gather_last_timestep: get("gather_last_timestep")?,
cast_f32_to_bf16: get("cast_f32_to_bf16")?,
cast_f32_to_f16: get("cast_f32_to_f16")?,
cast_bf16_to_f32: get("cast_bf16_to_f32")?,
cast_f16_to_f32: get("cast_f16_to_f32")?,
ssm_burnin_fwd_bf16: get("ssm_burnin_forward_bf16")?,
ssm_burnin_fwd_f16: get("ssm_burnin_forward_f16")?,
ssm_parallel_fwd: get("ssm_parallel_scan_fwd")?,
ssm_parallel_fwd_nosave: get("ssm_parallel_scan_fwd_nosave")?,
ssm_parallel_fwd_typed: TypedKernel {
f32: get("ssm_parallel_scan_fwd")?,
bf16: get("ssm_parallel_scan_fwd_bf16")?,
f16: get("ssm_parallel_scan_fwd_f16")?,
},
ssm_parallel_fwd_nosave_typed: TypedKernel {
f32: get("ssm_parallel_scan_fwd_nosave")?,
bf16: get("ssm_parallel_scan_fwd_nosave_bf16")?,
f16: get("ssm_parallel_scan_fwd_nosave_f16")?,
},
ssm_parallel_bwd_typed: TypedKernel {
f32: get("ssm_parallel_scan_bwd_f32")?,
bf16: get("ssm_parallel_scan_bwd_bf16")?,
f16: get("ssm_parallel_scan_bwd_f16")?,
},
ssm_parallel_bwd_fold_typed: {
let k = TypedKernel {
f32: get("ssm_parallel_scan_bwd_fold_f32")?,
bf16: get("ssm_parallel_scan_bwd_fold_bf16")?,
f16: get("ssm_parallel_scan_bwd_fold_f16")?,
};
for f in [&k.f32, &k.bf16, &k.f16] {
f.set_attribute(
cudarc::driver::sys::CUfunction_attribute_enum::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
67_584,
)
.map_err(|e| {
format!("set MAX_DYNAMIC_SHARED for scan_bwd_fold: {e:?}")
})?;
}
k
},
check_inf_nan_f32: get("check_inf_nan_f32")?,
scale_grads_f32: get("scale_grads_f32")?,
scale_grads_skip_f32: get("scale_grads_skip_f32")?,
grad_sumsq_partial_f32: get("grad_sumsq_partial_f32")?,
grad_clip_coef_f32: get("grad_clip_coef_f32")?,
scale_grads_dev_f32: get("scale_grads_dev_f32")?,
grad_region_sumsq_partial_f32: get("grad_region_sumsq_partial_f32")?,
grad_region_scale_dev_f32: get("grad_region_scale_dev_f32")?,
adamw_step_f32: get("adamw_step_f32")?,
adamw_step_f32_capturable: get("adamw_step_f32_capturable")?,
adamw_step_multi: load_typed("adamw_step_multi")?,
gemm_bi_bf16_bf16: get("gemm_bi_bf16_bf16")?,
gemm_bi_f16_f16: get("gemm_bi_f16_f16")?,
gemm_bi_bf16_f32: get("gemm_bi_bf16_f32")?,
gemm_bi_f16_f32: get("gemm_bi_f16_f32")?,
gemm_bi_f32_f32: get("gemm_bi_f32_f32")?,
gemm_bi_f32_f32_s2: get("gemm_bi_f32_f32_s2")?,
gemm_bi_f32_f32_n128_s2: get("gemm_bi_f32_f32_n128_s2")?,
fixed_sm89_half_pipeline,
fixed_sm89_half_pipeline_rejection,
fixed_sm89_half_swizzle,
fixed_sm89_half_swizzle_rejection,
fixed_sm89_half_s3,
fixed_sm89_half_s3_rejection,
fixed_sm89_tf32_rna_wide,
fixed_sm89_tf32_rna_wide_rejection,
fixed_sm89_tf32_rna_n96,
fixed_sm89_tf32_rna_n96_rejection,
fixed_sm89_half_m64n64_s3_f16,
fixed_sm89_half_m64n64_s3_f16_rejection,
fixed_sm89_half_m128n64_s2_f16,
fixed_sm89_half_m128n64_s2_f16_rejection,
fixed_sm89_f32_n64_copyplan,
fixed_sm89_f32_n64_copyplan_rejection,
fixed_sm120_f32_n64_copyplan,
fixed_sm120_f32_n64_copyplan_rejection,
fixed_sm120_f32_n64_copyplan_t256,
fixed_sm120_f32_n64_copyplan_t256_rejection,
fixed_sm120_f32_m128n64_copyplan_t256,
fixed_sm120_f32_m128n64_copyplan_t256_rejection,
fixed_sm120_f32_n64_sliced,
fixed_sm120_f32_n64_sliced_rejection,
fixed_sm120_fma_postbias,
fixed_sm120_fma_postbias_rejection,
gemm_bi_nn_tf32: {
let kernels = FixedTf32Kernels {
m128n64_s2: get("gemm_bi_nn_tf32_v1_m128n64_bk32_s2")?,
m128n64_s3: get("gemm_bi_nn_tf32_v1_m128n64_bk32_s3")?,
m64n64_s2: get("gemm_bi_nn_tf32_v1_m64n64_bk32_s2")?,
m64n64_s3: get("gemm_bi_nn_tf32_v1_m64n64_bk32_s3")?,
m16n32_s4: get("gemm_bi_nn_tf32_v1_m16n32_bk32_s4")?,
};
for (function, bytes) in [
(&kernels.m128n64_s2, 55_296),
(&kernels.m128n64_s3, 82_944),
(&kernels.m64n64_s2, 32_768),
(&kernels.m64n64_s3, 55_296),
(&kernels.m16n32_s4, 29_696),
] {
function
.set_attribute(
cudarc::driver::sys::CUfunction_attribute_enum::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
bytes,
)
.map_err(|error| {
format!("set MAX_DYNAMIC_SHARED for Fixed TF32: {error:?}")
})?;
}
kernels
},
gemm_bi_nn_tf32_sm120: if sm120_board
&& matches!(arch, "sm_120" | "sm_121" | "compute_120" | "compute_121")
{
let kernels = FixedSm120Tf32Kernels {
m128n64_s2: get("gemm_bi_nn_sm120_tma_tf32_v1_m128n64_bk32_s2")?,
m128n64_s3: get("gemm_bi_nn_sm120_tma_tf32_v1_m128n64_bk32_s3")?,
m64n128_s2: get("gemm_bi_nn_sm120_tma_tf32_v1_m64n128_bk32_s2")?,
m64n128_s3: get("gemm_bi_nn_sm120_tma_tf32_v1_m64n128_bk32_s3")?,
m64n64_s2_producer_warp: get(
"gemm_bi_nn_sm120_tma_tf32_v1_m64n64_bk32_s2_producer_warp",
)?,
m64n64_s2: get("gemm_bi_nn_sm120_tma_tf32_v1_m64n64_bk32_s2")?,
m64n64_s2_pair_store: get(
"gemm_bi_nn_sm120_tma_tf32_v1_m64n64_bk32_s2_pair_store",
)?,
};
for (function, bytes) in [
(&kernels.m128n64_s2, 49_280),
(&kernels.m128n64_s3, 73_856),
(&kernels.m64n128_s2, 49_280),
(&kernels.m64n128_s3, 73_856),
(&kernels.m64n64_s2_producer_warp, 32_896),
(&kernels.m64n64_s2, 32_896),
(&kernels.m64n64_s2_pair_store, 32_896),
] {
function
.set_attribute(
cudarc::driver::sys::CUfunction_attribute_enum::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
bytes,
)
.map_err(|error| {
format!("set MAX_DYNAMIC_SHARED for Fixed SM120 TF32: {error:?}")
})?;
}
Some(kernels)
} else {
None
},
gemm_bi_nn_half_sm120: if sm120_board
&& matches!(arch, "sm_120" | "sm_121" | "compute_120" | "compute_121")
{
Some(load_sm120_half("")?)
} else {
None
},
gemm_bi_nn_half_sm120_f32out: if sm120_board
&& matches!(arch, "sm_120" | "sm_121" | "compute_120" | "compute_121")
{
Some(load_sm120_half("_f32out")?)
} else {
None
},
matvec_bi_bf16_bf16: get("matvec_bi_bf16_bf16")?,
matvec_bi_f16_f16: get("matvec_bi_f16_f16")?,
matvec_bi_bf16_f32: get("matvec_bi_bf16_f32")?,
matvec_bi_f16_f32: get("matvec_bi_f16_f32")?,
matvec_bi_f32_f32: get("matvec_bi_f32_f32")?,
softplus_fwd_typed: load_typed("softplus_forward")?,
rmsnorm_fwd_resadd_typed: load_typed("rmsnorm_forward_resadd_f32in")?,
bias_broadcast_typed: load_typed("bias_broadcast")?,
elementwise_mul_typed: load_typed("elementwise_mul")?,
residual_add_typed: load_typed("residual_add")?,
gather_cols_typed: load_typed("gather_cols")?,
gather_bc_cols_typed: load_typed("gather_bc_cols")?,
gather_bc_cols_tmajor_typed: load_typed("gather_bc_cols_tmajor")?,
gather_bc_cols_tmajor_tiled_typed: load_typed("gather_bc_cols_tmajor_tiled")?,
gate_mul_silu_typed: load_typed("gate_mul_silu")?,
gate_mul_silu_v_typed: load_typed("gate_mul_silu_v")?,
elementwise_mul_v_typed: load_typed("elementwise_mul_v")?,
softplus_copy_typed: load_typed("softplus_copy")?,
ssm_step_fwd_fused_typed: load_typed("ssm_step_forward_fused")?,
conv1d_step_fwd_silu_typed: load_typed("conv1d_step_forward_silu")?,
ssm_burnin_nosave_typed: load_typed("ssm_burnin_forward_nosave")?,
softplus_bwd_typed: load_typed("softplus_backward")?,
gather_last_timestep_typed: load_typed("gather_last_timestep")?,
vec_cast_zplus_typed: load_typed("vec_cast_zplus")?,
concat_halves_typed: load_typed("concat_halves")?,
scatter_add_cols_typed: load_typed("scatter_add_cols")?,
reduce_bias_typed: load_typed("reduce_bias")?,
gating_bwd_typed: load_typed("gating_backward")?,
rmsnorm_bwd_typed: load_typed("rmsnorm_backward")?,
conv1d_burnin_bwd_typed: load_typed("conv1d_burnin_backward")?,
conv1d_burnin_fwd_tiled_typed: load_typed("conv1d_burnin_forward_tiled")?,
conv1d_bwd_tiled_typed: load_typed("conv1d_bwd_tiled")?,
ssm_backward_local_typed: load_typed("ssm_backward_local")?,
pack_xdbl_cols_typed: TypedKernel {
f32: get("pack_xdbl_cols_f32")?,
bf16: get("pack_xdbl_cols_bf16")?,
f16: get("pack_xdbl_cols_f16")?,
},
rmsnorm_fwd_f32in_typed: load_half("rmsnorm_forward_f32in")?,
rmsnorm_bwd_f32in_typed: load_half("rmsnorm_backward_f32in")?,
residual_add_f32_typed: load_half("residual_add_f32")?,
gemm_bi_nn_tc128_typed: load_half_dynsmem("gemm_bi_nn_tc128", 71_680)?,
gemm_bi_nn_tc128_f32out: load_half_dynsmem("gemm_bi_nn_tc128_f32out", 71_680)?,
gemm_bi_nn_tc64_f32out: load_half("gemm_bi_nn_tc64_f32out")?,
gemm_bi_nn_tc16_f32out: load_half("gemm_bi_nn_tc16_f32out")?,
gemm_bi_nn_tc64_typed: load_half("gemm_bi_nn_tc64")?,
gemm_bi_nn_tc16_typed: load_half("gemm_bi_nn_tc16")?,
gemm_bi_nn_tcw64_typed: load_half_dynsmem("gemm_bi_nn_tcw64", 65_536)?,
gemm_bi_nn_tcwn64_typed: load_half_dynsmem("gemm_bi_nn_tcwn64", 98_304)?,
gemm_bi_nn_sm90_typed: if arch == "sm_90a" {
Some(load_half_dynsmem("gemm_bi_nn_sm90a_wgmma_wg1", 49_152)?)
} else {
None
},
gemm_bi_nn_sm100_typed: if matches!(arch, "sm_100a" | "sm_103a" | "sm_110a") {
Some(load_half_dynsmem("gemm_bi_nn_sm100_tcgen_c4", 65_536)?)
} else {
None
},
_modules: CudaModuleAnchors::new(vec![module]),
})
}
pub fn compiler_identity(&self) -> super::kernel_identity::CompilerIdentity {
self.compiler_identity
}
pub(crate) fn specialized_compiler_identity(
&self,
) -> Option<super::kernel_identity::CompilerIdentity> {
self.triad
.sm120_compiler_identity()
.or_else(|| self.triad.sm100_compiler_identity())
.or_else(|| self.triad.sm90a_compiler_identity())
}
pub fn artifact_set_identity(&self) -> super::kernel_identity::ArtifactSetIdentity {
self.triad.artifact_set_identity()
}
pub fn f32_triad_availability(&self) -> super::gemm_bi_triad::F32TriadAvailability {
self.triad.f32_triad_availability()
}
pub fn specialized_tf32_rejection(&self) -> Option<&str> {
self.triad.specialized_tf32_rejection()
}
pub fn finalist_tf32_rejection(&self) -> Option<&str> {
self.triad.finalist_tf32_rejection()
}
pub fn triad_scalar_compiler_identity(&self) -> super::kernel_identity::CompilerIdentity {
self.triad.scalar_compiler_identity()
}
pub(crate) fn triad_scalar_compute_capability(&self) -> (u32, u32) {
self.triad.compute_capability()
}
pub(crate) fn tc64_streamk_resident_ctas(&self) -> u32 {
self.triad.tc64_streamk_resident_ctas()
}
pub(crate) fn triad_sm80_compiler_identity(&self) -> super::kernel_identity::CompilerIdentity {
self.triad.sm80_compiler_identity()
}
pub(crate) fn triad_sm89_finalist_compiler_identity(
&self,
) -> Option<super::kernel_identity::CompilerIdentity> {
self.triad.sm89_finalist_compiler_identity()
}
pub fn triad_sm89_half_compiler_identity(
&self,
) -> Option<super::kernel_identity::CompilerIdentity> {
self.triad.sm89_half_compiler_identity()
}
pub fn triad_sm89_half_artifact_identity(
&self,
) -> Option<super::kernel_identity::ArtifactIdentity> {
self.triad.artifact_set_identity().sm89_half
}
pub fn triad_sm89_half_rejection(&self) -> Option<&str> {
self.triad.sm89_half_rejection()
}
pub fn triad_sm89_half_exclusions(&self) -> Vec<(&'static str, &str)> {
self.triad
.sm89_half_exclusions()
.iter()
.map(|excluded| (excluded.symbol, excluded.reason.as_str()))
.collect()
}
#[doc(hidden)]
pub fn triad_sm89_half_function(
&self,
route: super::gemm_bi_triad::Sm89HalfRoute,
dtype: super::dtype::WeightDtype,
) -> Option<&CudaFunction> {
self.triad.sm89_half_function(route, dtype)
}
pub fn triad_sm89_exact_f32_compiler_identity(
&self,
) -> Option<super::kernel_identity::CompilerIdentity> {
self.triad.sm89_exact_f32_compiler_identity()
}
pub fn triad_sm89_exact_f32_artifact_identity(
&self,
) -> Option<super::kernel_identity::ArtifactIdentity> {
self.triad.artifact_set_identity().sm89_exact_f32
}
pub fn triad_sm89_exact_f32_rejection(&self) -> Option<&str> {
self.triad.sm89_exact_f32_rejection()
}
pub fn triad_sm89_exact_f32_exclusions(&self) -> Vec<(&'static str, &str)> {
self.triad
.sm89_exact_f32_exclusions()
.iter()
.map(|excluded| (excluded.symbol, excluded.reason.as_str()))
.collect()
}
#[doc(hidden)]
pub fn triad_sm89_exact_f32_function(&self, symbol: &str) -> Option<&CudaFunction> {
self.triad.sm89_exact_f32_function(symbol)
}
pub fn triad_sm89_exact_f32_d128_compiler_identity(
&self,
) -> Option<super::kernel_identity::CompilerIdentity> {
self.triad.sm89_exact_f32_d128_compiler_identity()
}
pub fn triad_sm89_exact_f32_d128_artifact_identity(
&self,
) -> Option<super::kernel_identity::ArtifactIdentity> {
self.triad.artifact_set_identity().sm89_exact_f32_d128
}
pub fn triad_sm89_exact_f32_d128_rejection(&self) -> Option<&str> {
self.triad.sm89_exact_f32_d128_rejection()
}
pub fn triad_sm89_exact_f32_d128_exclusions(&self) -> Vec<(&'static str, &str)> {
self.triad
.sm89_exact_f32_d128_exclusions()
.iter()
.map(|excluded| (excluded.symbol, excluded.reason.as_str()))
.collect()
}
#[doc(hidden)]
pub fn triad_sm89_exact_f32_d128_function(&self, symbol: &str) -> Option<&CudaFunction> {
self.triad.sm89_exact_f32_d128_function(symbol)
}
pub fn triad_sm89_tf32_joint_compiler_identity(
&self,
) -> Option<super::kernel_identity::CompilerIdentity> {
self.triad.sm89_tf32_joint_compiler_identity()
}
pub fn triad_sm89_tf32_joint_artifact_identity(
&self,
) -> Option<super::kernel_identity::ArtifactIdentity> {
self.triad.artifact_set_identity().sm89_tf32_joint
}
pub fn triad_sm89_tf32_joint_rejection(&self) -> Option<&str> {
self.triad.sm89_tf32_joint_rejection()
}
pub fn triad_sm89_tf32_joint_exclusions(&self) -> Vec<(&'static str, &str)> {
self.triad
.sm89_tf32_joint_exclusions()
.iter()
.map(|excluded| (excluded.symbol, excluded.reason.as_str()))
.collect()
}
#[doc(hidden)]
pub fn triad_sm89_tf32_joint_function(&self, symbol: &str) -> Option<&CudaFunction> {
self.triad.sm89_tf32_joint_function(symbol)
}
pub(crate) fn tf32_function(&self, symbol: &str) -> Option<&CudaFunction> {
self.triad.tf32_function(symbol)
}
pub(crate) fn triad_kernels(&self) -> &GemmBiKernels {
&self.triad
}
pub fn splitk_scratch_buf(
&self,
stream: &Arc<cudarc::driver::CudaStream>,
) -> Result<&cudarc::driver::CudaSlice<f32>, String> {
self.triad.splitk_scratch_buf(stream)
}
pub fn transpose_scratch_buf(
&self,
stream: &Arc<cudarc::driver::CudaStream>,
) -> Result<&cudarc::driver::CudaSlice<f32>, String> {
self.triad.transpose_scratch_buf(stream)
}
}
pub fn cuda_include_paths() -> Vec<String> {
let mut candidates: Vec<String> = Vec::new();
for var in ["CUDA_HOME", "CUDA_PATH", "CUDA_ROOT"] {
if let Ok(p) = std::env::var(var) {
candidates.push(format!("{p}/include"));
}
}
for std_path in [
"/usr/local/cuda/include",
"/usr/local/cuda-13.2/include",
"/usr/local/cuda-12.8/include",
"/usr/local/cuda-12.6/include",
"/usr/local/cuda-12.4/include",
"/usr/local/cuda-12.2/include",
"/opt/cuda/include",
] {
candidates.push(std_path.to_string());
}
candidates
.into_iter()
.filter(|p| std::path::Path::new(p).join("cuda_fp16.h").exists())
.collect()
}