metrale_model_layers/layers/ops/
w8a16_tc_rows.rs1use anyhow::Result;
20use metrale_gpu_runtime::gpu::{DevicePtr, GpuBackend};
21use metrale_gpu_runtime::kernel_args::KernelLaunch;
22
23pub const W8A16_TC_ROWS_COLS: u32 = 64;
25
26pub const W8A16_TC_ROWS_MAX_M: u32 = 64;
29
30const MODULE: &str = "w8a16_tc_rows";
31
32fn w8a16_tc_rows_enabled() -> bool {
35 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
36 *ON.get_or_init(|| std::env::var_os("METRALE_NO_W8A16_TC_ROWS").is_none())
37}
38
39pub fn w8a16_tc_rows_shape_ok(
43 m: u32,
44 n: u32,
45 k: u32,
46 a_row_stride: u32,
47 c_row_stride: u32,
48) -> bool {
49 m >= 1
50 && n > 0
51 && n.is_multiple_of(128)
52 && k > 0
53 && k.is_multiple_of(128)
54 && a_row_stride >= k
55 && a_row_stride.is_multiple_of(8)
56 && c_row_stride >= n
57}
58
59#[allow(clippy::too_many_arguments)]
64pub fn w8a16_tc_rows_launch(
65 gpu: &dyn GpuBackend,
66 input: DevicePtr,
67 weight: DevicePtr,
68 block_scale: DevicePtr,
69 output: DevicePtr,
70 m: u32,
71 n: u32,
72 k: u32,
73 a_row_stride: u32,
74 c_row_stride: u32,
75 stream: u64,
76) -> Result<bool> {
77 if !w8a16_tc_rows_enabled()
78 || crate::layers::row_tiers() != crate::layers::RowTiers::Canonical
79 || !w8a16_tc_rows_shape_ok(m, n, k, a_row_stride, c_row_stride)
80 || !gpu.has_module(MODULE)
81 {
82 return Ok(false);
83 }
84 w8a16_tc_rows(
85 gpu,
86 input,
87 weight,
88 block_scale,
89 output,
90 m,
91 n,
92 k,
93 a_row_stride,
94 c_row_stride,
95 stream,
96 )?;
97 Ok(true)
98}
99
100#[allow(clippy::too_many_arguments)]
104pub fn w8a16_tc_rows(
105 gpu: &dyn GpuBackend,
106 input: DevicePtr,
107 weight: DevicePtr,
108 block_scale: DevicePtr,
109 output: DevicePtr,
110 m: u32,
111 n: u32,
112 k: u32,
113 a_row_stride: u32,
114 c_row_stride: u32,
115 stream: u64,
116) -> Result<()> {
117 anyhow::ensure!(
118 w8a16_tc_rows_shape_ok(m, n, k, a_row_stride, c_row_stride),
119 "w8a16_tc_rows: m={m} n={n} k={k} lda={a_row_stride} ldc={c_row_stride} outside the kernel's contract"
120 );
121 let (entry, chunks) = if m <= 16 {
122 ("w8a16_tc_rows_16", 1)
123 } else if m <= 32 {
124 ("w8a16_tc_rows_32", 1)
125 } else if m <= W8A16_TC_ROWS_MAX_M {
126 ("w8a16_tc_rows_64", 1)
127 } else {
128 ("w8a16_tc_rows_64c", m.div_ceil(W8A16_TC_ROWS_MAX_M))
129 };
130 let kernel = gpu.op_cache().kernel(gpu, MODULE, entry)?;
131 KernelLaunch::new(gpu, kernel)
132 .grid([n / W8A16_TC_ROWS_COLS * chunks, 1, 1])
133 .block([128, 1, 1])
134 .arg_ptr(input)
135 .arg_ptr(weight)
136 .arg_ptr(block_scale)
137 .arg_ptr(output)
138 .arg_u32(m)
139 .arg_u32(n)
140 .arg_u32(k)
141 .arg_u32(a_row_stride)
142 .arg_u32(c_row_stride)
143 .launch(stream)
144}
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149
150 const CU: &str = include_str!("../../../../../kernels/gb10/common/w8a16_tc_rows.cu");
151 const ROWS: &str = include_str!("../../../../../kernels/gb10/common/tc_rows.cuh");
152
153 #[test]
157 fn launch_matches_the_kernel() {
158 assert!(
159 ROWS.contains("#define TR_WARPS 4\n")
160 && ROWS.contains("#define TR_COLS (TR_WARPS * 16)\n")
161 );
162 assert_eq!(W8A16_TC_ROWS_COLS, 4 * 16);
163 assert!(CU.contains(
164 "tr_block<Fp8Block128, 8, 2>(A, {B, block_scale}, C, M, N, K, lda, ldc, blockIdx.x);"
165 ));
166 assert!(CU.contains("const unsigned int chunks = (M + 63) / 64;"));
168 assert!(
169 CU.contains("tr_block<Fp8Block128, 8, 2>(A + (unsigned long long)chunk * 64 * lda")
170 );
171 assert_eq!(W8A16_TC_ROWS_MAX_M, 8 * 8);
172 for entry in [
173 "w8a16_tc_rows_16(",
174 "w8a16_tc_rows_32(",
175 "w8a16_tc_rows_64(",
176 "w8a16_tc_rows_64c(",
177 ] {
178 assert!(CU.contains(entry), "{entry} missing from the kernel");
179 }
180 }
181
182 #[test]
184 fn shape_contract() {
185 assert!(w8a16_tc_rows_shape_ok(1, 12288, 2048, 2048, 12288));
186 assert!(w8a16_tc_rows_shape_ok(64, 512, 2048, 2048, 512));
187 assert!(!w8a16_tc_rows_shape_ok(0, 512, 2048, 2048, 512));
188 assert!(w8a16_tc_rows_shape_ok(65, 512, 2048, 2048, 512));
189 assert!(w8a16_tc_rows_shape_ok(256, 12288, 2048, 2048, 12288));
190 assert!(!w8a16_tc_rows_shape_ok(8, 576, 2048, 2048, 576));
191 assert!(!w8a16_tc_rows_shape_ok(8, 512, 2000, 2048, 512));
192 assert!(!w8a16_tc_rows_shape_ok(8, 512, 2048, 2044, 512));
193 assert!(!w8a16_tc_rows_shape_ok(8, 512, 2048, 2052, 512));
194 assert!(!w8a16_tc_rows_shape_ok(8, 512, 2048, 2048, 511));
195 }
196}