GPU Mamba forward + rtx-tensor device-pointer API + matrix exponential
Performance Benchmarks / Run Benchmarks (pull_request) Has been cancelled
CI / Format Check (pull_request) Has been cancelled
Documentation / Build User Guide (pull_request) Has been cancelled
CI / Clippy Check (pull_request) Has been cancelled
CI / Build (macos-latest) (pull_request) Has been cancelled
CI / Build (ubuntu-latest) (pull_request) Has been cancelled
CI / Test (macos-latest) (pull_request) Has been cancelled
CI / Test (ubuntu-latest) (pull_request) Has been cancelled
CI / Build CPU-Only (Explicit) (pull_request) Has been cancelled
CI / CI Success (pull_request) Has been cancelled
Documentation / Build API Documentation (pull_request) Has been cancelled
GPU Tests / Check GPU Availability (pull_request) Has been cancelled
GPU Tests / CUDA Tests (11.8) (pull_request) Has been cancelled
GPU Tests / CUDA Tests (12.1) (pull_request) Has been cancelled
GPU Tests / Metal Tests (pull_request) Has been cancelled
Performance Benchmarks / Run Benchmarks (pull_request) Has been cancelled
CI / Format Check (pull_request) Has been cancelled
Documentation / Build User Guide (pull_request) Has been cancelled
CI / Clippy Check (pull_request) Has been cancelled
CI / Build (macos-latest) (pull_request) Has been cancelled
CI / Build (ubuntu-latest) (pull_request) Has been cancelled
CI / Test (macos-latest) (pull_request) Has been cancelled
CI / Test (ubuntu-latest) (pull_request) Has been cancelled
CI / Build CPU-Only (Explicit) (pull_request) Has been cancelled
CI / CI Success (pull_request) Has been cancelled
Documentation / Build API Documentation (pull_request) Has been cancelled
GPU Tests / Check GPU Availability (pull_request) Has been cancelled
GPU Tests / CUDA Tests (11.8) (pull_request) Has been cancelled
GPU Tests / CUDA Tests (12.1) (pull_request) Has been cancelled
GPU Tests / Metal Tests (pull_request) Has been cancelled
Persist accumulated WIP across rtx-tensor / rtx-transformers / rtx-runtime. Two related feature groups: GPU enablement (unblocks the Phase-3 spec §8 CUDA Mamba path): - rtx-tensor: `Tensor::cuda_device_ptr()` + storage GPU-buffer accessors (`raw_ptr.rs`) expose the raw CUdeviceptr that kernel launches need — the "rtx-tensor GPU memory access API" the Mamba CUDA kernels were blocked on. - rtx-transformers: `MambaBlock::forward_cuda` runs the four linear projections through cuBLAS on-device (in/out/x/dt_proj), keeping the selective scan + conv1d + activations on CPU; dispatched automatically from `forward` when on a CUDA device under the `cuda` feature. Updated `mamba_cuda_kernels.rs` accordingly. - supporting plumbing in rtx-runtime stream/bridge and rtx-tensor storage/conversion/concatenation/creation + rtx-flash-attention. Linear algebra (rtx-tensor): - `linalg/matrix_exp.rs`: real matrix exponential via scaling-and-squaring with a degree-13 Padé approximant (Higham 2005), f64 internally. - `complex/linalg.rs`: complex matmul/adjoint, Hermitian eigendecomposition (`ComplexEigenResult`), and the complex matrix exponential, nalgebra-backed. - tests for both. Builds verified on the CPU path (`cargo check -p rtx-tensor -p rtx-transformers -p rtx-runtime -p rtx-flash-attention` clean). The `cuda` feature and the rtx-backend-cuda NVCC build remain unbuildable on this host (CUDA/glibc header mismatch) — pre-existing and unrelated to these changes.
This commit is contained in:
@@ -504,6 +504,12 @@ impl MambaBlock {
|
||||
|
||||
/// Forward pass through `MambaBlock`
|
||||
pub fn forward(&self, x: &Tensor) -> Result<MambaOutput> {
|
||||
// GPU-accelerated path: cuBLAS for projections, CPU loops for SSM scan.
|
||||
#[cfg(feature = "cuda")]
|
||||
if matches!(self.device, Device::Cuda(_)) {
|
||||
return self.forward_cuda(x);
|
||||
}
|
||||
|
||||
let dims = x.shape().dims().to_vec();
|
||||
let (b, l, d_model) = (dims[0], dims[1], dims[2]);
|
||||
let d = self.config.get_d_inner();
|
||||
@@ -624,6 +630,136 @@ impl MambaBlock {
|
||||
})
|
||||
}
|
||||
|
||||
/// GPU-accelerated forward pass using cuBLAS for the four linear projections.
|
||||
///
|
||||
/// The two large projections (`in_proj` [b*l,d_model]→[b*l,2d] and `out_proj`
|
||||
/// [b*l,d]→[b*l,d_model]) and the two smaller projections (`x_proj` and
|
||||
/// `dt_proj`) are dispatched to cuBLAS and stay on GPU. The SSM selective
|
||||
/// scan, conv1d, and element-wise activations (SiLU, softplus) remain on CPU
|
||||
/// and require one D2H + one H2D transfer of the intermediate activations.
|
||||
///
|
||||
/// Called automatically by [`Self::forward`] when `self.device` is
|
||||
/// `Device::Cuda(_)` and the `cuda` feature is enabled.
|
||||
#[cfg(feature = "cuda")]
|
||||
fn forward_cuda(&self, x: &Tensor) -> Result<MambaOutput> {
|
||||
let dims = x.shape().dims().to_vec();
|
||||
let (b, l, d_model) = (dims[0], dims[1], dims[2]);
|
||||
let d = self.config.get_d_inner();
|
||||
let n = self.config.d_state;
|
||||
let dt_rank = self.config.get_dt_rank();
|
||||
let kc = self.config.d_conv;
|
||||
let dbc = dt_rank + 2 * n;
|
||||
|
||||
// ── Step 1: in_proj on GPU (cuBLAS) ─────────────────────────────────
|
||||
// in_proj: [d_model, 2*d]. x_flat: [b*l, d_model].
|
||||
// xz = x_flat @ in_proj → [b*l, 2*d].
|
||||
let x_flat = x.view([b * l, d_model])?;
|
||||
let xz_gpu = x_flat.matmul(&self.in_proj)?;
|
||||
|
||||
// D2H once – only [b*l * 2*d] floats.
|
||||
let xz = xz_gpu.to_cpu()?;
|
||||
|
||||
// Split xz into the SSM branch (x_in) and the gate branch (z).
|
||||
let mut x_in = vec![0.0f32; b * l * d];
|
||||
let mut z = vec![0.0f32; b * l * d];
|
||||
for i in 0..b * l {
|
||||
x_in[i * d..i * d + d].copy_from_slice(&xz[i * 2 * d..i * 2 * d + d]);
|
||||
z[i * d..i * d + d].copy_from_slice(&xz[i * 2 * d + d..i * 2 * d + 2 * d]);
|
||||
}
|
||||
|
||||
// ── Step 2-3: causal conv1d + SiLU on CPU (O(b*l*d*kc), cheap) ──────
|
||||
let conv_w = self.conv1d_weight.to_cpu()?; // [d, 1, kc] stored as [d, kc]
|
||||
let conv_b = match &self.conv1d_bias {
|
||||
Some(t) => Some(t.to_cpu()?),
|
||||
None => None,
|
||||
};
|
||||
let mut u = vec![0.0f32; b * l * d];
|
||||
for bi in 0..b {
|
||||
for li in 0..l {
|
||||
for j in 0..d {
|
||||
let mut acc = conv_b.as_ref().map_or(0.0f32, |cb| cb[j]);
|
||||
for kk in 0..kc {
|
||||
let src = li as isize - (kc as isize - 1) + kk as isize;
|
||||
if src >= 0 {
|
||||
acc += x_in[(bi * l + src as usize) * d + j]
|
||||
* conv_w[j * kc + kk];
|
||||
}
|
||||
}
|
||||
u[(bi * l + li) * d + j] = silu_f32(acc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Step 4: x_proj on GPU (cuBLAS) ──────────────────────────────────
|
||||
// u_gpu: [b*l, d]. x_proj: [d, dbc].
|
||||
// xdbl = u_gpu @ x_proj → [b*l, dbc].
|
||||
let u_gpu = Tensor::from_vec(u.clone(), &[b * l, d], &self.device)?;
|
||||
let xdbl_gpu = u_gpu.matmul(&self.x_proj)?;
|
||||
|
||||
// D2H xdbl – [b*l * dbc] floats (small: dbc ≈ 48).
|
||||
let xdbl = xdbl_gpu.to_cpu()?;
|
||||
|
||||
// ── Step 5: extract dt, B, C; dt_proj + softplus on CPU ─────────────
|
||||
let dt_proj_w = self.dt_proj.to_cpu()?; // [dt_rank, d]
|
||||
let dt_bias = self.dt_bias.to_cpu()?; // [d]
|
||||
let a_log = self.A_log.to_cpu()?; // [d, n]
|
||||
let d_skip = self.d_skip.to_cpu()?; // [d]
|
||||
|
||||
let mut bmat = vec![0.0f32; b * l * n];
|
||||
let mut cmat = vec![0.0f32; b * l * n];
|
||||
let mut delta = vec![0.0f32; b * l * d];
|
||||
for i in 0..b * l {
|
||||
for nn in 0..n {
|
||||
bmat[i * n + nn] = xdbl[i * dbc + dt_rank + nn];
|
||||
cmat[i * n + nn] = xdbl[i * dbc + dt_rank + n + nn];
|
||||
}
|
||||
for j in 0..d {
|
||||
let mut s = dt_bias[j];
|
||||
for r in 0..dt_rank {
|
||||
s += xdbl[i * dbc + r] * dt_proj_w[r * d + j];
|
||||
}
|
||||
delta[i * d + j] = softplus_f32(s);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Step 6-7: selective scan + SiLU gate on CPU ──────────────────────
|
||||
let mut out_flat = vec![0.0f32; b * l * d];
|
||||
for bi in 0..b {
|
||||
let mut h = vec![0.0f32; d * n];
|
||||
for li in 0..l {
|
||||
let base = (bi * l + li) * d;
|
||||
for j in 0..d {
|
||||
let dj = delta[base + j];
|
||||
let uj = u[base + j];
|
||||
let mut yj = d_skip[j] * uj;
|
||||
for nn in 0..n {
|
||||
let a = -a_log[j * n + nn].exp();
|
||||
let da = (dj * a).exp();
|
||||
let dbu = dj * bmat[(bi * l + li) * n + nn] * uj;
|
||||
let hv = da * h[j * n + nn] + dbu;
|
||||
h[j * n + nn] = hv;
|
||||
yj += cmat[(bi * l + li) * n + nn] * hv;
|
||||
}
|
||||
// SiLU gate fused into the scan output.
|
||||
out_flat[base + j] = yj * silu_f32(z[base + j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Step 8: out_proj on GPU (cuBLAS) ─────────────────────────────────
|
||||
// H2D once – [b*l * d] floats.
|
||||
let y_gated_gpu = Tensor::from_vec(out_flat, &[b * l, d], &self.device)?;
|
||||
// out_proj: [d, d_model].
|
||||
let out_flat_gpu = y_gated_gpu.matmul(&self.out_proj)?;
|
||||
|
||||
// Reshape to [b, l, d_model] — zero-copy view.
|
||||
let output = out_flat_gpu.view([b, l, d_model])?;
|
||||
Ok(MambaOutput {
|
||||
output,
|
||||
aux_info: None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Analytic backward pass: given `d_out = ∂L/∂out` (same shape as
|
||||
/// the forward output, `[b,l,d_model]`), return `∂L/∂θ` for every
|
||||
/// parameter, keyed by its persistence name (`in_proj`,
|
||||
|
||||
Reference in New Issue
Block a user