metrale_model_layers/layers/ops/
w4a16_tc_rows.rs1use anyhow::Result;
12use metrale_gpu_runtime::gpu::{DevicePtr, GpuBackend};
13use metrale_gpu_runtime::kernel_args::KernelLaunch;
14
15use crate::weight_map::QuantizedWeight;
16
17pub const W4A16_TC_ROWS_COLS: u32 = 64;
19
20pub const W4A16_TC_ROWS_MAX_M: u32 = 64;
22
23pub const W4A16_TC_ROWS_MODULE: &str = "w4a16_tc_rows";
25
26pub fn w4a16_tc_rows_shape_ok(m: u32, n: u32, k: u32, lda: u32, ldc: u32) -> bool {
31 (1..=W4A16_TC_ROWS_MAX_M).contains(&m)
32 && n > 0
33 && k > 0
34 && k.is_multiple_of(256)
35 && lda >= k
36 && lda.is_multiple_of(8)
37 && ldc >= n
38}
39
40#[allow(clippy::too_many_arguments)]
44pub fn w4a16_tc_rows(
45 gpu: &dyn GpuBackend,
46 input: DevicePtr,
47 weight: &QuantizedWeight,
48 output: DevicePtr,
49 m: u32,
50 n: u32,
51 k: u32,
52 lda: u32,
53 ldc: u32,
54 stream: u64,
55) -> Result<()> {
56 anyhow::ensure!(
57 w4a16_tc_rows_shape_ok(m, n, k, lda, ldc),
58 "w4a16_tc_rows: m={m} n={n} k={k} lda={lda} ldc={ldc} outside the kernel's contract"
59 );
60 let entry = if m <= 16 {
61 "w4a16_tc_rows_16"
62 } else if m <= 32 {
63 "w4a16_tc_rows_32"
64 } else {
65 "w4a16_tc_rows_64"
66 };
67 let kernel = gpu.op_cache().kernel(gpu, W4A16_TC_ROWS_MODULE, entry)?;
68 KernelLaunch::new(gpu, kernel)
69 .grid([n.div_ceil(W4A16_TC_ROWS_COLS), 1, 1])
70 .block([128, 1, 1])
71 .arg_ptr(input)
72 .arg_ptr(weight.weight)
73 .arg_ptr(weight.weight_scale)
74 .arg_f32(weight.weight_scale_2)
75 .arg_ptr(output)
76 .arg_u32(m)
77 .arg_u32(n)
78 .arg_u32(k)
79 .arg_u32(lda)
80 .arg_u32(ldc)
81 .launch(stream)
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 const CU: &str = include_str!("../../../../../kernels/gb10/common/w4a16_tc_rows.cu");
89 const ROWS: &str = include_str!("../../../../../kernels/gb10/common/tc_rows.cuh");
90
91 #[test]
94 fn launch_matches_the_kernel() {
95 assert!(
96 ROWS.contains("#define TR_WARPS 4\n")
97 && ROWS.contains("#define TR_COLS (TR_WARPS * 16)\n")
98 );
99 assert_eq!(W4A16_TC_ROWS_COLS, 4 * 16);
100 assert!(CU.contains(
101 "tr_block<Nvfp4G16, 8, 1, true>(A, {packed, scale, s2}, C, M, N, K, lda, ldc, blockIdx.x);"
102 ));
103 assert_eq!(W4A16_TC_ROWS_MAX_M, 8 * 8);
104 for entry in [
105 "w4a16_tc_rows_16(",
106 "w4a16_tc_rows_32(",
107 "w4a16_tc_rows_64(",
108 ] {
109 assert!(CU.contains(entry), "{entry} missing from the kernel");
110 }
111 }
112
113 #[test]
115 fn shape_contract() {
116 assert!(w4a16_tc_rows_shape_ok(1, 248320, 2048, 2048, 248320));
117 assert!(w4a16_tc_rows_shape_ok(64, 248320, 2048, 2048, 248320));
118 assert!(!w4a16_tc_rows_shape_ok(0, 248320, 2048, 2048, 248320));
119 assert!(!w4a16_tc_rows_shape_ok(65, 248320, 2048, 2048, 248320));
120 assert!(w4a16_tc_rows_shape_ok(8, 248070, 2048, 2048, 248070));
122 assert!(!w4a16_tc_rows_shape_ok(8, 248320, 2048 + 128, 2176, 248320));
123 assert!(!w4a16_tc_rows_shape_ok(8, 248320, 2048, 2044, 248320));
124 assert!(!w4a16_tc_rows_shape_ok(8, 248320, 2048, 2048, 248319));
125 }
126}