entrenar/autograd/cuda_backward/
gemm.rs1#![allow(unsafe_code)]
2#![allow(trivial_casts)]
3#![allow(clippy::borrow_as_ptr)]
4#![allow(clippy::ref_as_ptr)]
5
6#[cfg(feature = "cuda")]
7use trueno_gpu::driver::{CudaStream, GpuBuffer, LaunchConfig};
8#[cfg(feature = "cuda")]
9use trueno_gpu::kernels::backward::{GemmBackwardAKernel, GemmBackwardBKernel};
10#[cfg(feature = "cuda")]
11use trueno_gpu::kernels::Kernel;
12
13use super::super::cuda_tensor::{CudaTensorError, Result};
14#[cfg(feature = "cuda")]
15use super::cache::KERNEL_CACHE;
16
17#[cfg(feature = "cuda")]
19use crate::autograd::cuda_forward::{
20 bind_cublas_stream, cublas_gemm_backward_a, cublas_gemm_backward_b,
21};
22
23const BACKWARD_TILE_SIZE: u32 = 16;
28
29#[cfg(feature = "cuda")]
35pub fn gemm_backward_a(
36 grad_output: &GpuBuffer<f32>,
37 b: &GpuBuffer<f32>,
38 grad_a: &mut GpuBuffer<f32>,
39 m: u32,
40 k: u32,
41 n: u32,
42 stream: &CudaStream,
43) -> Result<()> {
44 let cache = KERNEL_CACHE.get().ok_or(CudaTensorError::DeviceNotInitialized)?;
45 let mut cache = cache.lock().map_err(|_err| {
46 CudaTensorError::KernelError("Failed to acquire kernel cache lock".to_string())
47 })?;
48
49 if let Some(cublas) = cache.cublas() {
55 bind_cublas_stream(cublas, stream)?;
56 return cublas_gemm_backward_a(cublas, grad_output, b, grad_a, m, k, n);
57 }
58
59 let tile = BACKWARD_TILE_SIZE;
60 let kernel = GemmBackwardAKernel::tiled_unrolled(m, n, k, tile);
62 let kernel_name = kernel.name();
63
64 let key = format!("gemm_backward_a_{m}_{k}_{n}");
65 let module = match cache.get_cached(&key) {
66 Some(m) => m,
67 None => {
68 let ptx = kernel.emit_ptx_for_target(cache.sm_target());
69 cache.get_or_compile(&key, &ptx)?
70 }
71 };
72
73 let smem = 2 * tile * tile * 4; let config = LaunchConfig {
76 grid: (k.div_ceil(tile), m.div_ceil(tile), 1),
77 block: (tile, tile, 1),
78 shared_mem: smem,
79 };
80
81 let grad_out_ptr = grad_output.as_ptr();
82 let b_ptr = b.as_ptr();
83 let grad_a_ptr = grad_a.as_ptr();
84
85 let mut args: [*mut std::ffi::c_void; 6] = [
88 &grad_out_ptr as *const _ as *mut _,
89 &b_ptr as *const _ as *mut _,
90 &grad_a_ptr as *const _ as *mut _,
91 &m as *const _ as *mut _,
92 &n as *const _ as *mut _,
93 &k as *const _ as *mut _,
94 ];
95
96 unsafe {
99 stream.launch_kernel(module, kernel_name, &config, &mut args).map_err(|e| {
100 CudaTensorError::KernelError(format!("GEMM backward A launch failed: {e:?}"))
101 })?;
102 }
103
104 Ok(())
105}
106
107#[cfg(feature = "cuda")]
113pub fn gemm_backward_b(
114 a: &GpuBuffer<f32>,
115 grad_output: &GpuBuffer<f32>,
116 grad_b: &mut GpuBuffer<f32>,
117 m: u32,
118 k: u32,
119 n: u32,
120 stream: &CudaStream,
121) -> Result<()> {
122 let cache = KERNEL_CACHE.get().ok_or(CudaTensorError::DeviceNotInitialized)?;
123 let mut cache = cache.lock().map_err(|_err| {
124 CudaTensorError::KernelError("Failed to acquire kernel cache lock".to_string())
125 })?;
126
127 if let Some(cublas) = cache.cublas() {
133 bind_cublas_stream(cublas, stream)?;
134 return cublas_gemm_backward_b(cublas, a, grad_output, grad_b, m, k, n);
135 }
136
137 let tile = BACKWARD_TILE_SIZE;
138 let kernel = GemmBackwardBKernel::tiled_unrolled(m, n, k, tile);
140 let kernel_name = kernel.name();
141
142 let key = format!("gemm_backward_b_{m}_{k}_{n}");
143 let module = match cache.get_cached(&key) {
144 Some(m) => m,
145 None => {
146 let ptx = kernel.emit_ptx_for_target(cache.sm_target());
147 cache.get_or_compile(&key, &ptx)?
148 }
149 };
150
151 let smem = 2 * tile * tile * 4;
153 let config = LaunchConfig {
154 grid: (n.div_ceil(tile), k.div_ceil(tile), 1),
155 block: (tile, tile, 1),
156 shared_mem: smem,
157 };
158
159 let a_ptr = a.as_ptr();
160 let grad_out_ptr = grad_output.as_ptr();
161 let grad_b_ptr = grad_b.as_ptr();
162
163 let mut args: [*mut std::ffi::c_void; 6] = [
166 &a_ptr as *const _ as *mut _,
167 &grad_out_ptr as *const _ as *mut _,
168 &grad_b_ptr as *const _ as *mut _,
169 &m as *const _ as *mut _,
170 &n as *const _ as *mut _,
171 &k as *const _ as *mut _,
172 ];
173
174 unsafe {
177 stream.launch_kernel(module, kernel_name, &config, &mut args).map_err(|e| {
178 CudaTensorError::KernelError(format!("GEMM backward B launch failed: {e:?}"))
179 })?;
180 }
181
182 Ok(())
183}
184
185#[cfg(feature = "cuda")]
190pub fn gemm_backward_a_accumulate(
191 grad_output: &GpuBuffer<f32>,
192 b: &GpuBuffer<f32>,
193 grad_a: &mut GpuBuffer<f32>,
194 m: u32,
195 k: u32,
196 n: u32,
197 stream: &CudaStream,
198) -> Result<()> {
199 let cache = KERNEL_CACHE.get().ok_or(CudaTensorError::DeviceNotInitialized)?;
200 let cache = cache.lock().map_err(|_err| {
201 CudaTensorError::KernelError("Failed to acquire kernel cache lock".to_string())
202 })?;
203
204 if let Some(cublas) = cache.cublas() {
207 bind_cublas_stream(cublas, stream)?;
208 return crate::autograd::cuda_forward::cublas_gemm_backward_a_accumulate(
209 cublas,
210 grad_output,
211 b,
212 grad_a,
213 m,
214 k,
215 n,
216 );
217 }
218
219 Err(CudaTensorError::KernelError(
221 "gemm_backward_a_accumulate requires cuBLAS (NF4 training always has it)".to_string(),
222 ))
223}
224
225#[cfg(feature = "cuda")]
230pub fn gemm_backward_a_fp16_dispatch_accumulate(
231 grad_output: &GpuBuffer<f32>,
232 w_fp16: Option<&GpuBuffer<u16>>,
233 w_fp32: &GpuBuffer<f32>,
234 grad_a: &mut GpuBuffer<f32>,
235 m: u32,
236 k: u32,
237 n: u32,
238 stream: &CudaStream,
239 _ctx: &trueno_gpu::driver::CudaContext,
240) -> Result<()> {
241 if w_fp16.is_some() {
244 let mut temp = GpuBuffer::<f32>::new(_ctx, (m * k) as usize)
246 .map_err(|e| CudaTensorError::AllocationFailed(format!("fp16 accum temp: {e:?}")))?;
247 gemm_backward_a_fp16_dispatch(
248 grad_output,
249 w_fp16,
250 w_fp32,
251 &mut temp,
252 m,
253 k,
254 n,
255 stream,
256 _ctx,
257 )?;
258 crate::transformer::cuda_block::cuda_add_inplace(grad_a, &temp, (m * k) as usize, stream)?;
259 Ok(())
260 } else {
261 gemm_backward_a_accumulate(grad_output, w_fp32, grad_a, m, k, n, stream)
262 }
263}
264
265#[cfg(feature = "cuda")]
271pub fn gemm_backward_a_fp16_dispatch(
272 grad_output: &GpuBuffer<f32>,
273 w_fp16: Option<&GpuBuffer<u16>>,
274 w_fp32: &GpuBuffer<f32>,
275 grad_a: &mut GpuBuffer<f32>,
276 m: u32,
277 k: u32,
278 n: u32,
279 stream: &CudaStream,
280 ctx: &trueno_gpu::driver::CudaContext,
281) -> Result<()> {
282 if let Some(w16) = w_fp16 {
283 let elems = (m * n) as usize;
284 let mut grad_f16 = GpuBuffer::<u16>::new(ctx, elems)
285 .map_err(|e| CudaTensorError::AllocationFailed(format!("grad f16 cast: {e:?}")))?;
286 crate::autograd::cuda_forward::cast_f32_to_f16_gpu(
287 grad_output,
288 &mut grad_f16,
289 m * n,
290 stream,
291 )?;
292 crate::autograd::cuda_forward::gemm_f16_to_f32_backward_a(
293 &grad_f16, w16, grad_a, m, k, n, stream,
294 )
295 } else {
296 gemm_backward_a(grad_output, w_fp32, grad_a, m, k, n, stream)
297 }
298}