use std::sync::Arc;
use cudarc::driver::PushKernelArg;
use crate::config::MambaConfig;
use crate::mamba_ssm::gpu::context::GpuCtx;
use crate::mamba_ssm::gpu::dtype::WeightDtype;
use crate::mamba_ssm::gpu::launch::grid_1d;
use crate::mamba_ssm::gpu::weights::{GpuMambaMixedWeights, GpuMambaTrainWeights};
use crate::weights::MambaWeights;
pub struct GpuMambaTrainMixedWeights {
pub master: GpuMambaTrainWeights,
pub compute: GpuMambaMixedWeights,
pub dtype: WeightDtype,
}
impl GpuMambaTrainMixedWeights {
pub fn from_cpu(
stream: &Arc<cudarc::driver::CudaStream>,
cpu: &MambaWeights,
cfg: &MambaConfig,
dtype: WeightDtype,
) -> Result<Self, String> {
let master = GpuMambaTrainWeights::from_cpu(stream, cpu)?;
let compute = GpuMambaMixedWeights::from_cpu(stream, cpu, cfg, dtype)?;
Ok(Self {
master,
compute,
dtype,
})
}
pub fn sync_master_to_compute(&self, ctx: &GpuCtx) -> Result<(), String> {
sync_one(
ctx,
&self.master.input_proj_w,
&self.compute.input_proj_w,
self.dtype,
)?;
sync_f32(ctx, &self.master.input_proj_b, &self.compute.input_proj_b)?;
for (mw, cw) in self.master.layers.iter().zip(&self.compute.layers) {
sync_f32(ctx, &mw.norm_weight, &cw.norm_weight)?;
sync_f32(ctx, &mw.conv1d_weight, &cw.conv1d_weight)?;
sync_f32(ctx, &mw.conv1d_bias, &cw.conv1d_bias)?;
sync_f32(ctx, &mw.dt_proj_b, &cw.dt_proj_b)?;
sync_f32(ctx, &mw.a_log, &cw.a_log)?;
sync_f32(ctx, &mw.d_param, &cw.d_param)?;
sync_one(ctx, &mw.in_proj_w, &cw.in_proj_w, self.dtype)?;
sync_one(ctx, &mw.x_proj_w, &cw.x_proj_w, self.dtype)?;
sync_one(ctx, &mw.dt_proj_w, &cw.dt_proj_w, self.dtype)?;
sync_one(ctx, &mw.out_proj_w, &cw.out_proj_w, self.dtype)?;
}
sync_f32(ctx, &self.master.norm_f_weight, &self.compute.norm_f_weight)?;
Ok(())
}
}
fn sync_one(
ctx: &GpuCtx,
master: &crate::mamba_ssm::gpu::buffers::GpuBuffer,
compute: &crate::mamba_ssm::gpu::buffers::WeightSliceDyn,
dtype: WeightDtype,
) -> Result<(), String> {
let n_elems = master.len();
debug_assert_eq!(n_elems, compute.len_elems());
if n_elems == 0 {
return Ok(());
}
if matches!(dtype, WeightDtype::F32) {
let bytes = n_elems * 4;
let dst_ptr = compute.ptr();
let src_ptr = master.cached_ptr();
let res = unsafe {
cudarc::driver::sys::cuMemcpyDtoDAsync_v2(
dst_ptr,
src_ptr,
bytes,
ctx.stream.cu_stream(),
)
};
if res != cudarc::driver::sys::CUresult::CUDA_SUCCESS {
return Err(format!("sync_one f32 D2D failed: {res:?}"));
}
return Ok(());
}
let kernel = match dtype {
WeightDtype::Bf16 => &ctx.kernels.cast_f32_to_bf16,
WeightDtype::F16 => &ctx.kernels.cast_f32_to_f16,
WeightDtype::F32 => unreachable!(),
};
let n_i32 = n_elems as i32;
let dst_ptr = compute.ptr();
let src_ptr = master.cached_ptr();
let mut builder = ctx.stream.launch_builder(kernel);
builder.arg(&dst_ptr);
builder.arg(&src_ptr);
builder.arg(&n_i32);
unsafe { builder.launch(grid_1d(n_elems)) }
.map_err(|e| format!("sync_one cast_f32_to_{dtype:?}: {e:?}"))?;
Ok(())
}
fn sync_f32(
ctx: &GpuCtx,
master: &crate::mamba_ssm::gpu::buffers::GpuBuffer,
compute: &crate::mamba_ssm::gpu::buffers::WeightSliceDyn,
) -> Result<(), String> {
let n_elems = master.len();
debug_assert_eq!(n_elems, compute.len_elems());
if n_elems == 0 {
return Ok(());
}
let bytes = n_elems * 4;
let res = unsafe {
cudarc::driver::sys::cuMemcpyDtoDAsync_v2(
compute.ptr(),
master.cached_ptr(),
bytes,
ctx.stream.cu_stream(),
)
};
if res != cudarc::driver::sys::CUresult::CUDA_SUCCESS {
return Err(format!("sync_f32 D2D failed: {res:?}"));
}
Ok(())
}