1use crate::codegen_sva::fabric_target::Precision;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub struct GpuLimits {
16 pub smem_bytes: usize,
18 pub max_threads: usize,
20}
21
22impl GpuLimits {
23 pub fn ampere() -> Self {
26 GpuLimits {
27 smem_bytes: 48 * 1024,
28 max_threads: 1024,
29 }
30 }
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub struct CudaTile {
36 pub bm: usize,
38 pub bn: usize,
40 pub bk: usize,
43 pub tm: usize,
45 pub tn: usize,
47}
48
49impl CudaTile {
50 pub fn threads(&self) -> usize {
52 if self.tm == 0 || self.tn == 0 {
53 return usize::MAX;
54 }
55 (self.bm / self.tm) * (self.bn / self.tn)
56 }
57
58 pub fn smem_bytes(&self) -> usize {
61 2 * (self.bm * self.bk + self.bk * self.bn)
62 }
63
64 pub fn is_legal(&self, gpu: &GpuLimits) -> bool {
67 self.bm > 0
68 && self.bn > 0
69 && self.bk > 0
70 && self.tm > 0
71 && self.tn > 0
72 && self.bm % self.tm == 0
73 && self.bn % self.tn == 0
74 && self.threads() >= 32
75 && self.threads() <= gpu.max_threads
76 && self.smem_bytes() <= gpu.smem_bytes
77 }
78}
79
80pub fn cuda_tile_lattice(gpu: &GpuLimits) -> Vec<CudaTile> {
83 let mut out = Vec::new();
84 for &bm in &[32usize, 64, 128] {
85 for &bn in &[32usize, 64, 128] {
86 for &bk in &[8usize, 16, 32] {
87 for &tm in &[2usize, 4, 8] {
88 for &tn in &[2usize, 4, 8] {
89 let t = CudaTile { bm, bn, bk, tm, tn };
90 if t.is_legal(gpu) {
91 out.push(t);
92 }
93 }
94 }
95 }
96 }
97 }
98 out
99}
100
101pub fn emit_tiled_dp4a(tile: &CudaTile) -> String {
108 let defs = format!(
109 "#define BM {}\n#define BN {}\n#define BK {}\n#define TM {}\n#define TN {}\n",
110 tile.bm, tile.bn, tile.bk, tile.tm, tile.tn,
111 );
112 let body = r#"#define NTHREAD ((BM / TM) * (BN / TN))
114#define COL_THREADS (BN / TN)
115
116// C(M x N) = A(M x K) . B(K x N), int8 operands, int32 accumulate, __dp4a. Block/K must divide the problem
117// (host pads); ragged M/N are bounds-guarded on the write-back.
118extern "C" __global__ void gemm_tiled_dp4a(const signed char* A, const signed char* B, int* C,
119 int M, int N, int K) {
120 __shared__ signed char As[BM * BK];
121 __shared__ signed char Bs[BK * BN];
122 const int block_row = blockIdx.y * BM;
123 const int block_col = blockIdx.x * BN;
124 const int tid = threadIdx.x;
125 const int thread_row = (tid / COL_THREADS) * TM;
126 const int thread_col = (tid % COL_THREADS) * TN;
127
128 int acc[TM][TN];
129 #pragma unroll
130 for (int i = 0; i < TM; ++i)
131 #pragma unroll
132 for (int j = 0; j < TN; ++j) acc[i][j] = 0;
133
134 for (int k0 = 0; k0 < K; k0 += BK) {
135 for (int idx = tid; idx < BM * BK; idx += NTHREAD) {
136 int r = idx / BK, c = idx % BK;
137 As[idx] = A[(block_row + r) * K + (k0 + c)];
138 }
139 for (int idx = tid; idx < BK * BN; idx += NTHREAD) {
140 int r = idx / BN, c = idx % BN;
141 Bs[idx] = B[(k0 + r) * N + (block_col + c)];
142 }
143 __syncthreads();
144
145 #pragma unroll
146 for (int kk = 0; kk < BK; kk += 4) {
147 #pragma unroll
148 for (int i = 0; i < TM; ++i) {
149 signed char a0 = As[(thread_row + i) * BK + kk + 0];
150 signed char a1 = As[(thread_row + i) * BK + kk + 1];
151 signed char a2 = As[(thread_row + i) * BK + kk + 2];
152 signed char a3 = As[(thread_row + i) * BK + kk + 3];
153 int aa = (a0 & 0xff) | ((a1 & 0xff) << 8) | ((a2 & 0xff) << 16) | ((a3 & 0xff) << 24);
154 #pragma unroll
155 for (int j = 0; j < TN; ++j) {
156 signed char b0 = Bs[(kk + 0) * BN + thread_col + j];
157 signed char b1 = Bs[(kk + 1) * BN + thread_col + j];
158 signed char b2 = Bs[(kk + 2) * BN + thread_col + j];
159 signed char b3 = Bs[(kk + 3) * BN + thread_col + j];
160 int bb = (b0 & 0xff) | ((b1 & 0xff) << 8) | ((b2 & 0xff) << 16) | ((b3 & 0xff) << 24);
161 acc[i][j] = __dp4a(aa, bb, acc[i][j]);
162 }
163 }
164 }
165 __syncthreads();
166 }
167
168 #pragma unroll
169 for (int i = 0; i < TM; ++i)
170 #pragma unroll
171 for (int j = 0; j < TN; ++j) {
172 int gr = block_row + thread_row + i;
173 int gc = block_col + thread_col + j;
174 if (gr < M && gc < N) C[gr * N + gc] = acc[i][j];
175 }
176}
177"#;
178 format!(
179 "// Auto-generated by LOGOS codegen_cuda::gemm_tile — tiled DP4A GEMM (bm={} bn={} bk={} tm={} tn={}).\n\
180 #include <cstdint>\n{defs}{body}",
181 tile.bm, tile.bn, tile.bk, tile.tm, tile.tn,
182 )
183}
184
185pub fn emit_tiled_dp4a_program(tile: &CudaTile, m: usize, k: usize, n: usize) -> String {
190 let kernel = emit_tiled_dp4a(tile);
191 let dims = format!("#define MM {m}\n#define KK {k}\n#define NN {n}\n");
192 let host = r#"#include <cstdio>
193#include <cstdlib>
194int main() {
195 const int M = MM, K = KK, N = NN;
196 signed char* hA = (signed char*)malloc((size_t)M * K);
197 signed char* hB = (signed char*)malloc((size_t)K * N);
198 for (long i = 0; i < (long)M * K; i++) hA[i] = (signed char)(((i * 7 + 3) % 251) - 125);
199 for (long i = 0; i < (long)K * N; i++) hB[i] = (signed char)(((i * 5 + 1) % 251) - 125);
200 signed char *dA, *dB; int* dC;
201 cudaMalloc((void**)&dA, (size_t)M * K);
202 cudaMalloc((void**)&dB, (size_t)K * N);
203 cudaMalloc((void**)&dC, (size_t)M * N * sizeof(int));
204 cudaMemcpy(dA, hA, (size_t)M * K, cudaMemcpyHostToDevice);
205 cudaMemcpy(dB, hB, (size_t)K * N, cudaMemcpyHostToDevice);
206 dim3 grid(N / BN, M / BM);
207 dim3 block(NTHREAD);
208 gemm_tiled_dp4a<<<grid, block>>>(dA, dB, dC, M, N, K);
209 cudaDeviceSynchronize();
210 int* hC = (int*)malloc((size_t)M * N * sizeof(int));
211 cudaMemcpy(hC, dC, (size_t)M * N * sizeof(int), cudaMemcpyDeviceToHost);
212 for (long i = 0; i < (long)M * N; i++) printf("%d\n", hC[i]);
213 return 0;
214}
215"#;
216 format!("{kernel}{dims}{host}")
217}
218
219pub fn emit_tiled_dp4a_bench(tile: &CudaTile, m: usize, k: usize, n: usize) -> String {
223 let kernel = emit_tiled_dp4a(tile);
224 let dims = format!("#define MM {m}\n#define KK {k}\n#define NN {n}\n");
225 let host = r#"#include <cstdio>
226#include <cstdlib>
227int main() {
228 const long M = MM, K = KK, N = NN;
229 signed char *dA, *dB; int* dC;
230 cudaMalloc((void**)&dA, (size_t)M * K);
231 cudaMalloc((void**)&dB, (size_t)K * N);
232 cudaMalloc((void**)&dC, (size_t)M * N * sizeof(int));
233 cudaMemset(dA, 1, (size_t)M * K);
234 cudaMemset(dB, 1, (size_t)K * N);
235 dim3 grid(N / BN, M / BM);
236 dim3 block(NTHREAD);
237 for (int w = 0; w < 5; w++) gemm_tiled_dp4a<<<grid, block>>>(dA, dB, dC, M, N, K);
238 cudaDeviceSynchronize();
239 cudaEvent_t s, e; cudaEventCreate(&s); cudaEventCreate(&e);
240 const int ITERS = 30; float total = 0;
241 for (int it = 0; it < ITERS; it++) {
242 cudaEventRecord(s); gemm_tiled_dp4a<<<grid, block>>>(dA, dB, dC, M, N, K); cudaEventRecord(e); cudaEventSynchronize(e);
243 float ms = 0; cudaEventElapsedTime(&ms, s, e); total += ms;
244 }
245 double gops = 2.0 * (double)M * (double)N * (double)K / ((total/ITERS) / 1000.0) / 1e9;
246 printf("ms %.4f\nGOPS %.1f\n", total/ITERS, gops);
247 return 0;
248}
249"#;
250 format!("{kernel}{dims}{host}")
251}
252
253pub fn wmma_legal(m: usize, k: usize, n: usize) -> bool {
262 m % 16 == 0 && k % 16 == 0 && n % 16 == 0
263}
264
265pub fn emit_wmma_i8() -> String {
268 r#"// Auto-generated by LOGOS codegen_cuda::gemm_tile — WMMA int8 tensor-core GEMM (IMMA).
269#include <cstdint>
270#include <mma.h>
271using namespace nvcuda;
272
273#define WM 16
274#define WN 16
275#define WK 16
276
277// C(M x N) = A(M x K) . B(K x N), int8 operands, int32 accumulate, on the tensor cores. One warp computes a
278// 16x16 output tile; M, N, K must be multiples of 16 (wmma_legal).
279extern "C" __global__ void gemm_wmma_i8(const signed char* A, const signed char* B, int* C,
280 int M, int N, int K) {
281 const int tile_row = blockIdx.y; // which 16-row block of C this warp owns
282 const int tile_col = blockIdx.x; // which 16-col block of C this warp owns
283
284 wmma::fragment<wmma::matrix_a, WM, WN, WK, signed char, wmma::row_major> a_frag;
285 wmma::fragment<wmma::matrix_b, WM, WN, WK, signed char, wmma::row_major> b_frag;
286 wmma::fragment<wmma::accumulator, WM, WN, WK, int> acc_frag;
287 wmma::fill_fragment(acc_frag, 0);
288
289 for (int k = 0; k < K; k += WK) {
290 wmma::load_matrix_sync(a_frag, A + tile_row * WM * K + k, K); // A[tile_row*16 .. , k ..], ld=K
291 wmma::load_matrix_sync(b_frag, B + k * N + tile_col * WN, N); // B[k .. , tile_col*16 ..], ld=N
292 wmma::mma_sync(acc_frag, a_frag, b_frag, acc_frag);
293 }
294 wmma::store_matrix_sync(C + tile_row * WM * N + tile_col * WN, acc_frag, N, wmma::mem_row_major);
295}
296"#
297 .to_string()
298}
299
300pub fn emit_wmma_i8_program(m: usize, k: usize, n: usize) -> String {
304 let kernel = emit_wmma_i8();
305 let dims = format!("#define MM {m}\n#define KK {k}\n#define NN {n}\n");
306 let host = r#"#include <cstdio>
307#include <cstdlib>
308int main() {
309 const int M = MM, K = KK, N = NN;
310 signed char* hA = (signed char*)malloc((size_t)M * K);
311 signed char* hB = (signed char*)malloc((size_t)K * N);
312 for (long i = 0; i < (long)M * K; i++) hA[i] = (signed char)(((i * 7 + 3) % 251) - 125);
313 for (long i = 0; i < (long)K * N; i++) hB[i] = (signed char)(((i * 5 + 1) % 251) - 125);
314 signed char *dA, *dB; int* dC;
315 cudaMalloc((void**)&dA, (size_t)M * K);
316 cudaMalloc((void**)&dB, (size_t)K * N);
317 cudaMalloc((void**)&dC, (size_t)M * N * sizeof(int));
318 cudaMemcpy(dA, hA, (size_t)M * K, cudaMemcpyHostToDevice);
319 cudaMemcpy(dB, hB, (size_t)K * N, cudaMemcpyHostToDevice);
320 dim3 grid(N / 16, M / 16);
321 gemm_wmma_i8<<<grid, 32>>>(dA, dB, dC, M, N, K);
322 cudaDeviceSynchronize();
323 int* hC = (int*)malloc((size_t)M * N * sizeof(int));
324 cudaMemcpy(hC, dC, (size_t)M * N * sizeof(int), cudaMemcpyDeviceToHost);
325 for (long i = 0; i < (long)M * N; i++) printf("%d\n", hC[i]);
326 return 0;
327}
328"#;
329 format!("{kernel}{dims}{host}")
330}
331
332pub fn emit_wmma_i8_bench(m: usize, k: usize, n: usize) -> String {
334 let kernel = emit_wmma_i8();
335 let dims = format!("#define MM {m}\n#define KK {k}\n#define NN {n}\n");
336 let host = r#"#include <cstdio>
337#include <cstdlib>
338int main() {
339 const long M = MM, K = KK, N = NN;
340 signed char *dA, *dB; int* dC;
341 cudaMalloc((void**)&dA, (size_t)M * K);
342 cudaMalloc((void**)&dB, (size_t)K * N);
343 cudaMalloc((void**)&dC, (size_t)M * N * sizeof(int));
344 cudaMemset(dA, 1, (size_t)M * K);
345 cudaMemset(dB, 1, (size_t)K * N);
346 dim3 grid(N / 16, M / 16);
347 for (int w = 0; w < 5; w++) gemm_wmma_i8<<<grid, 32>>>(dA, dB, dC, M, N, K);
348 cudaDeviceSynchronize();
349 cudaEvent_t s, e; cudaEventCreate(&s); cudaEventCreate(&e);
350 const int ITERS = 30; float total = 0;
351 for (int it = 0; it < ITERS; it++) {
352 cudaEventRecord(s); gemm_wmma_i8<<<grid, 32>>>(dA, dB, dC, M, N, K); cudaEventRecord(e); cudaEventSynchronize(e);
353 float ms = 0; cudaEventElapsedTime(&ms, s, e); total += ms;
354 }
355 double gops = 2.0 * (double)M * (double)N * (double)K / ((total/ITERS) / 1000.0) / 1e9;
356 printf("ms %.4f\nGOPS %.1f\n", total/ITERS, gops);
357 return 0;
358}
359"#;
360 format!("{kernel}{dims}{host}")
361}
362
363#[derive(Debug, Clone, Copy, PartialEq, Eq)]
373pub enum MmaKind {
374 Dp4aI8,
376 WmmaI8,
378 HmmaBf16,
380 HmmaF16,
382}
383
384impl MmaKind {
385 pub fn operand_ctype(self) -> &'static str {
387 match self {
388 MmaKind::HmmaBf16 => "__nv_bfloat16",
389 MmaKind::HmmaF16 => "__half",
390 _ => "signed char",
391 }
392 }
393 pub fn acc_ctype(self) -> &'static str {
395 match self {
396 MmaKind::HmmaBf16 | MmaKind::HmmaF16 => "float",
397 _ => "int",
398 }
399 }
400 pub fn header(self) -> &'static str {
402 match self {
403 MmaKind::HmmaBf16 => "#include <cuda_bf16.h>\n",
404 MmaKind::HmmaF16 => "#include <cuda_fp16.h>\n",
405 _ => "",
406 }
407 }
408 pub fn tag(self) -> &'static str {
410 match self {
411 MmaKind::Dp4aI8 => "dp4a_i8",
412 MmaKind::WmmaI8 => "i8",
413 MmaKind::HmmaBf16 => "bf16",
414 MmaKind::HmmaF16 => "f16",
415 }
416 }
417 pub fn min_arch(self) -> u32 {
419 match self {
420 MmaKind::Dp4aI8 => 61,
421 MmaKind::HmmaF16 => 70,
422 MmaKind::WmmaI8 => 75,
423 MmaKind::HmmaBf16 => 80,
424 }
425 }
426 pub fn is_tensor_core(self) -> bool {
428 !matches!(self, MmaKind::Dp4aI8)
429 }
430 pub fn operand_bytes(self) -> usize {
433 match self {
434 MmaKind::HmmaBf16 | MmaKind::HmmaF16 => 2,
435 MmaKind::Dp4aI8 | MmaKind::WmmaI8 => 1,
436 }
437 }
438 pub fn host_convert(self, expr: &str) -> String {
440 match self {
441 MmaKind::HmmaBf16 => format!("__float2bfloat16((float)({expr}))"),
442 MmaKind::HmmaF16 => format!("__float2half((float)({expr}))"),
443 _ => format!("(signed char)({expr})"),
444 }
445 }
446}
447
448pub fn select_mma(prec: Precision, m: usize, k: usize, n: usize, sm_arch: u32) -> MmaKind {
452 let aligned = wmma_legal(m, k, n);
453 match prec {
454 Precision::Bf16 if aligned && sm_arch >= 80 => MmaKind::HmmaBf16,
455 Precision::Fp16 if aligned && sm_arch >= 70 => MmaKind::HmmaF16,
456 Precision::Int8 if aligned && sm_arch >= 75 => MmaKind::WmmaI8,
457 _ => MmaKind::Dp4aI8,
458 }
459}
460
461#[derive(Debug, Clone, Copy, PartialEq, Eq)]
463pub enum HmmaKernel {
464 CpAsync { tile: WmmaTile, stages: usize },
467 Basic,
469}
470
471pub fn select_hmma_kernel(
475 kind: MmaKind,
476 m: usize,
477 k: usize,
478 n: usize,
479 sm_arch: u32,
480 gpu: &GpuLimits,
481) -> HmmaKernel {
482 let tile = default_wmma_tile();
483 let stages = 2;
484 let epc = 16 / kind.operand_bytes(); let tile_divides = m % tile.bm == 0 && n % tile.bn == 0 && k % tile.bk == 0;
486 let chunks_align = tile.bk % epc == 0 && tile.bn % epc == 0;
487 let footprint = stages * (tile.bm * tile.bk + tile.bk * tile.bn) * kind.operand_bytes();
489 if sm_arch >= 80
490 && tile_divides
491 && chunks_align
492 && footprint <= gpu.smem_bytes
493 && tile.threads() <= gpu.max_threads
494 {
495 HmmaKernel::CpAsync { tile, stages }
496 } else {
497 HmmaKernel::Basic
498 }
499}
500
501pub fn lattice_best_bf16_bench(
506 m: usize,
507 k: usize,
508 n: usize,
509 sm_arch: u32,
510 gpu: &GpuLimits,
511) -> String {
512 match select_hmma_kernel(MmaKind::HmmaBf16, m, k, n, sm_arch, gpu) {
513 HmmaKernel::CpAsync { tile, stages } => {
514 emit_wmma_cpasync_bench(MmaKind::HmmaBf16, &tile, stages, m, k, n)
515 }
516 HmmaKernel::Basic => emit_wmma_basic_bench(MmaKind::HmmaBf16, m, k, n),
517 }
518}
519
520pub fn emit_wmma(kind: MmaKind) -> String {
524 let op = kind.operand_ctype();
525 let acc = kind.acc_ctype();
526 let tag = kind.tag();
527 let header = kind.header();
528 let zero = if acc == "float" { "0.0f" } else { "0" };
529 format!(
530 r#"// Auto-generated by LOGOS codegen_cuda::gemm_tile — WMMA {tag} tensor-core GEMM ({op} -> {acc}).
531#include <cstdint>
532#include <mma.h>
533{header}using namespace nvcuda;
534
535#define WM 16
536#define WN 16
537#define WK 16
538
539// C(M x N) = A(M x K) . B(K x N); {op} operands, {acc} accumulate, on the tensor cores. One warp = a 16x16
540// output tile; M, N, K multiples of 16 (wmma_legal).
541extern "C" __global__ void gemm_wmma_{tag}(const {op}* A, const {op}* B, {acc}* C, int M, int N, int K) {{
542 const int tile_row = blockIdx.y;
543 const int tile_col = blockIdx.x;
544 wmma::fragment<wmma::matrix_a, WM, WN, WK, {op}, wmma::row_major> a_frag;
545 wmma::fragment<wmma::matrix_b, WM, WN, WK, {op}, wmma::row_major> b_frag;
546 wmma::fragment<wmma::accumulator, WM, WN, WK, {acc}> acc_frag;
547 wmma::fill_fragment(acc_frag, {zero});
548 for (int k = 0; k < K; k += WK) {{
549 wmma::load_matrix_sync(a_frag, A + tile_row * WM * K + k, K);
550 wmma::load_matrix_sync(b_frag, B + k * N + tile_col * WN, N);
551 wmma::mma_sync(acc_frag, a_frag, b_frag, acc_frag);
552 }}
553 wmma::store_matrix_sync(C + tile_row * WM * N + tile_col * WN, acc_frag, N, wmma::mem_row_major);
554}}
555"#,
556 tag = tag,
557 op = op,
558 acc = acc,
559 header = header,
560 zero = zero,
561 )
562}
563
564pub fn emit_wmma_program(kind: MmaKind, m: usize, k: usize, n: usize) -> String {
569 let kernel = emit_wmma(kind);
570 let op = kind.operand_ctype();
571 let acc = kind.acc_ctype();
572 let tag = kind.tag();
573 let conv_a = kind.host_convert("(i % 7) - 3");
574 let conv_b = kind.host_convert("(i % 5) - 2");
575 format!(
576 r#"{kernel}#include <cstdio>
577#include <cstdlib>
578#define MM {m}
579#define KK {k}
580#define NN {n}
581int main() {{
582 const int M = MM, K = KK, N = NN;
583 {op}* hA = ({op}*)malloc((size_t)M * K * sizeof({op}));
584 {op}* hB = ({op}*)malloc((size_t)K * N * sizeof({op}));
585 for (long i = 0; i < (long)M * K; i++) hA[i] = {conv_a};
586 for (long i = 0; i < (long)K * N; i++) hB[i] = {conv_b};
587 {op} *dA, *dB; {acc}* dC;
588 cudaMalloc((void**)&dA, (size_t)M * K * sizeof({op}));
589 cudaMalloc((void**)&dB, (size_t)K * N * sizeof({op}));
590 cudaMalloc((void**)&dC, (size_t)M * N * sizeof({acc}));
591 cudaMemcpy(dA, hA, (size_t)M * K * sizeof({op}), cudaMemcpyHostToDevice);
592 cudaMemcpy(dB, hB, (size_t)K * N * sizeof({op}), cudaMemcpyHostToDevice);
593 dim3 grid(N / 16, M / 16);
594 gemm_wmma_{tag}<<<grid, 32>>>(dA, dB, dC, M, N, K);
595 cudaDeviceSynchronize();
596 {acc}* hC = ({acc}*)malloc((size_t)M * N * sizeof({acc}));
597 cudaMemcpy(hC, dC, (size_t)M * N * sizeof({acc}), cudaMemcpyDeviceToHost);
598 double sum = 0.0; for (long i = 0; i < (long)M * N; i++) sum += (double)hC[i];
599 printf("%.1f\n", sum);
600 return 0;
601}}
602"#,
603 kernel = kernel,
604 op = op,
605 acc = acc,
606 tag = tag,
607 m = m,
608 k = k,
609 n = n,
610 conv_a = conv_a,
611 conv_b = conv_b,
612 )
613}
614
615pub const AMPERE_SM_REGS: usize = 65536;
627
628impl CudaTile {
629 pub fn regs_per_thread(&self) -> usize {
631 self.tm * self.tn + 24
632 }
633
634 pub fn is_feasible(&self, gpu: &GpuLimits) -> bool {
636 self.is_legal(gpu) && self.regs_per_thread() * self.threads() <= AMPERE_SM_REGS
637 }
638
639 pub fn blocks_per_sm(&self) -> usize {
642 let by_regs = AMPERE_SM_REGS / (self.regs_per_thread() * self.threads()).max(1);
643 let by_smem = 102_400 / self.smem_bytes().max(1);
644 let by_thread = 1536 / self.threads().max(1);
645 by_regs.min(by_smem).min(by_thread).min(16)
646 }
647
648 pub fn occupancy(&self) -> f64 {
650 (self.blocks_per_sm() * self.threads() / 32) as f64 / 48.0
651 }
652
653 pub fn dram_traffic(&self) -> f64 {
657 1.0 / self.bm as f64 + 1.0 / self.bn as f64
658 }
659
660 pub fn smem_traffic(&self) -> f64 {
664 1.0 / self.tm as f64 + 1.0 / self.tn as f64
665 }
666
667 pub fn dominates(&self, other: &CudaTile) -> bool {
673 let weakly = self.dram_traffic() <= other.dram_traffic()
674 && self.smem_traffic() <= other.smem_traffic()
675 && self.occupancy() >= other.occupancy();
676 let strict = self.dram_traffic() < other.dram_traffic()
677 || self.smem_traffic() < other.smem_traffic()
678 || self.occupancy() > other.occupancy();
679 weakly && strict
680 }
681}
682
683pub fn feasible_tiles(gpu: &GpuLimits) -> Vec<CudaTile> {
686 let mut v = Vec::new();
687 for &bm in &[32usize, 64, 128, 256] {
688 for &bn in &[32usize, 64, 128, 256] {
689 for &bk in &[8usize, 16, 32] {
690 for &tm in &[4usize, 8, 16] {
691 for &tn in &[4usize, 8, 16] {
692 let t = CudaTile { bm, bn, bk, tm, tn };
693 if t.is_feasible(gpu) {
694 v.push(t);
695 }
696 }
697 }
698 }
699 }
700 }
701 v
702}
703
704pub fn pareto_frontier(gpu: &GpuLimits) -> Vec<CudaTile> {
711 let all = feasible_tiles(gpu);
712 all.iter()
713 .filter(|t| !all.iter().any(|o| o.dominates(t)))
714 .copied()
715 .collect()
716}
717
718#[derive(Debug, Clone, Copy, PartialEq, Eq)]
721pub enum ComputePrimitive {
722 Dp4aCudaCore,
724 WmmaTensorCore,
726}
727
728pub fn select_primitive(m: usize, k: usize, n: usize, has_tensor_cores: bool) -> ComputePrimitive {
733 if has_tensor_cores && wmma_legal(m, k, n) {
734 ComputePrimitive::WmmaTensorCore
735 } else {
736 ComputePrimitive::Dp4aCudaCore
737 }
738}
739
740pub fn emit_gemm_program(
744 m: usize,
745 k: usize,
746 n: usize,
747 has_tensor_cores: bool,
748 tile: &CudaTile,
749) -> String {
750 match select_primitive(m, k, n, has_tensor_cores) {
751 ComputePrimitive::WmmaTensorCore => emit_wmma_i8_program(m, k, n),
752 ComputePrimitive::Dp4aCudaCore => emit_tiled_dp4a_program(tile, m, k, n),
753 }
754}
755
756#[derive(Debug, Clone, Copy, PartialEq, Eq)]
766pub struct WmmaTile {
767 pub bm: usize,
768 pub bn: usize,
769 pub bk: usize,
770 pub warps_m: usize,
771 pub warps_n: usize,
772}
773
774impl WmmaTile {
775 pub fn warps(&self) -> usize {
776 self.warps_m * self.warps_n
777 }
778 pub fn threads(&self) -> usize {
779 self.warps() * 32
780 }
781 pub fn wm(&self) -> usize {
783 self.bm / self.warps_m
784 }
785 pub fn wn(&self) -> usize {
786 self.bn / self.warps_n
787 }
788 pub fn smem_bytes(&self) -> usize {
790 self.bm * self.bk + self.bk * self.bn
791 }
792 pub fn acc_regs(&self) -> usize {
794 (self.wm() / 16) * (self.wn() / 16) * 8
795 }
796 pub fn is_legal(&self, gpu: &GpuLimits) -> bool {
798 self.bm % 16 == 0
799 && self.bn % 16 == 0
800 && self.bk % 16 == 0
801 && self.warps_m > 0
802 && self.warps_n > 0
803 && self.bm % self.warps_m == 0
804 && self.bn % self.warps_n == 0
805 && self.wm() % 16 == 0
806 && self.wn() % 16 == 0
807 && self.threads() <= gpu.max_threads
808 && self.smem_bytes() <= gpu.smem_bytes
809 && self.acc_regs() + 32 <= 255 }
811}
812
813pub fn default_wmma_tile() -> WmmaTile {
816 WmmaTile {
817 bm: 128,
818 bn: 128,
819 bk: 32,
820 warps_m: 2,
821 warps_n: 4,
822 }
823}
824
825fn wmma_tiled_kernel(t: &WmmaTile) -> String {
826 wmma_tiled_kernel_for(t, MmaKind::WmmaI8)
827}
828
829fn wmma_tiled_kernel_for(t: &WmmaTile, kind: MmaKind) -> String {
833 let defs = format!(
834 "#define BM {}\n#define BN {}\n#define BK {}\n#define WARPS_M {}\n#define WARPS_N {}\n",
835 t.bm, t.bn, t.bk, t.warps_m, t.warps_n,
836 );
837 let op = kind.operand_ctype();
838 let acc = kind.acc_ctype();
839 let zero = if acc == "float" { "0.0f" } else { "0" };
840 let body = WMMA_TILED_BODY_I8
841 .replace(
842 "#include <mma.h>\n",
843 &format!("#include <mma.h>\n{}", kind.header()),
844 )
845 .replace("signed char", op)
846 .replace("int* C,", &format!("{acc}* C,"))
847 .replace(
848 "wmma::accumulator, 16, 16, 16, int>",
849 &format!("wmma::accumulator, 16, 16, 16, {acc}>"),
850 )
851 .replace(
852 "wmma::fill_fragment(acc[i][j], 0)",
853 &format!("wmma::fill_fragment(acc[i][j], {zero})"),
854 );
855 format!("{defs}{body}")
856}
857
858const WMMA_TILED_BODY_I8: &str = r#"#include <mma.h>
859using namespace nvcuda;
860#define WM (BM / WARPS_M)
861#define WN (BN / WARPS_N)
862#define NTHREAD (WARPS_M * WARPS_N * 32)
863// C(M x N) = A(M x K) . B(K x N); A,B row-major int8; int32 accumulate; tensor cores fed from shared memory.
864extern "C" __global__ void gemm_wmma_tiled(const signed char* A, const signed char* B, int* C,
865 int M, int N, int K) {
866 __shared__ signed char As[BM * BK];
867 __shared__ signed char Bs[BK * BN];
868 const int tid = threadIdx.x;
869 const int warpId = tid / 32;
870 const int warp_row = (warpId / WARPS_N) * WM;
871 const int warp_col = (warpId % WARPS_N) * WN;
872 const int block_row = blockIdx.y * BM;
873 const int block_col = blockIdx.x * BN;
874
875 wmma::fragment<wmma::accumulator, 16, 16, 16, int> acc[WM / 16][WN / 16];
876 #pragma unroll
877 for (int i = 0; i < WM / 16; ++i)
878 #pragma unroll
879 for (int j = 0; j < WN / 16; ++j) wmma::fill_fragment(acc[i][j], 0);
880
881 for (int k0 = 0; k0 < K; k0 += BK) {
882 for (int idx = tid; idx < BM * BK; idx += NTHREAD)
883 As[idx] = A[(block_row + idx / BK) * K + (k0 + idx % BK)];
884 for (int idx = tid; idx < BK * BN; idx += NTHREAD)
885 Bs[idx] = B[(k0 + idx / BN) * N + (block_col + idx % BN)];
886 __syncthreads();
887
888 #pragma unroll
889 for (int kk = 0; kk < BK; kk += 16) {
890 wmma::fragment<wmma::matrix_a, 16, 16, 16, signed char, wmma::row_major> a_frag[WM / 16];
891 wmma::fragment<wmma::matrix_b, 16, 16, 16, signed char, wmma::row_major> b_frag[WN / 16];
892 #pragma unroll
893 for (int i = 0; i < WM / 16; ++i)
894 wmma::load_matrix_sync(a_frag[i], As + (warp_row + i * 16) * BK + kk, BK);
895 #pragma unroll
896 for (int j = 0; j < WN / 16; ++j)
897 wmma::load_matrix_sync(b_frag[j], Bs + kk * BN + (warp_col + j * 16), BN);
898 #pragma unroll
899 for (int i = 0; i < WM / 16; ++i)
900 #pragma unroll
901 for (int j = 0; j < WN / 16; ++j)
902 wmma::mma_sync(acc[i][j], a_frag[i], b_frag[j], acc[i][j]);
903 }
904 __syncthreads();
905 }
906
907 #pragma unroll
908 for (int i = 0; i < WM / 16; ++i)
909 #pragma unroll
910 for (int j = 0; j < WN / 16; ++j)
911 wmma::store_matrix_sync(C + (block_row + warp_row + i * 16) * N + (block_col + warp_col + j * 16),
912 acc[i][j], N, wmma::mem_row_major);
913}
914"#;
915
916pub fn emit_wmma_tiled_i8_for(tile: &WmmaTile) -> String {
918 wmma_tiled_kernel(tile)
919}
920
921pub fn emit_wmma_tiled_i8() -> String {
923 wmma_tiled_kernel(&default_wmma_tile())
924}
925
926pub fn emit_wmma_tiled_program(
930 kind: MmaKind,
931 tile: &WmmaTile,
932 m: usize,
933 k: usize,
934 n: usize,
935) -> String {
936 let kernel = wmma_tiled_kernel_for(tile, kind);
937 let op = kind.operand_ctype();
938 let acc = kind.acc_ctype();
939 let conv_a = kind.host_convert("(p % 7) - 3");
940 let conv_b = kind.host_convert("(p % 5) - 2");
941 format!(
942 r#"{kernel}#include <cstdio>
943#include <cstdlib>
944#include <cmath>
945#define M {m}
946#define N {n}
947#define K {k}
948int main(void){{
949 const int MM=M,KK=K,NN=N;
950 {op}* hA=({op}*)malloc((size_t)MM*KK*sizeof({op}));
951 {op}* hB=({op}*)malloc((size_t)KK*NN*sizeof({op}));
952 for(long p=0;p<(long)MM*KK;p++) hA[p]={conv_a};
953 for(long p=0;p<(long)KK*NN;p++) hB[p]={conv_b};
954 {op} *dA,*dB; {acc}* dC;
955 if(cudaMalloc((void**)&dA,(size_t)MM*KK*sizeof({op}))||cudaMalloc((void**)&dB,(size_t)KK*NN*sizeof({op}))||cudaMalloc((void**)&dC,(size_t)MM*NN*sizeof({acc}))){{ fprintf(stderr,"malloc\n"); return 1; }}
956 cudaMemcpy(dA,hA,(size_t)MM*KK*sizeof({op}),cudaMemcpyHostToDevice);
957 cudaMemcpy(dB,hB,(size_t)KK*NN*sizeof({op}),cudaMemcpyHostToDevice);
958 dim3 grid(NN/{bn}, MM/{bm}); gemm_wmma_tiled<<<grid, {threads}>>>(dA,dB,dC,MM,NN,KK);
959 if(cudaDeviceSynchronize()!=cudaSuccess){{ fprintf(stderr,"sync\n"); return 1; }}
960 {acc}* hC=({acc}*)malloc((size_t)MM*NN*sizeof({acc}));
961 cudaMemcpy(hC,dC,(size_t)MM*NN*sizeof({acc}),cudaMemcpyDeviceToHost);
962 double sum=0; for(long t=0;t<(long)MM*NN;t++) sum+=(double)hC[t];
963 printf("%lld\n",(long long)llround(sum));
964 return 0;
965}}
966"#,
967 kernel = kernel,
968 op = op,
969 acc = acc,
970 m = m,
971 k = k,
972 n = n,
973 conv_a = conv_a,
974 conv_b = conv_b,
975 bm = tile.bm,
976 bn = tile.bn,
977 threads = tile.threads()
978 )
979}
980
981pub fn emit_wmma_basic_bench(kind: MmaKind, m: usize, k: usize, n: usize) -> String {
983 let kernel = emit_wmma(kind);
984 let op = kind.operand_ctype();
985 let acc = kind.acc_ctype();
986 let tag = kind.tag();
987 gemm_timed_main(
988 &kernel,
989 op,
990 acc,
991 &format!("gemm_wmma_{tag}<<<grid, 32>>>"),
992 "dim3 grid(N/16, M/16);",
993 m,
994 k,
995 n,
996 )
997}
998
999pub fn emit_wmma_tiled_bench(
1001 kind: MmaKind,
1002 tile: &WmmaTile,
1003 m: usize,
1004 k: usize,
1005 n: usize,
1006) -> String {
1007 let kernel = wmma_tiled_kernel_for(tile, kind);
1008 let op = kind.operand_ctype();
1009 let acc = kind.acc_ctype();
1010 let launch = format!("gemm_wmma_tiled<<<grid, {}>>>", tile.threads());
1011 let grid = format!("dim3 grid(N/{}, M/{});", tile.bn, tile.bm);
1012 gemm_timed_main(&kernel, op, acc, &launch, &grid, m, k, n)
1013}
1014
1015fn gemm_timed_main(
1018 kernel: &str,
1019 op: &str,
1020 acc: &str,
1021 launch: &str,
1022 grid: &str,
1023 m: usize,
1024 k: usize,
1025 n: usize,
1026) -> String {
1027 format!(
1028 r#"{kernel}#include <cstdio>
1029int main(){{
1030 const long M={m}, K={k}, N={n};
1031 {op} *dA,*dB; {acc}* dC;
1032 cudaMalloc((void**)&dA,(size_t)M*K*sizeof({op})); cudaMalloc((void**)&dB,(size_t)K*N*sizeof({op})); cudaMalloc((void**)&dC,(size_t)M*N*sizeof({acc}));
1033 cudaMemset(dA,1,(size_t)M*K*sizeof({op})); cudaMemset(dB,1,(size_t)K*N*sizeof({op}));
1034 {grid}
1035 for(int w=0;w<5;w++) {launch}(dA,dB,dC,M,N,K);
1036 cudaDeviceSynchronize();
1037 cudaEvent_t s,e; cudaEventCreate(&s); cudaEventCreate(&e);
1038 const int ITERS=30; float total=0;
1039 for(int it=0;it<ITERS;it++){{ cudaEventRecord(s); {launch}(dA,dB,dC,M,N,K); cudaEventRecord(e); cudaEventSynchronize(e); float ms=0; cudaEventElapsedTime(&ms,s,e); total+=ms; }}
1040 double gops = 2.0*(double)M*(double)N*(double)K/((total/ITERS)/1000.0)/1e9;
1041 printf("GOPS %.1f\n", gops);
1042 return 0;
1043}}
1044"#,
1045 kernel = kernel,
1046 op = op,
1047 acc = acc,
1048 launch = launch,
1049 grid = grid,
1050 m = m,
1051 k = k,
1052 n = n
1053 )
1054}
1055
1056fn wmma_tiled_program_for(tile: &WmmaTile, m: usize, k: usize, n: usize) -> String {
1057 format!(
1058 r#"{kernel}
1059#include <cstdio>
1060#include <cstdlib>
1061int main() {{
1062 const int M = {m}, K = {k}, N = {n};
1063 signed char *hA = (signed char*)malloc((size_t)M*K), *hB = (signed char*)malloc((size_t)K*N);
1064 for (long i = 0; i < (long)M*K; ++i) hA[i] = (signed char)(((i*7+3) % 251) - 125);
1065 for (long i = 0; i < (long)K*N; ++i) hB[i] = (signed char)(((i*5+1) % 251) - 125);
1066 signed char *dA, *dB; int *dC, *hC = (int*)malloc((size_t)M*N*sizeof(int));
1067 cudaMalloc(&dA, (size_t)M*K); cudaMalloc(&dB, (size_t)K*N); cudaMalloc(&dC, (size_t)M*N*sizeof(int));
1068 cudaMemcpy(dA, hA, (size_t)M*K, cudaMemcpyHostToDevice);
1069 cudaMemcpy(dB, hB, (size_t)K*N, cudaMemcpyHostToDevice);
1070 dim3 grid(N/{bn}, M/{bm}); gemm_wmma_tiled<<<grid, {threads}>>>(dA, dB, dC, M, N, K);
1071 cudaDeviceSynchronize();
1072 cudaMemcpy(hC, dC, (size_t)M*N*sizeof(int), cudaMemcpyDeviceToHost);
1073 for (long i = 0; i < (long)M*N; ++i) printf("%d\n", hC[i]);
1074 return 0;
1075}}
1076"#,
1077 kernel = wmma_tiled_kernel(tile),
1078 m = m,
1079 k = k,
1080 n = n,
1081 bm = tile.bm,
1082 bn = tile.bn,
1083 threads = tile.threads()
1084 )
1085}
1086
1087fn wmma_tiled_bench_for(tile: &WmmaTile, m: usize, k: usize, n: usize) -> String {
1088 format!(
1089 r#"{kernel}
1090#include <cstdio>
1091#include <cstdlib>
1092int main() {{
1093 const int M = {m}, K = {k}, N = {n};
1094 signed char *dA, *dB; int *dC;
1095 cudaMalloc(&dA, (size_t)M*K); cudaMalloc(&dB, (size_t)K*N); cudaMalloc(&dC, (size_t)M*N*sizeof(int));
1096 cudaMemset(dA, 1, (size_t)M*K); cudaMemset(dB, 1, (size_t)K*N);
1097 dim3 grid(N/{bn}, M/{bm});
1098 gemm_wmma_tiled<<<grid, {threads}>>>(dA, dB, dC, M, N, K); cudaDeviceSynchronize();
1099 cudaEvent_t s, e; cudaEventCreate(&s); cudaEventCreate(&e);
1100 const int IT = 30; float total = 0;
1101 for (int it = 0; it < IT; ++it) {{
1102 cudaEventRecord(s); gemm_wmma_tiled<<<grid, {threads}>>>(dA, dB, dC, M, N, K); cudaEventRecord(e); cudaEventSynchronize(e);
1103 float ms = 0; cudaEventElapsedTime(&ms, s, e); total += ms;
1104 }}
1105 double gops = 2.0*M*N*K / ((total/IT)/1e3) / 1e9;
1106 printf("GOPS %.1f\n", gops);
1107 return 0;
1108}}
1109"#,
1110 kernel = wmma_tiled_kernel(tile),
1111 m = m,
1112 k = k,
1113 n = n,
1114 bm = tile.bm,
1115 bn = tile.bn,
1116 threads = tile.threads()
1117 )
1118}
1119
1120pub fn emit_wmma_tiled_i8_program(m: usize, k: usize, n: usize) -> String {
1122 wmma_tiled_program_for(&default_wmma_tile(), m, k, n)
1123}
1124pub fn emit_wmma_tiled_i8_bench(m: usize, k: usize, n: usize) -> String {
1126 wmma_tiled_bench_for(&default_wmma_tile(), m, k, n)
1127}
1128pub fn emit_wmma_tiled_i8_program_for(tile: &WmmaTile, m: usize, k: usize, n: usize) -> String {
1130 wmma_tiled_program_for(tile, m, k, n)
1131}
1132pub fn emit_wmma_tiled_i8_bench_for(tile: &WmmaTile, m: usize, k: usize, n: usize) -> String {
1134 wmma_tiled_bench_for(tile, m, k, n)
1135}
1136
1137impl WmmaTile {
1145 pub fn cpasync_legal(&self, gpu: &GpuLimits) -> bool {
1147 self.is_legal(gpu)
1148 && self.bk % 16 == 0
1149 && self.bn % 16 == 0
1150 && (self.bm * self.bk) % 16 == 0
1151 && 2 * self.smem_bytes() <= gpu.smem_bytes }
1153}
1154
1155fn wmma_cpasync_kernel(t: &WmmaTile) -> String {
1156 wmma_cpasync_kernel_staged(t, 2)
1157}
1158
1159fn wmma_cpasync_kernel_staged(t: &WmmaTile, stages: usize) -> String {
1160 wmma_cpasync_kernel_staged_for(t, stages, MmaKind::WmmaI8)
1161}
1162
1163fn wmma_cpasync_kernel_staged_for(t: &WmmaTile, stages: usize, kind: MmaKind) -> String {
1167 let op = kind.operand_ctype();
1168 let acc = kind.acc_ctype();
1169 let zero = if acc == "float" { "0.0f" } else { "0" };
1170 let epc = if op == "signed char" { 16 } else { 8 }; let defs = format!(
1172 "#define BM {}\n#define BN {}\n#define BK {}\n#define WARPS_M {}\n#define WARPS_N {}\n#define STAGES {}\n#define EPC {}\n",
1173 t.bm, t.bn, t.bk, t.warps_m, t.warps_n, stages, epc,
1174 );
1175 let body = CPASYNC_BODY_I8
1176 .replace(
1177 "#include <cuda_pipeline.h>\n",
1178 &format!("#include <cuda_pipeline.h>\n{}", kind.header()),
1179 )
1180 .replace("signed char", op)
1181 .replace("int* C,", &format!("{acc}* C,"))
1182 .replace(
1183 "wmma::accumulator, 16, 16, 16, int>",
1184 &format!("wmma::accumulator, 16, 16, 16, {acc}>"),
1185 )
1186 .replace(
1187 "wmma::fill_fragment(acc[i][j], 0)",
1188 &format!("wmma::fill_fragment(acc[i][j], {zero})"),
1189 )
1190 .replace("(BM * BK) / 16", "(BM * BK) / EPC")
1191 .replace("(BK * BN) / 16", "(BK * BN) / EPC")
1192 .replace(
1193 "(idx * 16) / BK, c = (idx * 16) % BK",
1194 "(idx * EPC) / BK, c = (idx * EPC) % BK",
1195 )
1196 .replace(
1197 "(idx * 16) / BN, c = (idx * 16) % BN",
1198 "(idx * EPC) / BN, c = (idx * EPC) % BN",
1199 );
1200 format!("{defs}{body}")
1201}
1202
1203const CPASYNC_BODY_I8: &str = r#"#include <mma.h>
1204#include <cuda_pipeline.h>
1205using namespace nvcuda;
1206#define WM (BM / WARPS_M)
1207#define WN (BN / WARPS_N)
1208#define NTHREAD (WARPS_M * WARPS_N * 32)
1209extern "C" __global__ void gemm_wmma_cpasync(const signed char* A, const signed char* B, int* C,
1210 int M, int N, int K) {
1211 __shared__ signed char As[STAGES][BM * BK];
1212 __shared__ signed char Bs[STAGES][BK * BN];
1213 const int tid = threadIdx.x;
1214 const int warpId = tid / 32;
1215 const int warp_row = (warpId / WARPS_N) * WM;
1216 const int warp_col = (warpId % WARPS_N) * WN;
1217 const int block_row = blockIdx.y * BM;
1218 const int block_col = blockIdx.x * BN;
1219 const int nstages = K / BK;
1220
1221 wmma::fragment<wmma::accumulator, 16, 16, 16, int> acc[WM / 16][WN / 16];
1222 #pragma unroll
1223 for (int i = 0; i < WM / 16; ++i)
1224 #pragma unroll
1225 for (int j = 0; j < WN / 16; ++j) wmma::fill_fragment(acc[i][j], 0);
1226
1227 // stream stage `st` (K offset `st*BK`) into shared buffer `buf` with 16-byte async copies.
1228 #define LOAD_STAGE(buf, st) do { \
1229 int _k0 = (st) * BK; \
1230 for (int idx = tid; idx < (BM * BK) / 16; idx += NTHREAD) { \
1231 int r = (idx * 16) / BK, c = (idx * 16) % BK; \
1232 __pipeline_memcpy_async(&As[buf][r * BK + c], &A[(block_row + r) * K + _k0 + c], 16); \
1233 } \
1234 for (int idx = tid; idx < (BK * BN) / 16; idx += NTHREAD) { \
1235 int r = (idx * 16) / BN, c = (idx * 16) % BN; \
1236 __pipeline_memcpy_async(&Bs[buf][r * BN + c], &B[(_k0 + r) * N + block_col + c], 16); \
1237 } \
1238 __pipeline_commit(); \
1239 } while (0)
1240
1241 // prologue: prefetch the first STAGES-1 K-tiles so STAGES-1 async groups are always in flight.
1242 #pragma unroll
1243 for (int i = 0; i < STAGES - 1; ++i) { if (i < nstages) LOAD_STAGE(i, i); else __pipeline_commit(); }
1244
1245 for (int s = 0; s < nstages; ++s) {
1246 __pipeline_wait_prior(STAGES - 2); // block until stage s's buffer has landed
1247 __syncthreads();
1248
1249 int buf = s % STAGES;
1250 #pragma unroll
1251 for (int kk = 0; kk < BK; kk += 16) {
1252 wmma::fragment<wmma::matrix_a, 16, 16, 16, signed char, wmma::row_major> a_frag[WM / 16];
1253 wmma::fragment<wmma::matrix_b, 16, 16, 16, signed char, wmma::row_major> b_frag[WN / 16];
1254 #pragma unroll
1255 for (int i = 0; i < WM / 16; ++i)
1256 wmma::load_matrix_sync(a_frag[i], As[buf] + (warp_row + i * 16) * BK + kk, BK);
1257 #pragma unroll
1258 for (int j = 0; j < WN / 16; ++j)
1259 wmma::load_matrix_sync(b_frag[j], Bs[buf] + kk * BN + (warp_col + j * 16), BN);
1260 #pragma unroll
1261 for (int i = 0; i < WM / 16; ++i)
1262 #pragma unroll
1263 for (int j = 0; j < WN / 16; ++j)
1264 wmma::mma_sync(acc[i][j], a_frag[i], b_frag[j], acc[i][j]);
1265 }
1266 __syncthreads();
1267 // prefetch the tile STAGES-1 ahead into its rotating buffer, keeping the pipeline full.
1268 int nxt = s + STAGES - 1;
1269 if (nxt < nstages) LOAD_STAGE(nxt % STAGES, nxt); else __pipeline_commit();
1270 }
1271
1272 #pragma unroll
1273 for (int i = 0; i < WM / 16; ++i)
1274 #pragma unroll
1275 for (int j = 0; j < WN / 16; ++j)
1276 wmma::store_matrix_sync(C + (block_row + warp_row + i * 16) * N + (block_col + warp_col + j * 16),
1277 acc[i][j], N, wmma::mem_row_major);
1278}
1279"#;
1280
1281pub fn emit_wmma_cpasync_i8_for(tile: &WmmaTile) -> String {
1282 wmma_cpasync_kernel(tile)
1283}
1284
1285fn wmma_cpasync_harness(
1286 tile: &WmmaTile,
1287 stages: usize,
1288 m: usize,
1289 k: usize,
1290 n: usize,
1291 bench: bool,
1292) -> String {
1293 let launch = format!(
1294 "dim3 grid(N/{}, M/{}); gemm_wmma_cpasync<<<grid, {}>>>",
1295 tile.bn,
1296 tile.bm,
1297 tile.threads()
1298 );
1299 if bench {
1300 format!(
1301 r#"{kernel}
1302#include <cstdio>
1303#include <cstdlib>
1304int main() {{
1305 const int M = {m}, K = {k}, N = {n};
1306 signed char *dA, *dB; int *dC;
1307 cudaMalloc(&dA, (size_t)M*K); cudaMalloc(&dB, (size_t)K*N); cudaMalloc(&dC, (size_t)M*N*sizeof(int));
1308 // A,B all ones → every C[i][j] must equal K. In-harness correctness GUARD: a kernel that races (computes
1309 // garbage fast) or fails to launch can never print a GOPS number.
1310 cudaMemset(dA, 1, (size_t)M*K); cudaMemset(dB, 1, (size_t)K*N);
1311 {launch}(dA, dB, dC, M, N, K);
1312 cudaError_t err = cudaDeviceSynchronize();
1313 if (err != cudaSuccess) {{ printf("LAUNCH_ERROR %s\n", cudaGetErrorString(err)); return 1; }}
1314 int *hC = (int*)malloc((size_t)M*N*sizeof(int));
1315 cudaMemcpy(hC, dC, (size_t)M*N*sizeof(int), cudaMemcpyDeviceToHost);
1316 long bad = 0; long step = ((long)M*N) / 4096 + 1;
1317 for (long i = 0; i < (long)M*N; i += step) if (hC[i] != K) {{ if (bad < 3) printf("WRONG at %ld: got %d want %d\n", i, hC[i], K); bad++; }}
1318 if (bad) {{ printf("INCORRECT %ld samples\n", bad); return 1; }}
1319 // Per-iteration SYNCED timing: each launch completes before the next is queued. Firing async launches in a
1320 // tight loop and bracketing with events under-times badly (rapid launches error/no-op while a stale
1321 // correct C sits in place) — this is the honest method.
1322 cudaEvent_t s, e; cudaEventCreate(&s); cudaEventCreate(&e);
1323 const int IT = 30; float total = 0;
1324 for (int it = 0; it < IT; ++it) {{
1325 cudaEventRecord(s); {launch}(dA, dB, dC, M, N, K); cudaEventRecord(e); cudaEventSynchronize(e);
1326 float ms = 0; cudaEventElapsedTime(&ms, s, e); total += ms;
1327 }}
1328 cudaError_t le = cudaGetLastError();
1329 if (le != cudaSuccess) {{ printf("LOOP_ERROR %s\n", cudaGetErrorString(le)); return 1; }}
1330 printf("GOPS %.1f\n", 2.0*M*N*K / ((total/IT)/1e3) / 1e9);
1331 return 0;
1332}}
1333"#,
1334 kernel = wmma_cpasync_kernel_staged(tile, stages),
1335 m = m,
1336 k = k,
1337 n = n,
1338 launch = launch
1339 )
1340 } else {
1341 format!(
1342 r#"{kernel}
1343#include <cstdio>
1344#include <cstdlib>
1345int main() {{
1346 const int M = {m}, K = {k}, N = {n};
1347 signed char *hA = (signed char*)malloc((size_t)M*K), *hB = (signed char*)malloc((size_t)K*N);
1348 for (long i = 0; i < (long)M*K; ++i) hA[i] = (signed char)(((i*7+3) % 251) - 125);
1349 for (long i = 0; i < (long)K*N; ++i) hB[i] = (signed char)(((i*5+1) % 251) - 125);
1350 signed char *dA, *dB; int *dC, *hC = (int*)malloc((size_t)M*N*sizeof(int));
1351 cudaMalloc(&dA, (size_t)M*K); cudaMalloc(&dB, (size_t)K*N); cudaMalloc(&dC, (size_t)M*N*sizeof(int));
1352 cudaMemcpy(dA, hA, (size_t)M*K, cudaMemcpyHostToDevice);
1353 cudaMemcpy(dB, hB, (size_t)K*N, cudaMemcpyHostToDevice);
1354 {launch}(dA, dB, dC, M, N, K); cudaDeviceSynchronize();
1355 cudaMemcpy(hC, dC, (size_t)M*N*sizeof(int), cudaMemcpyDeviceToHost);
1356 for (long i = 0; i < (long)M*N; ++i) printf("%d\n", hC[i]);
1357 return 0;
1358}}
1359"#,
1360 kernel = wmma_cpasync_kernel_staged(tile, stages),
1361 m = m,
1362 k = k,
1363 n = n,
1364 launch = launch
1365 )
1366 }
1367}
1368
1369pub fn emit_wmma_cpasync_i8_program_for(tile: &WmmaTile, m: usize, k: usize, n: usize) -> String {
1370 wmma_cpasync_harness(tile, 2, m, k, n, false)
1371}
1372pub fn emit_wmma_cpasync_i8_bench_for(tile: &WmmaTile, m: usize, k: usize, n: usize) -> String {
1373 wmma_cpasync_harness(tile, 2, m, k, n, true)
1374}
1375pub fn emit_wmma_cpasync_i8_program_staged(
1378 tile: &WmmaTile,
1379 stages: usize,
1380 m: usize,
1381 k: usize,
1382 n: usize,
1383) -> String {
1384 wmma_cpasync_harness(tile, stages, m, k, n, false)
1385}
1386pub fn emit_wmma_cpasync_i8_bench_staged(
1387 tile: &WmmaTile,
1388 stages: usize,
1389 m: usize,
1390 k: usize,
1391 n: usize,
1392) -> String {
1393 wmma_cpasync_harness(tile, stages, m, k, n, true)
1394}
1395
1396pub fn emit_wmma_cpasync_program(
1399 kind: MmaKind,
1400 tile: &WmmaTile,
1401 stages: usize,
1402 m: usize,
1403 k: usize,
1404 n: usize,
1405) -> String {
1406 let kernel = wmma_cpasync_kernel_staged_for(tile, stages, kind);
1407 let op = kind.operand_ctype();
1408 let acc = kind.acc_ctype();
1409 let conv_a = kind.host_convert("(p % 7) - 3");
1410 let conv_b = kind.host_convert("(p % 5) - 2");
1411 format!(
1412 r#"{kernel}#include <cstdio>
1413#include <cstdlib>
1414#include <cmath>
1415#define M {m}
1416#define N {n}
1417#define K {k}
1418int main(void){{
1419 const int MM=M,KK=K,NN=N;
1420 {op}* hA=({op}*)malloc((size_t)MM*KK*sizeof({op}));
1421 {op}* hB=({op}*)malloc((size_t)KK*NN*sizeof({op}));
1422 for(long p=0;p<(long)MM*KK;p++) hA[p]={conv_a};
1423 for(long p=0;p<(long)KK*NN;p++) hB[p]={conv_b};
1424 {op} *dA,*dB; {acc}* dC;
1425 if(cudaMalloc((void**)&dA,(size_t)MM*KK*sizeof({op}))||cudaMalloc((void**)&dB,(size_t)KK*NN*sizeof({op}))||cudaMalloc((void**)&dC,(size_t)MM*NN*sizeof({acc}))){{ fprintf(stderr,"malloc\n"); return 1; }}
1426 cudaMemcpy(dA,hA,(size_t)MM*KK*sizeof({op}),cudaMemcpyHostToDevice);
1427 cudaMemcpy(dB,hB,(size_t)KK*NN*sizeof({op}),cudaMemcpyHostToDevice);
1428 dim3 grid(NN/{bn}, MM/{bm}); gemm_wmma_cpasync<<<grid, {threads}>>>(dA,dB,dC,MM,NN,KK);
1429 if(cudaDeviceSynchronize()!=cudaSuccess){{ fprintf(stderr,"sync\n"); return 1; }}
1430 {acc}* hC=({acc}*)malloc((size_t)MM*NN*sizeof({acc}));
1431 cudaMemcpy(hC,dC,(size_t)MM*NN*sizeof({acc}),cudaMemcpyDeviceToHost);
1432 double sum=0; for(long t=0;t<(long)MM*NN;t++) sum+=(double)hC[t];
1433 printf("%lld\n",(long long)llround(sum));
1434 return 0;
1435}}
1436"#,
1437 kernel = kernel,
1438 op = op,
1439 acc = acc,
1440 m = m,
1441 k = k,
1442 n = n,
1443 conv_a = conv_a,
1444 conv_b = conv_b,
1445 bm = tile.bm,
1446 bn = tile.bn,
1447 threads = tile.threads()
1448 )
1449}
1450
1451pub fn emit_wmma_cpasync_bench(
1453 kind: MmaKind,
1454 tile: &WmmaTile,
1455 stages: usize,
1456 m: usize,
1457 k: usize,
1458 n: usize,
1459) -> String {
1460 let kernel = wmma_cpasync_kernel_staged_for(tile, stages, kind);
1461 gemm_timed_main(
1462 &kernel,
1463 kind.operand_ctype(),
1464 kind.acc_ctype(),
1465 &format!("gemm_wmma_cpasync<<<grid, {}>>>", tile.threads()),
1466 &format!("dim3 grid(N/{}, M/{});", tile.bn, tile.bm),
1467 m,
1468 k,
1469 n,
1470 )
1471}
1472pub fn cpasync_stages_fit(tile: &WmmaTile, stages: usize, gpu: &GpuLimits) -> bool {
1474 tile.bk % 16 == 0
1475 && tile.bn % 16 == 0
1476 && (tile.bm * tile.bk) % 16 == 0
1477 && tile.bm % 16 == 0
1478 && tile.bn % 16 == 0
1479 && tile.wm() % 16 == 0
1480 && tile.wn() % 16 == 0
1481 && tile.warps_m > 0
1482 && tile.warps_n > 0
1483 && tile.bm % tile.warps_m == 0
1484 && tile.bn % tile.warps_n == 0
1485 && tile.threads() <= gpu.max_threads
1486 && stages * (tile.bm * tile.bk + tile.bk * tile.bn) <= gpu.smem_bytes
1487}
1488
1489#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1500pub enum GemmLayout {
1501 Nn,
1503 Nt,
1505 Tn,
1507 Tt,
1509}
1510
1511impl GemmLayout {
1512 pub fn is_tensor_core_native(&self) -> bool {
1514 matches!(self, GemmLayout::Tn)
1515 }
1516 pub fn all() -> [GemmLayout; 4] {
1518 [
1519 GemmLayout::Nn,
1520 GemmLayout::Nt,
1521 GemmLayout::Tn,
1522 GemmLayout::Tt,
1523 ]
1524 }
1525}
1526
1527pub fn select_layout(caller_is_tn: bool) -> GemmLayout {
1532 if caller_is_tn {
1533 GemmLayout::Tn
1534 } else {
1535 GemmLayout::Nn
1536 }
1537}
1538
1539fn mma_tn_kernel() -> &'static str {
1548 r#"#define BM 64
1549#define BN 64
1550#define BK 16
1551#define WARPS_M 2
1552#define WARPS_N 2
1553#define WM (BM / WARPS_M)
1554#define WN (BN / WARPS_N)
1555#define NTHREAD (WARPS_M * WARPS_N * 32)
1556#define PK4(p,st) (((p)[0]&0xff)|(((p)[st]&0xff)<<8)|(((p)[2*(st)]&0xff)<<16)|(((p)[3*(st)]&0xff)<<24))
1557// C(MxN)=A(MxK).B(KxN), int8, int32 accumulate, via the native mma.sync.aligned.m16n8k16.row.col.s32.s8.s8.s32.
1558extern "C" __global__ void gemm_mma_tn(const signed char* A, const signed char* B, int* C, int M, int N, int K) {
1559 __shared__ signed char As[BM * BK];
1560 __shared__ signed char Bs[BK * BN];
1561 const int tid = threadIdx.x, warpId = tid / 32, lane = tid % 32;
1562 const int gid = lane / 4, tg = lane % 4; // groupID (0..7), thread-in-group (0..3)
1563 const int warp_row = (warpId / WARPS_N) * WM, warp_col = (warpId % WARPS_N) * WN;
1564 const int block_row = blockIdx.y * BM, block_col = blockIdx.x * BN;
1565
1566 int c[WM / 16][WN / 8][4];
1567 #pragma unroll
1568 for (int mi = 0; mi < WM / 16; ++mi)
1569 #pragma unroll
1570 for (int ni = 0; ni < WN / 8; ++ni) { c[mi][ni][0]=c[mi][ni][1]=c[mi][ni][2]=c[mi][ni][3]=0; }
1571
1572 for (int k0 = 0; k0 < K; k0 += BK) {
1573 for (int idx = tid; idx < BM * BK; idx += NTHREAD) { int r = idx / BK, cc = idx % BK; As[idx] = A[(block_row + r) * K + k0 + cc]; }
1574 for (int idx = tid; idx < BK * BN; idx += NTHREAD) { int r = idx / BN, cc = idx % BN; Bs[idx] = B[(k0 + r) * N + block_col + cc]; }
1575 __syncthreads();
1576
1577 #pragma unroll
1578 for (int kk = 0; kk < BK; kk += 16) {
1579 #pragma unroll
1580 for (int mi = 0; mi < WM / 16; ++mi) {
1581 signed char* Ap0 = &As[(warp_row + mi * 16 + gid) * BK + kk + tg * 4];
1582 signed char* Ap1 = &As[(warp_row + mi * 16 + gid + 8) * BK + kk + tg * 4];
1583 int a0 = PK4(Ap0, 1), a1 = PK4(Ap1, 1);
1584 #pragma unroll
1585 for (int ni = 0; ni < WN / 8; ++ni) {
1586 signed char* Bp = &Bs[(kk + tg * 4) * BN + (warp_col + ni * 8 + gid)];
1587 int b0 = PK4(Bp, BN);
1588 asm volatile(
1589 "mma.sync.aligned.m16n8k16.row.col.s32.s8.s8.s32 {%0,%1,%2,%3},{%4,%5},{%6},{%0,%1,%2,%3};"
1590 : "+r"(c[mi][ni][0]), "+r"(c[mi][ni][1]), "+r"(c[mi][ni][2]), "+r"(c[mi][ni][3])
1591 : "r"(a0), "r"(a1), "r"(b0));
1592 }
1593 }
1594 }
1595 __syncthreads();
1596 }
1597
1598 #pragma unroll
1599 for (int mi = 0; mi < WM / 16; ++mi)
1600 #pragma unroll
1601 for (int ni = 0; ni < WN / 8; ++ni) {
1602 int rb = block_row + warp_row + mi * 16, cb = block_col + warp_col + ni * 8;
1603 C[(rb + gid) * N + cb + tg * 2] = c[mi][ni][0];
1604 C[(rb + gid) * N + cb + tg * 2 + 1] = c[mi][ni][1];
1605 C[(rb + gid + 8) * N + cb + tg * 2] = c[mi][ni][2];
1606 C[(rb + gid + 8) * N + cb + tg * 2 + 1] = c[mi][ni][3];
1607 }
1608}
1609"#
1610}
1611
1612pub fn emit_mma_tn_i8() -> String {
1613 mma_tn_kernel().to_string()
1614}
1615
1616pub fn emit_mma_tn_i8_program(m: usize, k: usize, n: usize) -> String {
1617 format!(
1618 r#"{kernel}
1619#include <cstdio>
1620#include <cstdlib>
1621int main() {{
1622 const int M={m}, K={k}, N={n};
1623 signed char *hA=(signed char*)malloc((size_t)M*K), *hB=(signed char*)malloc((size_t)K*N);
1624 for (long i=0;i<(long)M*K;++i) hA[i]=(signed char)(((i*7+3)%251)-125);
1625 for (long i=0;i<(long)K*N;++i) hB[i]=(signed char)(((i*5+1)%251)-125);
1626 signed char *dA,*dB; int *dC,*hC=(int*)malloc((size_t)M*N*4);
1627 cudaMalloc(&dA,(size_t)M*K); cudaMalloc(&dB,(size_t)K*N); cudaMalloc(&dC,(size_t)M*N*4);
1628 cudaMemcpy(dA,hA,(size_t)M*K,cudaMemcpyHostToDevice); cudaMemcpy(dB,hB,(size_t)K*N,cudaMemcpyHostToDevice);
1629 dim3 grid(N/64, M/64); gemm_mma_tn<<<grid,128>>>(dA,dB,dC,M,N,K); cudaDeviceSynchronize();
1630 cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
1631 for (long i=0;i<(long)M*N;++i) printf("%d\n", hC[i]);
1632 return 0;
1633}}
1634"#,
1635 kernel = mma_tn_kernel(),
1636 m = m,
1637 k = k,
1638 n = n
1639 )
1640}
1641
1642pub fn emit_mma_tn_i8_bench(m: usize, k: usize, n: usize) -> String {
1643 format!(
1644 r#"{kernel}
1645#include <cstdio>
1646#include <cstdlib>
1647int main() {{
1648 const int M={m}, K={k}, N={n};
1649 signed char *dA,*dB; int *dC;
1650 cudaMalloc(&dA,(size_t)M*K); cudaMalloc(&dB,(size_t)K*N); cudaMalloc(&dC,(size_t)M*N*4);
1651 cudaMemset(dA,1,(size_t)M*K); cudaMemset(dB,1,(size_t)K*N);
1652 dim3 grid(N/64,M/64);
1653 gemm_mma_tn<<<grid,128>>>(dA,dB,dC,M,N,K);
1654 cudaError_t err=cudaDeviceSynchronize(); if(err){{printf("LAUNCH_ERROR %s\n",cudaGetErrorString(err));return 1;}}
1655 int *hC=(int*)malloc((size_t)M*N*4); cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
1656 long step=((long)M*N)/4096+1; for(long i=0;i<(long)M*N;i+=step) if(hC[i]!=K){{printf("INCORRECT at %ld: %d!=%d\n",i,hC[i],K);return 1;}}
1657 cudaEvent_t s,e; cudaEventCreate(&s); cudaEventCreate(&e); int IT=30; float total=0;
1658 for(int it=0;it<IT;it++){{ cudaEventRecord(s); gemm_mma_tn<<<grid,128>>>(dA,dB,dC,M,N,K); cudaEventRecord(e); cudaEventSynchronize(e); float ms=0; cudaEventElapsedTime(&ms,s,e); total+=ms; }}
1659 printf("GOPS %.1f\n", 2.0*M*N*K/((total/IT)/1e3)/1e9);
1660 return 0;
1661}}
1662"#,
1663 kernel = mma_tn_kernel(),
1664 m = m,
1665 k = k,
1666 n = n
1667 )
1668}
1669
1670fn mma_tn_cpasync_kernel() -> &'static str {
1676 r#"#include <cuda_pipeline.h>
1677#define BM 128
1678#define BN 64
1679#define BK 32
1680#define WARPS_M 2
1681#define WARPS_N 2
1682#define WM (BM / WARPS_M)
1683#define WN (BN / WARPS_N)
1684#define NTHREAD (WARPS_M * WARPS_N * 32)
1685#define PK4(p,st) (((p)[0]&0xff)|(((p)[st]&0xff)<<8)|(((p)[2*(st)]&0xff)<<16)|(((p)[3*(st)]&0xff)<<24))
1686extern "C" __global__ void gemm_mma_tn_cpasync(const signed char* A, const signed char* B, int* C, int M, int N, int K) {
1687 __shared__ signed char As[2][BM * BK];
1688 __shared__ signed char Bs[2][BK * BN];
1689 const int tid = threadIdx.x, warpId = tid / 32, lane = tid % 32;
1690 const int gid = lane / 4, tg = lane % 4;
1691 const int warp_row = (warpId / WARPS_N) * WM, warp_col = (warpId % WARPS_N) * WN;
1692 const int block_row = blockIdx.y * BM, block_col = blockIdx.x * BN;
1693 const int nstages = K / BK;
1694
1695 int c[WM / 16][WN / 8][4];
1696 #pragma unroll
1697 for (int mi = 0; mi < WM / 16; ++mi)
1698 #pragma unroll
1699 for (int ni = 0; ni < WN / 8; ++ni) { c[mi][ni][0]=c[mi][ni][1]=c[mi][ni][2]=c[mi][ni][3]=0; }
1700
1701 #define LOAD_STAGE(buf, st) do { int _k0 = (st) * BK; \
1702 for (int idx = tid; idx < (BM * BK) / 16; idx += NTHREAD) { int r=(idx*16)/BK, cc=(idx*16)%BK; \
1703 __pipeline_memcpy_async(&As[buf][r*BK+cc], &A[(block_row+r)*K + _k0+cc], 16); } \
1704 for (int idx = tid; idx < (BK * BN) / 16; idx += NTHREAD) { int r=(idx*16)/BN, cc=(idx*16)%BN; \
1705 __pipeline_memcpy_async(&Bs[buf][r*BN+cc], &B[(_k0+r)*N + block_col+cc], 16); } \
1706 __pipeline_commit(); } while (0)
1707
1708 LOAD_STAGE(0, 0);
1709 for (int s = 0; s < nstages; ++s) {
1710 if (s + 1 < nstages) LOAD_STAGE((s + 1) & 1, s + 1);
1711 __pipeline_wait_prior(s + 1 < nstages ? 1 : 0);
1712 __syncthreads();
1713 int buf = s & 1;
1714 #pragma unroll
1715 for (int kk = 0; kk < BK; kk += 16) {
1716 #pragma unroll
1717 for (int mi = 0; mi < WM / 16; ++mi) {
1718 signed char* Ap0 = &As[buf][(warp_row + mi*16 + gid) * BK + kk + tg*4];
1719 signed char* Ap1 = &As[buf][(warp_row + mi*16 + gid + 8) * BK + kk + tg*4];
1720 int a0 = PK4(Ap0, 1), a1 = PK4(Ap1, 1);
1721 #pragma unroll
1722 for (int ni = 0; ni < WN / 8; ++ni) {
1723 signed char* Bp = &Bs[buf][(kk + tg*4) * BN + (warp_col + ni*8 + gid)];
1724 int b0 = PK4(Bp, BN);
1725 asm volatile("mma.sync.aligned.m16n8k16.row.col.s32.s8.s8.s32 {%0,%1,%2,%3},{%4,%5},{%6},{%0,%1,%2,%3};"
1726 : "+r"(c[mi][ni][0]),"+r"(c[mi][ni][1]),"+r"(c[mi][ni][2]),"+r"(c[mi][ni][3])
1727 : "r"(a0),"r"(a1),"r"(b0));
1728 }
1729 }
1730 }
1731 __syncthreads();
1732 }
1733 #pragma unroll
1734 for (int mi = 0; mi < WM / 16; ++mi)
1735 #pragma unroll
1736 for (int ni = 0; ni < WN / 8; ++ni) {
1737 int rb = block_row + warp_row + mi*16, cb = block_col + warp_col + ni*8;
1738 C[(rb+gid)*N + cb+tg*2] = c[mi][ni][0];
1739 C[(rb+gid)*N + cb+tg*2+1] = c[mi][ni][1];
1740 C[(rb+gid+8)*N + cb+tg*2] = c[mi][ni][2];
1741 C[(rb+gid+8)*N + cb+tg*2+1] = c[mi][ni][3];
1742 }
1743}
1744"#
1745}
1746
1747pub fn emit_mma_tn_cpasync_program(m: usize, k: usize, n: usize) -> String {
1748 format!(
1749 r#"{kernel}
1750#include <cstdio>
1751#include <cstdlib>
1752int main() {{ const int M={m},K={k},N={n};
1753 signed char *hA=(signed char*)malloc((size_t)M*K),*hB=(signed char*)malloc((size_t)K*N);
1754 for(long i=0;i<(long)M*K;++i) hA[i]=(signed char)(((i*7+3)%251)-125);
1755 for(long i=0;i<(long)K*N;++i) hB[i]=(signed char)(((i*5+1)%251)-125);
1756 signed char *dA,*dB; int *dC,*hC=(int*)malloc((size_t)M*N*4);
1757 cudaMalloc(&dA,(size_t)M*K);cudaMalloc(&dB,(size_t)K*N);cudaMalloc(&dC,(size_t)M*N*4);
1758 cudaMemcpy(dA,hA,(size_t)M*K,cudaMemcpyHostToDevice);cudaMemcpy(dB,hB,(size_t)K*N,cudaMemcpyHostToDevice);
1759 dim3 grid(N/64,M/128); gemm_mma_tn_cpasync<<<grid,128>>>(dA,dB,dC,M,N,K); cudaDeviceSynchronize();
1760 cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
1761 for(long i=0;i<(long)M*N;++i) printf("%d\n",hC[i]); return 0; }}
1762"#,
1763 kernel = mma_tn_cpasync_kernel(),
1764 m = m,
1765 k = k,
1766 n = n
1767 )
1768}
1769
1770pub fn emit_mma_tn_cpasync_bench(m: usize, k: usize, n: usize) -> String {
1771 format!(
1772 r#"{kernel}
1773#include <cstdio>
1774#include <cstdlib>
1775int main() {{ const int M={m},K={k},N={n};
1776 signed char *dA,*dB; int *dC;
1777 cudaMalloc(&dA,(size_t)M*K);cudaMalloc(&dB,(size_t)K*N);cudaMalloc(&dC,(size_t)M*N*4);
1778 cudaMemset(dA,1,(size_t)M*K);cudaMemset(dB,1,(size_t)K*N);
1779 dim3 grid(N/64,M/128);
1780 gemm_mma_tn_cpasync<<<grid,128>>>(dA,dB,dC,M,N,K);
1781 cudaError_t err=cudaDeviceSynchronize(); if(err){{printf("LAUNCH_ERROR %s\n",cudaGetErrorString(err));return 1;}}
1782 int *hC=(int*)malloc((size_t)M*N*4); cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
1783 long step=((long)M*N)/4096+1; for(long i=0;i<(long)M*N;i+=step) if(hC[i]!=K){{printf("INCORRECT at %ld: %d!=%d\n",i,hC[i],K);return 1;}}
1784 cudaEvent_t s,e; cudaEventCreate(&s); cudaEventCreate(&e); int IT=30; float total=0;
1785 for(int it=0;it<IT;it++){{ cudaEventRecord(s); gemm_mma_tn_cpasync<<<grid,128>>>(dA,dB,dC,M,N,K); cudaEventRecord(e); cudaEventSynchronize(e); float ms=0; cudaEventElapsedTime(&ms,s,e); total+=ms; }}
1786 printf("GOPS %.1f\n", 2.0*M*N*K/((total/IT)/1e3)/1e9); return 0; }}
1787"#,
1788 kernel = mma_tn_cpasync_kernel(),
1789 m = m,
1790 k = k,
1791 n = n
1792 )
1793}
1794
1795fn mma_tn_colb_kernel() -> &'static str {
1802 r#"#include <cuda_pipeline.h>
1803#define BM 128
1804#define BN 64
1805#define BK 32
1806#define WARPS_M 2
1807#define WARPS_N 2
1808#define WM (BM / WARPS_M)
1809#define WN (BN / WARPS_N)
1810#define NTHREAD (WARPS_M * WARPS_N * 32)
1811#define PK4(p,st) (((p)[0]&0xff)|(((p)[st]&0xff)<<8)|(((p)[2*(st)]&0xff)<<16)|(((p)[3*(st)]&0xff)<<24))
1812// C(MxN) = A(MxK, row-major) . B(KxN), with B stored COL-MAJOR: Bcol[n*K + k]. Both operands K-contiguous.
1813extern "C" __global__ void gemm_mma_tn_colb(const signed char* A, const signed char* Bcol, int* C, int M, int N, int K) {
1814 __shared__ signed char As[2][BM * BK]; // [m][k], k contiguous
1815 __shared__ signed char Bs[2][BN * BK]; // [n][k], k contiguous (col-major tile)
1816 const int tid = threadIdx.x, warpId = tid / 32, lane = tid % 32;
1817 const int gid = lane / 4, tg = lane % 4;
1818 const int warp_row = (warpId / WARPS_N) * WM, warp_col = (warpId % WARPS_N) * WN;
1819 const int block_row = blockIdx.y * BM, block_col = blockIdx.x * BN;
1820 const int nstages = K / BK;
1821
1822 int c[WM / 16][WN / 8][4];
1823 #pragma unroll
1824 for (int mi = 0; mi < WM / 16; ++mi)
1825 #pragma unroll
1826 for (int ni = 0; ni < WN / 8; ++ni) { c[mi][ni][0]=c[mi][ni][1]=c[mi][ni][2]=c[mi][ni][3]=0; }
1827
1828 #define LOAD_STAGE(buf, st) do { int _k0 = (st) * BK; \
1829 for (int idx = tid; idx < (BM * BK) / 16; idx += NTHREAD) { int r=(idx*16)/BK, cc=(idx*16)%BK; \
1830 __pipeline_memcpy_async(&As[buf][r*BK+cc], &A[(block_row+r)*K + _k0+cc], 16); } \
1831 for (int idx = tid; idx < (BN * BK) / 16; idx += NTHREAD) { int r=(idx*16)/BK, cc=(idx*16)%BK; \
1832 __pipeline_memcpy_async(&Bs[buf][r*BK+cc], &Bcol[(block_col+r)*K + _k0+cc], 16); } \
1833 __pipeline_commit(); } while (0)
1834
1835 LOAD_STAGE(0, 0);
1836 for (int s = 0; s < nstages; ++s) {
1837 if (s + 1 < nstages) LOAD_STAGE((s + 1) & 1, s + 1);
1838 __pipeline_wait_prior(s + 1 < nstages ? 1 : 0);
1839 __syncthreads();
1840 int buf = s & 1;
1841 #pragma unroll
1842 for (int kk = 0; kk < BK; kk += 16) {
1843 #pragma unroll
1844 for (int mi = 0; mi < WM / 16; ++mi) {
1845 signed char* Ap0 = &As[buf][(warp_row + mi*16 + gid) * BK + kk + tg*4];
1846 signed char* Ap1 = &As[buf][(warp_row + mi*16 + gid + 8) * BK + kk + tg*4];
1847 int a0 = PK4(Ap0, 1), a1 = PK4(Ap1, 1);
1848 #pragma unroll
1849 for (int ni = 0; ni < WN / 8; ++ni) {
1850 // B fragment: col=warp_col+ni*8+gid, k=kk+tg*4..+3 — now CONTIGUOUS (stride 1).
1851 signed char* Bp = &Bs[buf][(warp_col + ni*8 + gid) * BK + kk + tg*4];
1852 int b0 = PK4(Bp, 1);
1853 asm volatile("mma.sync.aligned.m16n8k16.row.col.s32.s8.s8.s32 {%0,%1,%2,%3},{%4,%5},{%6},{%0,%1,%2,%3};"
1854 : "+r"(c[mi][ni][0]),"+r"(c[mi][ni][1]),"+r"(c[mi][ni][2]),"+r"(c[mi][ni][3])
1855 : "r"(a0),"r"(a1),"r"(b0));
1856 }
1857 }
1858 }
1859 __syncthreads();
1860 }
1861 #pragma unroll
1862 for (int mi = 0; mi < WM / 16; ++mi)
1863 #pragma unroll
1864 for (int ni = 0; ni < WN / 8; ++ni) {
1865 int rb = block_row + warp_row + mi*16, cb = block_col + warp_col + ni*8;
1866 C[(rb+gid)*N + cb+tg*2] = c[mi][ni][0];
1867 C[(rb+gid)*N + cb+tg*2+1] = c[mi][ni][1];
1868 C[(rb+gid+8)*N + cb+tg*2] = c[mi][ni][2];
1869 C[(rb+gid+8)*N + cb+tg*2+1] = c[mi][ni][3];
1870 }
1871}
1872"#
1873}
1874
1875pub fn emit_mma_tn_colb_program(m: usize, k: usize, n: usize) -> String {
1876 format!(
1877 r#"{kernel}
1878#include <cstdio>
1879#include <cstdlib>
1880int main() {{ const int M={m},K={k},N={n};
1881 signed char *hA=(signed char*)malloc((size_t)M*K),*hB=(signed char*)malloc((size_t)K*N);
1882 for(long i=0;i<(long)M*K;++i) hA[i]=(signed char)(((i*7+3)%251)-125);
1883 // B stored COL-MAJOR: hB[n*K + k] holds B[k][n]; fill so oracle C[i][j]=sum_k A[i][k]*B[k][j].
1884 for(long n=0;n<N;++n) for(long kk=0;kk<K;++kk){{ long bkn=(long)kk*N+n; hB[n*K+kk]=(signed char)(((bkn*5+1)%251)-125); }}
1885 signed char *dA,*dB; int *dC,*hC=(int*)malloc((size_t)M*N*4);
1886 cudaMalloc(&dA,(size_t)M*K);cudaMalloc(&dB,(size_t)K*N);cudaMalloc(&dC,(size_t)M*N*4);
1887 cudaMemcpy(dA,hA,(size_t)M*K,cudaMemcpyHostToDevice);cudaMemcpy(dB,hB,(size_t)K*N,cudaMemcpyHostToDevice);
1888 dim3 grid(N/64,M/128); gemm_mma_tn_colb<<<grid,128>>>(dA,dB,dC,M,N,K); cudaDeviceSynchronize();
1889 cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
1890 for(long i=0;i<(long)M*N;++i) printf("%d\n",hC[i]); return 0; }}
1891"#,
1892 kernel = mma_tn_colb_kernel(),
1893 m = m,
1894 k = k,
1895 n = n
1896 )
1897}
1898
1899pub fn emit_mma_tn_colb_bench(m: usize, k: usize, n: usize) -> String {
1900 format!(
1901 r#"{kernel}
1902#include <cstdio>
1903#include <cstdlib>
1904int main() {{ const int M={m},K={k},N={n};
1905 signed char *dA,*dB; int *dC;
1906 cudaMalloc(&dA,(size_t)M*K);cudaMalloc(&dB,(size_t)K*N);cudaMalloc(&dC,(size_t)M*N*4);
1907 cudaMemset(dA,1,(size_t)M*K);cudaMemset(dB,1,(size_t)K*N);
1908 dim3 grid(N/64,M/128);
1909 gemm_mma_tn_colb<<<grid,128>>>(dA,dB,dC,M,N,K);
1910 cudaError_t err=cudaDeviceSynchronize(); if(err){{printf("LAUNCH_ERROR %s\n",cudaGetErrorString(err));return 1;}}
1911 int *hC=(int*)malloc((size_t)M*N*4); cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
1912 long step=((long)M*N)/4096+1; for(long i=0;i<(long)M*N;i+=step) if(hC[i]!=K){{printf("INCORRECT at %ld: %d!=%d\n",i,hC[i],K);return 1;}}
1913 cudaEvent_t s,e; cudaEventCreate(&s); cudaEventCreate(&e); int IT=30; float total=0;
1914 for(int it=0;it<IT;it++){{ cudaEventRecord(s); gemm_mma_tn_colb<<<grid,128>>>(dA,dB,dC,M,N,K); cudaEventRecord(e); cudaEventSynchronize(e); float ms=0; cudaEventElapsedTime(&ms,s,e); total+=ms; }}
1915 printf("GOPS %.1f\n", 2.0*M*N*K/((total/IT)/1e3)/1e9); return 0; }}
1916"#,
1917 kernel = mma_tn_colb_kernel(),
1918 m = m,
1919 k = k,
1920 n = n
1921 )
1922}
1923
1924fn mma_tn_ldmatrix_kernel() -> &'static str {
1931 r#"#include <cuda_pipeline.h>
1932#define BM 128
1933#define BN 64
1934#define BK 32
1935#define WARPS_M 2
1936#define WARPS_N 2
1937#define WM (BM / WARPS_M)
1938#define WN (BN / WARPS_N)
1939#define NTHREAD (WARPS_M * WARPS_N * 32)
1940// C(MxN)=A(MxK,row-major).B(KxN, stored COL-MAJOR: Bcol[n*K+k]). ldmatrix feeds mma.sync.aligned.m16n8k16.s8.
1941extern "C" __global__ void gemm_mma_tn_ldm(const signed char* A, const signed char* Bcol, int* C, int M, int N, int K) {
1942 __shared__ signed char As[2][BM * BK]; // [m][k]
1943 __shared__ signed char Bs[2][BN * BK]; // [n][k] (col-major tile)
1944 const int tid = threadIdx.x, warpId = tid / 32, lane = tid % 32;
1945 const int gid = lane / 4, tg = lane % 4;
1946 const int warp_row = (warpId / WARPS_N) * WM, warp_col = (warpId % WARPS_N) * WN;
1947 const int block_row = blockIdx.y * BM, block_col = blockIdx.x * BN;
1948 const int nstages = K / BK;
1949
1950 int c[WM / 16][WN / 8][4];
1951 #pragma unroll
1952 for (int mi = 0; mi < WM / 16; ++mi)
1953 #pragma unroll
1954 for (int ni = 0; ni < WN / 8; ++ni) { c[mi][ni][0]=c[mi][ni][1]=c[mi][ni][2]=c[mi][ni][3]=0; }
1955
1956 #define LOAD_STAGE(buf, st) do { int _k0 = (st) * BK; \
1957 for (int idx = tid; idx < (BM * BK) / 16; idx += NTHREAD) { int r=(idx*16)/BK, cc=(idx*16)%BK; \
1958 __pipeline_memcpy_async(&As[buf][r*BK+cc], &A[(block_row+r)*K + _k0+cc], 16); } \
1959 for (int idx = tid; idx < (BN * BK) / 16; idx += NTHREAD) { int r=(idx*16)/BK, cc=(idx*16)%BK; \
1960 __pipeline_memcpy_async(&Bs[buf][r*BK+cc], &Bcol[(block_col+r)*K + _k0+cc], 16); } \
1961 __pipeline_commit(); } while (0)
1962
1963 LOAD_STAGE(0, 0);
1964 for (int s = 0; s < nstages; ++s) {
1965 if (s + 1 < nstages) LOAD_STAGE((s + 1) & 1, s + 1);
1966 __pipeline_wait_prior(s + 1 < nstages ? 1 : 0);
1967 __syncthreads();
1968 int buf = s & 1;
1969 #pragma unroll
1970 for (int kk = 0; kk < BK; kk += 16) {
1971 int a[WM / 16][2], b[WN / 8];
1972 #pragma unroll
1973 for (int mi = 0; mi < WM / 16; ++mi) {
1974 unsigned aAddr = (unsigned)__cvta_generic_to_shared(&As[buf][(warp_row + mi*16 + (lane & 15)) * BK + kk]);
1975 asm volatile("ldmatrix.sync.aligned.m8n8.x2.shared.b16 {%0,%1},[%2];" : "=r"(a[mi][0]), "=r"(a[mi][1]) : "r"(aAddr));
1976 }
1977 #pragma unroll
1978 for (int ni = 0; ni < WN / 8; ++ni) {
1979 unsigned bAddr = (unsigned)__cvta_generic_to_shared(&Bs[buf][(warp_col + ni*8 + (lane & 7)) * BK + kk]);
1980 asm volatile("ldmatrix.sync.aligned.m8n8.x1.shared.b16 {%0},[%1];" : "=r"(b[ni]) : "r"(bAddr));
1981 }
1982 #pragma unroll
1983 for (int mi = 0; mi < WM / 16; ++mi)
1984 #pragma unroll
1985 for (int ni = 0; ni < WN / 8; ++ni)
1986 asm volatile("mma.sync.aligned.m16n8k16.row.col.s32.s8.s8.s32 {%0,%1,%2,%3},{%4,%5},{%6},{%0,%1,%2,%3};"
1987 : "+r"(c[mi][ni][0]),"+r"(c[mi][ni][1]),"+r"(c[mi][ni][2]),"+r"(c[mi][ni][3])
1988 : "r"(a[mi][0]),"r"(a[mi][1]),"r"(b[ni]));
1989 }
1990 __syncthreads();
1991 }
1992 #pragma unroll
1993 for (int mi = 0; mi < WM / 16; ++mi)
1994 #pragma unroll
1995 for (int ni = 0; ni < WN / 8; ++ni) {
1996 int rb = block_row + warp_row + mi*16, cb = block_col + warp_col + ni*8;
1997 C[(rb+gid)*N + cb+tg*2] = c[mi][ni][0];
1998 C[(rb+gid)*N + cb+tg*2+1] = c[mi][ni][1];
1999 C[(rb+gid+8)*N + cb+tg*2] = c[mi][ni][2];
2000 C[(rb+gid+8)*N + cb+tg*2+1] = c[mi][ni][3];
2001 }
2002}
2003"#
2004}
2005
2006pub fn emit_mma_tn_ldmatrix_program(m: usize, k: usize, n: usize) -> String {
2007 format!(
2008 r#"{kernel}
2009#include <cstdio>
2010#include <cstdlib>
2011int main() {{ const int M={m},K={k},N={n};
2012 signed char *hA=(signed char*)malloc((size_t)M*K),*hB=(signed char*)malloc((size_t)K*N);
2013 for(long i=0;i<(long)M*K;++i) hA[i]=(signed char)(((i*7+3)%251)-125);
2014 for(long n=0;n<N;++n) for(long kk=0;kk<K;++kk){{ long bkn=(long)kk*N+n; hB[n*K+kk]=(signed char)(((bkn*5+1)%251)-125); }}
2015 signed char *dA,*dB; int *dC,*hC=(int*)malloc((size_t)M*N*4);
2016 cudaMalloc(&dA,(size_t)M*K);cudaMalloc(&dB,(size_t)K*N);cudaMalloc(&dC,(size_t)M*N*4);
2017 cudaMemcpy(dA,hA,(size_t)M*K,cudaMemcpyHostToDevice);cudaMemcpy(dB,hB,(size_t)K*N,cudaMemcpyHostToDevice);
2018 dim3 grid(N/64,M/128); gemm_mma_tn_ldm<<<grid,128>>>(dA,dB,dC,M,N,K);
2019 cudaError_t e=cudaDeviceSynchronize(); if(e){{printf("ERR %s\n",cudaGetErrorString(e));return 1;}}
2020 cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
2021 for(long i=0;i<(long)M*N;++i) printf("%d\n",hC[i]); return 0; }}
2022"#,
2023 kernel = mma_tn_ldmatrix_kernel(),
2024 m = m,
2025 k = k,
2026 n = n
2027 )
2028}
2029
2030pub fn emit_mma_tn_ldmatrix_bench(m: usize, k: usize, n: usize) -> String {
2031 format!(
2032 r#"{kernel}
2033#include <cstdio>
2034#include <cstdlib>
2035int main() {{ const int M={m},K={k},N={n};
2036 signed char *dA,*dB; int *dC;
2037 cudaMalloc(&dA,(size_t)M*K);cudaMalloc(&dB,(size_t)K*N);cudaMalloc(&dC,(size_t)M*N*4);
2038 cudaMemset(dA,1,(size_t)M*K);cudaMemset(dB,1,(size_t)K*N);
2039 dim3 grid(N/64,M/128);
2040 gemm_mma_tn_ldm<<<grid,128>>>(dA,dB,dC,M,N,K);
2041 cudaError_t err=cudaDeviceSynchronize(); if(err){{printf("LAUNCH_ERROR %s\n",cudaGetErrorString(err));return 1;}}
2042 int *hC=(int*)malloc((size_t)M*N*4); cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
2043 long step=((long)M*N)/4096+1; for(long i=0;i<(long)M*N;i+=step) if(hC[i]!=K){{printf("INCORRECT at %ld: %d!=%d\n",i,hC[i],K);return 1;}}
2044 cudaEvent_t s,e; cudaEventCreate(&s); cudaEventCreate(&e); int IT=30; float total=0;
2045 for(int it=0;it<IT;it++){{ cudaEventRecord(s); gemm_mma_tn_ldm<<<grid,128>>>(dA,dB,dC,M,N,K); cudaEventRecord(e); cudaEventSynchronize(e); float ms=0; cudaEventElapsedTime(&ms,s,e); total+=ms; }}
2046 printf("GOPS %.1f\n", 2.0*M*N*K/((total/IT)/1e3)/1e9); return 0; }}
2047"#,
2048 kernel = mma_tn_ldmatrix_kernel(),
2049 m = m,
2050 k = k,
2051 n = n
2052 )
2053}
2054
2055pub fn emit_mma_tn_ldm_cfg_bench(
2059 bm: usize,
2060 bn: usize,
2061 bk: usize,
2062 warps_m: usize,
2063 warps_n: usize,
2064 stages: usize,
2065 m: usize,
2066 k: usize,
2067 n: usize,
2068) -> String {
2069 let defs = format!(
2070 "#define BM {bm}\n#define BN {bn}\n#define BK {bk}\n#define WARPS_M {warps_m}\n#define WARPS_N {warps_n}\n#define STAGES {stages}\n"
2071 );
2072 let kernel = format!("{defs}{}", MMA_TN_LDM_CFG_BODY);
2073 format!(
2074 r#"{kernel}
2075#include <cstdio>
2076#include <cstdlib>
2077int main() {{ const int M={m},K={k},N={n};
2078 signed char *dA,*dB; int *dC;
2079 cudaMalloc(&dA,(size_t)M*K);cudaMalloc(&dB,(size_t)K*N);cudaMalloc(&dC,(size_t)M*N*4);
2080 cudaMemset(dA,1,(size_t)M*K);cudaMemset(dB,1,(size_t)K*N);
2081 dim3 grid(N/{bn}, M/{bm});
2082 gemm_mma_tn_ldm<<<grid,{threads}>>>(dA,dB,dC,M,N,K);
2083 cudaError_t err=cudaDeviceSynchronize(); if(err){{printf("LAUNCH_ERROR %s\n",cudaGetErrorString(err));return 1;}}
2084 int *hC=(int*)malloc((size_t)M*N*4); cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
2085 long step=((long)M*N)/4096+1; for(long i=0;i<(long)M*N;i+=step) if(hC[i]!=K){{printf("INCORRECT %ld: %d!=%d\n",i,hC[i],K);return 1;}}
2086 cudaEvent_t s,e; cudaEventCreate(&s); cudaEventCreate(&e); int IT=30; float total=0;
2087 for(int it=0;it<IT;it++){{ cudaEventRecord(s); gemm_mma_tn_ldm<<<grid,{threads}>>>(dA,dB,dC,M,N,K); cudaEventRecord(e); cudaEventSynchronize(e); float ms=0; cudaEventElapsedTime(&ms,s,e); total+=ms; }}
2088 printf("GOPS %.1f\n", 2.0*M*N*K/((total/IT)/1e3)/1e9); return 0; }}
2089"#,
2090 kernel = kernel,
2091 m = m,
2092 k = k,
2093 n = n,
2094 bm = bm,
2095 bn = bn,
2096 threads = warps_m * warps_n * 32
2097 )
2098}
2099
2100const MMA_TN_LDM_CFG_BODY: &str = r#"#include <cuda_pipeline.h>
2101#define WM (BM / WARPS_M)
2102#define WN (BN / WARPS_N)
2103#define NTHREAD (WARPS_M * WARPS_N * 32)
2104extern "C" __global__ void gemm_mma_tn_ldm(const signed char* A, const signed char* Bcol, int* C, int M, int N, int K) {
2105 __shared__ signed char As[STAGES][BM * BK];
2106 __shared__ signed char Bs[STAGES][BN * BK];
2107 const int tid = threadIdx.x, warpId = tid / 32, lane = tid % 32;
2108 const int gid = lane / 4, tg = lane % 4;
2109 const int warp_row = (warpId / WARPS_N) * WM, warp_col = (warpId % WARPS_N) * WN;
2110 const int block_row = blockIdx.y * BM, block_col = blockIdx.x * BN;
2111 const int nstages = K / BK;
2112 int c[WM / 16][WN / 8][4];
2113 #pragma unroll
2114 for (int mi=0; mi<WM/16; ++mi)
2115 #pragma unroll
2116 for (int ni=0; ni<WN/8; ++ni) { c[mi][ni][0]=c[mi][ni][1]=c[mi][ni][2]=c[mi][ni][3]=0; }
2117 #define LOAD_STAGE(buf, st) do { int _k0=(st)*BK; \
2118 for (int idx=tid; idx<(BM*BK)/16; idx+=NTHREAD){ int r=(idx*16)/BK, cc=(idx*16)%BK; \
2119 __pipeline_memcpy_async(&As[buf][r*BK+cc], &A[(block_row+r)*K + _k0+cc], 16); } \
2120 for (int idx=tid; idx<(BN*BK)/16; idx+=NTHREAD){ int r=(idx*16)/BK, cc=(idx*16)%BK; \
2121 __pipeline_memcpy_async(&Bs[buf][r*BK+cc], &Bcol[(block_col+r)*K + _k0+cc], 16); } \
2122 __pipeline_commit(); } while(0)
2123 #pragma unroll
2124 for (int i=0;i<STAGES-1;++i){ if(i<nstages) LOAD_STAGE(i,i); else __pipeline_commit(); }
2125 for (int s=0; s<nstages; ++s) {
2126 __pipeline_wait_prior(STAGES-2);
2127 __syncthreads();
2128 int buf = s % STAGES;
2129 #pragma unroll
2130 for (int kk=0; kk<BK; kk+=16) {
2131 int a[WM/16][2], b[WN/8];
2132 #pragma unroll
2133 for (int mi=0; mi<WM/16; ++mi){
2134 unsigned aA=(unsigned)__cvta_generic_to_shared(&As[buf][(warp_row+mi*16+(lane&15))*BK+kk]);
2135 asm volatile("ldmatrix.sync.aligned.m8n8.x2.shared.b16 {%0,%1},[%2];":"=r"(a[mi][0]),"=r"(a[mi][1]):"r"(aA)); }
2136 #pragma unroll
2137 for (int ni=0; ni<WN/8; ++ni){
2138 unsigned bA=(unsigned)__cvta_generic_to_shared(&Bs[buf][(warp_col+ni*8+(lane&7))*BK+kk]);
2139 asm volatile("ldmatrix.sync.aligned.m8n8.x1.shared.b16 {%0},[%1];":"=r"(b[ni]):"r"(bA)); }
2140 #pragma unroll
2141 for (int mi=0; mi<WM/16; ++mi)
2142 #pragma unroll
2143 for (int ni=0; ni<WN/8; ++ni)
2144 asm volatile("mma.sync.aligned.m16n8k16.row.col.s32.s8.s8.s32 {%0,%1,%2,%3},{%4,%5},{%6},{%0,%1,%2,%3};"
2145 :"+r"(c[mi][ni][0]),"+r"(c[mi][ni][1]),"+r"(c[mi][ni][2]),"+r"(c[mi][ni][3])
2146 :"r"(a[mi][0]),"r"(a[mi][1]),"r"(b[ni]));
2147 }
2148 __syncthreads();
2149 int nxt=s+STAGES-1; if(nxt<nstages) LOAD_STAGE(nxt%STAGES, nxt); else __pipeline_commit();
2150 }
2151 #pragma unroll
2152 for (int mi=0; mi<WM/16; ++mi)
2153 #pragma unroll
2154 for (int ni=0; ni<WN/8; ++ni) {
2155 int rb=block_row+warp_row+mi*16, cb=block_col+warp_col+ni*8;
2156 C[(rb+gid)*N+cb+tg*2]=c[mi][ni][0]; C[(rb+gid)*N+cb+tg*2+1]=c[mi][ni][1];
2157 C[(rb+gid+8)*N+cb+tg*2]=c[mi][ni][2]; C[(rb+gid+8)*N+cb+tg*2+1]=c[mi][ni][3];
2158 }
2159}
2160"#;
2161
2162pub fn emit_mma_tn_ldm_swizzle_bench(
2170 bm: usize,
2171 bn: usize,
2172 bk: usize,
2173 warps_m: usize,
2174 warps_n: usize,
2175 stages: usize,
2176 pad: usize,
2177 m: usize,
2178 k: usize,
2179 n: usize,
2180) -> String {
2181 let defs = format!(
2182 "#define BM {bm}\n#define BN {bn}\n#define BK {bk}\n#define WARPS_M {warps_m}\n#define WARPS_N {warps_n}\n#define STAGES {stages}\n#define PAD {pad}\n"
2183 );
2184 let kernel = format!("{defs}{}", MMA_TN_LDM_SWZ_BODY);
2185 format!(
2186 r#"{kernel}
2187#include <cstdio>
2188#include <cstdlib>
2189int main() {{ const int M={m},K={k},N={n};
2190 signed char *dA,*dB; int *dC;
2191 cudaMalloc(&dA,(size_t)M*K);cudaMalloc(&dB,(size_t)K*N);cudaMalloc(&dC,(size_t)M*N*4);
2192 cudaMemset(dA,1,(size_t)M*K);cudaMemset(dB,1,(size_t)K*N);
2193 dim3 grid(N/{bn}, M/{bm});
2194 gemm_mma_tn_ldm<<<grid,{threads}>>>(dA,dB,dC,M,N,K);
2195 cudaError_t err=cudaDeviceSynchronize(); if(err){{printf("LAUNCH_ERROR %s\n",cudaGetErrorString(err));return 1;}}
2196 int *hC=(int*)malloc((size_t)M*N*4); cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
2197 long step=((long)M*N)/4096+1; for(long i=0;i<(long)M*N;i+=step) if(hC[i]!=K){{printf("INCORRECT %ld: %d!=%d\n",i,hC[i],K);return 1;}}
2198 cudaEvent_t s,e; cudaEventCreate(&s); cudaEventCreate(&e); int IT=30; float total=0;
2199 for(int it=0;it<IT;it++){{ cudaEventRecord(s); gemm_mma_tn_ldm<<<grid,{threads}>>>(dA,dB,dC,M,N,K); cudaEventRecord(e); cudaEventSynchronize(e); float ms=0; cudaEventElapsedTime(&ms,s,e); total+=ms; }}
2200 printf("GOPS %.1f\n", 2.0*M*N*K/((total/IT)/1e3)/1e9); return 0; }}
2201"#,
2202 kernel = kernel,
2203 m = m,
2204 k = k,
2205 n = n,
2206 bm = bm,
2207 bn = bn,
2208 threads = warps_m * warps_n * 32
2209 )
2210}
2211
2212pub fn emit_mma_tn_ldm_swizzle_program(
2214 bm: usize,
2215 bn: usize,
2216 bk: usize,
2217 warps_m: usize,
2218 warps_n: usize,
2219 stages: usize,
2220 pad: usize,
2221 m: usize,
2222 k: usize,
2223 n: usize,
2224) -> String {
2225 let defs = format!(
2226 "#define BM {bm}\n#define BN {bn}\n#define BK {bk}\n#define WARPS_M {warps_m}\n#define WARPS_N {warps_n}\n#define STAGES {stages}\n#define PAD {pad}\n"
2227 );
2228 let kernel = format!("{defs}{}", MMA_TN_LDM_SWZ_BODY);
2229 format!(
2230 r#"{kernel}
2231#include <cstdio>
2232#include <cstdlib>
2233int main() {{ const int M={m},K={k},N={n};
2234 signed char *hA=(signed char*)malloc((size_t)M*K),*hB=(signed char*)malloc((size_t)K*N);
2235 for(long i=0;i<(long)M*K;++i) hA[i]=(signed char)(((i*7+3)%251)-125);
2236 for(long nn=0;nn<N;++nn) for(long kk=0;kk<K;++kk){{ long bkn=(long)kk*N+nn; hB[nn*K+kk]=(signed char)(((bkn*5+1)%251)-125); }}
2237 signed char *dA,*dB; int *dC,*hC=(int*)malloc((size_t)M*N*4);
2238 cudaMalloc(&dA,(size_t)M*K);cudaMalloc(&dB,(size_t)K*N);cudaMalloc(&dC,(size_t)M*N*4);
2239 cudaMemcpy(dA,hA,(size_t)M*K,cudaMemcpyHostToDevice);cudaMemcpy(dB,hB,(size_t)K*N,cudaMemcpyHostToDevice);
2240 dim3 grid(N/{bn},M/{bm}); gemm_mma_tn_ldm<<<grid,{threads}>>>(dA,dB,dC,M,N,K);
2241 cudaError_t e=cudaDeviceSynchronize(); if(e){{printf("ERR %s\n",cudaGetErrorString(e));return 1;}}
2242 cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
2243 for(long i=0;i<(long)M*N;++i) printf("%d\n",hC[i]); return 0; }}
2244"#,
2245 kernel = kernel,
2246 m = m,
2247 k = k,
2248 n = n,
2249 bm = bm,
2250 bn = bn,
2251 threads = warps_m * warps_n * 32
2252 )
2253}
2254
2255const MMA_TN_LDM_SWZ_BODY: &str = r#"#include <cuda_pipeline.h>
2256#define WM (BM / WARPS_M)
2257#define WN (BN / WARPS_N)
2258#define NTHREAD (WARPS_M * WARPS_N * 32)
2259#define SBK (BK + PAD)
2260extern "C" __global__ void gemm_mma_tn_ldm(const signed char* A, const signed char* Bcol, int* C, int M, int N, int K) {
2261 __shared__ signed char As[STAGES][BM * SBK];
2262 __shared__ signed char Bs[STAGES][BN * SBK];
2263 const int tid = threadIdx.x, warpId = tid / 32, lane = tid % 32;
2264 const int gid = lane / 4, tg = lane % 4;
2265 const int warp_row = (warpId / WARPS_N) * WM, warp_col = (warpId % WARPS_N) * WN;
2266 const int block_row = blockIdx.y * BM, block_col = blockIdx.x * BN;
2267 const int nstages = K / BK;
2268 int c[WM / 16][WN / 8][4];
2269 #pragma unroll
2270 for (int mi=0; mi<WM/16; ++mi)
2271 #pragma unroll
2272 for (int ni=0; ni<WN/8; ++ni) { c[mi][ni][0]=c[mi][ni][1]=c[mi][ni][2]=c[mi][ni][3]=0; }
2273 #define LOAD_STAGE(buf, st) do { int _k0=(st)*BK; \
2274 for (int idx=tid; idx<(BM*BK)/16; idx+=NTHREAD){ int r=(idx*16)/BK, cc=(idx*16)%BK; \
2275 __pipeline_memcpy_async(&As[buf][r*SBK+cc], &A[(block_row+r)*K + _k0+cc], 16); } \
2276 for (int idx=tid; idx<(BN*BK)/16; idx+=NTHREAD){ int r=(idx*16)/BK, cc=(idx*16)%BK; \
2277 __pipeline_memcpy_async(&Bs[buf][r*SBK+cc], &Bcol[(block_col+r)*K + _k0+cc], 16); } \
2278 __pipeline_commit(); } while(0)
2279 #pragma unroll
2280 for (int i=0;i<STAGES-1;++i){ if(i<nstages) LOAD_STAGE(i,i); else __pipeline_commit(); }
2281 for (int s=0; s<nstages; ++s) {
2282 __pipeline_wait_prior(STAGES-2);
2283 __syncthreads();
2284 int buf = s % STAGES;
2285 #pragma unroll
2286 for (int kk=0; kk<BK; kk+=16) {
2287 int a[WM/16][2], b[WN/8];
2288 #pragma unroll
2289 for (int mi=0; mi<WM/16; ++mi){
2290 unsigned aA=(unsigned)__cvta_generic_to_shared(&As[buf][(warp_row+mi*16+(lane&15))*SBK+kk]);
2291 asm volatile("ldmatrix.sync.aligned.m8n8.x2.shared.b16 {%0,%1},[%2];":"=r"(a[mi][0]),"=r"(a[mi][1]):"r"(aA)); }
2292 #pragma unroll
2293 for (int ni=0; ni<WN/8; ++ni){
2294 unsigned bA=(unsigned)__cvta_generic_to_shared(&Bs[buf][(warp_col+ni*8+(lane&7))*SBK+kk]);
2295 asm volatile("ldmatrix.sync.aligned.m8n8.x1.shared.b16 {%0},[%1];":"=r"(b[ni]):"r"(bA)); }
2296 #pragma unroll
2297 for (int mi=0; mi<WM/16; ++mi)
2298 #pragma unroll
2299 for (int ni=0; ni<WN/8; ++ni)
2300 asm volatile("mma.sync.aligned.m16n8k16.row.col.s32.s8.s8.s32 {%0,%1,%2,%3},{%4,%5},{%6},{%0,%1,%2,%3};"
2301 :"+r"(c[mi][ni][0]),"+r"(c[mi][ni][1]),"+r"(c[mi][ni][2]),"+r"(c[mi][ni][3])
2302 :"r"(a[mi][0]),"r"(a[mi][1]),"r"(b[ni]));
2303 }
2304 __syncthreads();
2305 int nxt=s+STAGES-1; if(nxt<nstages) LOAD_STAGE(nxt%STAGES, nxt); else __pipeline_commit();
2306 }
2307 #pragma unroll
2308 for (int mi=0; mi<WM/16; ++mi)
2309 #pragma unroll
2310 for (int ni=0; ni<WN/8; ++ni) {
2311 int rb=block_row+warp_row+mi*16, cb=block_col+warp_col+ni*8;
2312 C[(rb+gid)*N+cb+tg*2]=c[mi][ni][0]; C[(rb+gid)*N+cb+tg*2+1]=c[mi][ni][1];
2313 C[(rb+gid+8)*N+cb+tg*2]=c[mi][ni][2]; C[(rb+gid+8)*N+cb+tg*2+1]=c[mi][ni][3];
2314 }
2315}
2316"#;
2317
2318pub fn emit_mma_tn_k32_bench(
2324 bm: usize,
2325 bn: usize,
2326 warps_m: usize,
2327 warps_n: usize,
2328 stages: usize,
2329 m: usize,
2330 k: usize,
2331 n: usize,
2332) -> String {
2333 let defs = format!("#define BM {bm}\n#define BN {bn}\n#define BK 32\n#define WARPS_M {warps_m}\n#define WARPS_N {warps_n}\n#define STAGES {stages}\n");
2334 let kernel = format!("{defs}{}", MMA_TN_K32_BODY);
2335 format!(
2336 r#"{kernel}
2337#include <cstdio>
2338#include <cstdlib>
2339int main() {{ const int M={m},K={k},N={n};
2340 signed char *dA,*dB; int *dC;
2341 cudaMalloc(&dA,(size_t)M*K);cudaMalloc(&dB,(size_t)K*N);cudaMalloc(&dC,(size_t)M*N*4);
2342 cudaMemset(dA,1,(size_t)M*K);cudaMemset(dB,1,(size_t)K*N);
2343 dim3 grid(N/{bn}, M/{bm});
2344 gemm_mma_tn_k32<<<grid,{threads}>>>(dA,dB,dC,M,N,K);
2345 cudaError_t err=cudaDeviceSynchronize(); if(err){{printf("LAUNCH_ERROR %s\n",cudaGetErrorString(err));return 1;}}
2346 int *hC=(int*)malloc((size_t)M*N*4); cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
2347 long step=((long)M*N)/4096+1; for(long i=0;i<(long)M*N;i+=step) if(hC[i]!=K){{printf("INCORRECT %ld: %d!=%d\n",i,hC[i],K);return 1;}}
2348 cudaEvent_t s,e; cudaEventCreate(&s); cudaEventCreate(&e); int IT=30; float total=0;
2349 for(int it=0;it<IT;it++){{ cudaEventRecord(s); gemm_mma_tn_k32<<<grid,{threads}>>>(dA,dB,dC,M,N,K); cudaEventRecord(e); cudaEventSynchronize(e); float ms=0; cudaEventElapsedTime(&ms,s,e); total+=ms; }}
2350 printf("GOPS %.1f\n", 2.0*M*N*K/((total/IT)/1e3)/1e9); return 0; }}
2351"#,
2352 kernel = kernel,
2353 m = m,
2354 k = k,
2355 n = n,
2356 bm = bm,
2357 bn = bn,
2358 threads = warps_m * warps_n * 32
2359 )
2360}
2361
2362pub fn emit_mma_tn_k32_program(
2363 bm: usize,
2364 bn: usize,
2365 warps_m: usize,
2366 warps_n: usize,
2367 stages: usize,
2368 m: usize,
2369 k: usize,
2370 n: usize,
2371) -> String {
2372 let defs = format!("#define BM {bm}\n#define BN {bn}\n#define BK 32\n#define WARPS_M {warps_m}\n#define WARPS_N {warps_n}\n#define STAGES {stages}\n");
2373 let kernel = format!("{defs}{}", MMA_TN_K32_BODY);
2374 format!(
2375 r#"{kernel}
2376#include <cstdio>
2377#include <cstdlib>
2378int main() {{ const int M={m},K={k},N={n};
2379 signed char *hA=(signed char*)malloc((size_t)M*K),*hB=(signed char*)malloc((size_t)K*N);
2380 for(long i=0;i<(long)M*K;++i) hA[i]=(signed char)(((i*7+3)%251)-125);
2381 for(long nn=0;nn<N;++nn) for(long kk=0;kk<K;++kk){{ long bkn=(long)kk*N+nn; hB[nn*K+kk]=(signed char)(((bkn*5+1)%251)-125); }}
2382 signed char *dA,*dB; int *dC,*hC=(int*)malloc((size_t)M*N*4);
2383 cudaMalloc(&dA,(size_t)M*K);cudaMalloc(&dB,(size_t)K*N);cudaMalloc(&dC,(size_t)M*N*4);
2384 cudaMemcpy(dA,hA,(size_t)M*K,cudaMemcpyHostToDevice);cudaMemcpy(dB,hB,(size_t)K*N,cudaMemcpyHostToDevice);
2385 dim3 grid(N/{bn},M/{bm}); gemm_mma_tn_k32<<<grid,{threads}>>>(dA,dB,dC,M,N,K);
2386 cudaError_t e=cudaDeviceSynchronize(); if(e){{printf("ERR %s\n",cudaGetErrorString(e));return 1;}}
2387 cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
2388 for(long i=0;i<(long)M*N;++i) printf("%d\n",hC[i]); return 0; }}
2389"#,
2390 kernel = kernel,
2391 m = m,
2392 k = k,
2393 n = n,
2394 bm = bm,
2395 bn = bn,
2396 threads = warps_m * warps_n * 32
2397 )
2398}
2399
2400const MMA_TN_K32_BODY: &str = r#"#include <cuda_pipeline.h>
2401#define WM (BM / WARPS_M)
2402#define WN (BN / WARPS_N)
2403#define NTHREAD (WARPS_M * WARPS_N * 32)
2404extern "C" __global__ void gemm_mma_tn_k32(const signed char* A, const signed char* Bcol, int* C, int M, int N, int K) {
2405 __shared__ signed char As[STAGES][BM * BK];
2406 __shared__ signed char Bs[STAGES][BN * BK];
2407 const int tid = threadIdx.x, warpId = tid / 32, lane = tid % 32;
2408 const int gid = lane / 4, tg = lane % 4;
2409 const int warp_row = (warpId / WARPS_N) * WM, warp_col = (warpId % WARPS_N) * WN;
2410 const int block_row = blockIdx.y * BM, block_col = blockIdx.x * BN;
2411 const int nstages = K / BK;
2412 int c[WM / 16][WN / 8][4];
2413 #pragma unroll
2414 for (int mi=0; mi<WM/16; ++mi)
2415 #pragma unroll
2416 for (int ni=0; ni<WN/8; ++ni) { c[mi][ni][0]=c[mi][ni][1]=c[mi][ni][2]=c[mi][ni][3]=0; }
2417 #define LOAD_STAGE(buf, st) do { int _k0=(st)*BK; \
2418 for (int idx=tid; idx<(BM*BK)/16; idx+=NTHREAD){ int r=(idx*16)/BK, cc=(idx*16)%BK; \
2419 __pipeline_memcpy_async(&As[buf][r*BK+cc], &A[(block_row+r)*K + _k0+cc], 16); } \
2420 for (int idx=tid; idx<(BN*BK)/16; idx+=NTHREAD){ int r=(idx*16)/BK, cc=(idx*16)%BK; \
2421 __pipeline_memcpy_async(&Bs[buf][r*BK+cc], &Bcol[(block_col+r)*K + _k0+cc], 16); } \
2422 __pipeline_commit(); } while(0)
2423 #pragma unroll
2424 for (int i=0;i<STAGES-1;++i){ if(i<nstages) LOAD_STAGE(i,i); else __pipeline_commit(); }
2425 for (int s=0; s<nstages; ++s) {
2426 __pipeline_wait_prior(STAGES-2);
2427 __syncthreads();
2428 int buf = s % STAGES;
2429 int a[WM/16][4], b[WN/8][2];
2430 #pragma unroll
2431 for (int mi=0; mi<WM/16; ++mi){
2432 int mtx=lane/8, row=lane%8;
2433 unsigned aA=(unsigned)__cvta_generic_to_shared(&As[buf][(warp_row+mi*16 + (mtx&1)*8 + row)*BK + (mtx>>1)*16]);
2434 asm volatile("ldmatrix.sync.aligned.m8n8.x4.shared.b16 {%0,%1,%2,%3},[%4];":"=r"(a[mi][0]),"=r"(a[mi][1]),"=r"(a[mi][2]),"=r"(a[mi][3]):"r"(aA)); }
2435 #pragma unroll
2436 for (int ni=0; ni<WN/8; ++ni){
2437 int mtx=(lane>>3)&1, row=lane&7;
2438 unsigned bA=(unsigned)__cvta_generic_to_shared(&Bs[buf][(warp_col+ni*8 + row)*BK + mtx*16]);
2439 asm volatile("ldmatrix.sync.aligned.m8n8.x2.shared.b16 {%0,%1},[%2];":"=r"(b[ni][0]),"=r"(b[ni][1]):"r"(bA)); }
2440 #pragma unroll
2441 for (int mi=0; mi<WM/16; ++mi)
2442 #pragma unroll
2443 for (int ni=0; ni<WN/8; ++ni)
2444 asm volatile("mma.sync.aligned.m16n8k32.row.col.s32.s8.s8.s32 {%0,%1,%2,%3},{%4,%5,%6,%7},{%8,%9},{%0,%1,%2,%3};"
2445 :"+r"(c[mi][ni][0]),"+r"(c[mi][ni][1]),"+r"(c[mi][ni][2]),"+r"(c[mi][ni][3])
2446 :"r"(a[mi][0]),"r"(a[mi][1]),"r"(a[mi][2]),"r"(a[mi][3]),"r"(b[ni][0]),"r"(b[ni][1]));
2447 __syncthreads();
2448 int nxt=s+STAGES-1; if(nxt<nstages) LOAD_STAGE(nxt%STAGES, nxt); else __pipeline_commit();
2449 }
2450 #pragma unroll
2451 for (int mi=0; mi<WM/16; ++mi)
2452 #pragma unroll
2453 for (int ni=0; ni<WN/8; ++ni) {
2454 int rb=block_row+warp_row+mi*16, cb=block_col+warp_col+ni*8;
2455 C[(rb+gid)*N+cb+tg*2]=c[mi][ni][0]; C[(rb+gid)*N+cb+tg*2+1]=c[mi][ni][1];
2456 C[(rb+gid+8)*N+cb+tg*2]=c[mi][ni][2]; C[(rb+gid+8)*N+cb+tg*2+1]=c[mi][ni][3];
2457 }
2458}
2459"#;
2460
2461pub fn emit_mma_tn_k32swz_bench(
2468 bm: usize,
2469 bn: usize,
2470 warps_m: usize,
2471 warps_n: usize,
2472 stages: usize,
2473 m: usize,
2474 k: usize,
2475 n: usize,
2476) -> String {
2477 let defs = format!("#define BM {bm}\n#define BN {bn}\n#define BK 32\n#define WARPS_M {warps_m}\n#define WARPS_N {warps_n}\n#define STAGES {stages}\n");
2478 let kernel = format!("{defs}{}", MMA_TN_K32SWZ_BODY);
2479 format!(
2480 r#"{kernel}
2481#include <cstdio>
2482#include <cstdlib>
2483int main() {{ const int M={m},K={k},N={n};
2484 signed char *dA,*dB; int *dC;
2485 cudaMalloc(&dA,(size_t)M*K);cudaMalloc(&dB,(size_t)K*N);cudaMalloc(&dC,(size_t)M*N*4);
2486 cudaMemset(dA,1,(size_t)M*K);cudaMemset(dB,1,(size_t)K*N);
2487 dim3 grid(N/{bn}, M/{bm});
2488 gemm_mma_tn_k32swz<<<grid,{threads}>>>(dA,dB,dC,M,N,K);
2489 cudaError_t err=cudaDeviceSynchronize(); if(err){{printf("LAUNCH_ERROR %s\n",cudaGetErrorString(err));return 1;}}
2490 int *hC=(int*)malloc((size_t)M*N*4); cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
2491 long step=((long)M*N)/4096+1; for(long i=0;i<(long)M*N;i+=step) if(hC[i]!=K){{printf("INCORRECT %ld: %d!=%d\n",i,hC[i],K);return 1;}}
2492 cudaEvent_t s,e; cudaEventCreate(&s); cudaEventCreate(&e); int IT=30; float total=0;
2493 for(int it=0;it<IT;it++){{ cudaEventRecord(s); gemm_mma_tn_k32swz<<<grid,{threads}>>>(dA,dB,dC,M,N,K); cudaEventRecord(e); cudaEventSynchronize(e); float ms=0; cudaEventElapsedTime(&ms,s,e); total+=ms; }}
2494 printf("GOPS %.1f\n", 2.0*M*N*K/((total/IT)/1e3)/1e9); return 0; }}
2495"#,
2496 kernel = kernel,
2497 m = m,
2498 k = k,
2499 n = n,
2500 bm = bm,
2501 bn = bn,
2502 threads = warps_m * warps_n * 32
2503 )
2504}
2505
2506pub fn emit_mma_tn_k32swz_program(
2507 bm: usize,
2508 bn: usize,
2509 warps_m: usize,
2510 warps_n: usize,
2511 stages: usize,
2512 m: usize,
2513 k: usize,
2514 n: usize,
2515) -> String {
2516 let defs = format!("#define BM {bm}\n#define BN {bn}\n#define BK 32\n#define WARPS_M {warps_m}\n#define WARPS_N {warps_n}\n#define STAGES {stages}\n");
2517 let kernel = format!("{defs}{}", MMA_TN_K32SWZ_BODY);
2518 format!(
2519 r#"{kernel}
2520#include <cstdio>
2521#include <cstdlib>
2522int main() {{ const int M={m},K={k},N={n};
2523 signed char *hA=(signed char*)malloc((size_t)M*K),*hB=(signed char*)malloc((size_t)K*N);
2524 for(long i=0;i<(long)M*K;++i) hA[i]=(signed char)(((i*7+3)%251)-125);
2525 for(long nn=0;nn<N;++nn) for(long kk=0;kk<K;++kk){{ long bkn=(long)kk*N+nn; hB[nn*K+kk]=(signed char)(((bkn*5+1)%251)-125); }}
2526 signed char *dA,*dB; int *dC,*hC=(int*)malloc((size_t)M*N*4);
2527 cudaMalloc(&dA,(size_t)M*K);cudaMalloc(&dB,(size_t)K*N);cudaMalloc(&dC,(size_t)M*N*4);
2528 cudaMemcpy(dA,hA,(size_t)M*K,cudaMemcpyHostToDevice);cudaMemcpy(dB,hB,(size_t)K*N,cudaMemcpyHostToDevice);
2529 dim3 grid(N/{bn},M/{bm}); gemm_mma_tn_k32swz<<<grid,{threads}>>>(dA,dB,dC,M,N,K);
2530 cudaError_t e=cudaDeviceSynchronize(); if(e){{printf("ERR %s\n",cudaGetErrorString(e));return 1;}}
2531 cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
2532 for(long i=0;i<(long)M*N;++i) printf("%d\n",hC[i]); return 0; }}
2533"#,
2534 kernel = kernel,
2535 m = m,
2536 k = k,
2537 n = n,
2538 bm = bm,
2539 bn = bn,
2540 threads = warps_m * warps_n * 32
2541 )
2542}
2543
2544const MMA_TN_K32SWZ_BODY: &str = r#"#include <cuda_pipeline.h>
2545#define WM (BM / WARPS_M)
2546#define WN (BN / WARPS_N)
2547#define NTHREAD (WARPS_M * WARPS_N * 32)
2548#define SW(row) ((((row)>>2)&1)*16)
2549extern "C" __global__ void gemm_mma_tn_k32swz(const signed char* A, const signed char* Bcol, int* C, int M, int N, int K) {
2550 __shared__ signed char As[STAGES][BM * BK];
2551 __shared__ signed char Bs[STAGES][BN * BK];
2552 const int tid = threadIdx.x, warpId = tid / 32, lane = tid % 32;
2553 const int gid = lane / 4, tg = lane % 4;
2554 const int warp_row = (warpId / WARPS_N) * WM, warp_col = (warpId % WARPS_N) * WN;
2555 const int block_row = blockIdx.y * BM, block_col = blockIdx.x * BN;
2556 const int nstages = K / BK;
2557 int c[WM / 16][WN / 8][4];
2558 #pragma unroll
2559 for (int mi=0; mi<WM/16; ++mi)
2560 #pragma unroll
2561 for (int ni=0; ni<WN/8; ++ni) { c[mi][ni][0]=c[mi][ni][1]=c[mi][ni][2]=c[mi][ni][3]=0; }
2562 // store with the SW swizzle: cc is the 16-byte chunk offset (0 or 16), XORed by SW(row).
2563 #define LOAD_STAGE(buf, st) do { int _k0=(st)*BK; \
2564 for (int idx=tid; idx<(BM*BK)/16; idx+=NTHREAD){ int r=(idx*16)/BK, cc=(idx*16)%BK; \
2565 __pipeline_memcpy_async(&As[buf][r*BK + (cc ^ SW(r))], &A[(block_row+r)*K + _k0+cc], 16); } \
2566 for (int idx=tid; idx<(BN*BK)/16; idx+=NTHREAD){ int r=(idx*16)/BK, cc=(idx*16)%BK; \
2567 __pipeline_memcpy_async(&Bs[buf][r*BK + (cc ^ SW(r))], &Bcol[(block_col+r)*K + _k0+cc], 16); } \
2568 __pipeline_commit(); } while(0)
2569 #pragma unroll
2570 for (int i=0;i<STAGES-1;++i){ if(i<nstages) LOAD_STAGE(i,i); else __pipeline_commit(); }
2571 for (int s=0; s<nstages; ++s) {
2572 __pipeline_wait_prior(STAGES-2);
2573 __syncthreads();
2574 int buf = s % STAGES;
2575 int a[WM/16][4], b[WN/8][2];
2576 #pragma unroll
2577 for (int mi=0; mi<WM/16; ++mi){
2578 int mtx=lane/8, row=lane%8; int shrow=warp_row+mi*16 + (mtx&1)*8 + row; int koff=(mtx>>1)*16;
2579 unsigned aA=(unsigned)__cvta_generic_to_shared(&As[buf][shrow*BK + (koff ^ SW(shrow))]);
2580 asm volatile("ldmatrix.sync.aligned.m8n8.x4.shared.b16 {%0,%1,%2,%3},[%4];":"=r"(a[mi][0]),"=r"(a[mi][1]),"=r"(a[mi][2]),"=r"(a[mi][3]):"r"(aA)); }
2581 #pragma unroll
2582 for (int ni=0; ni<WN/8; ++ni){
2583 int mtx=(lane>>3)&1, row=lane&7; int shrow=warp_col+ni*8 + row; int koff=mtx*16;
2584 unsigned bA=(unsigned)__cvta_generic_to_shared(&Bs[buf][shrow*BK + (koff ^ SW(shrow))]);
2585 asm volatile("ldmatrix.sync.aligned.m8n8.x2.shared.b16 {%0,%1},[%2];":"=r"(b[ni][0]),"=r"(b[ni][1]):"r"(bA)); }
2586 #pragma unroll
2587 for (int mi=0; mi<WM/16; ++mi)
2588 #pragma unroll
2589 for (int ni=0; ni<WN/8; ++ni)
2590 asm volatile("mma.sync.aligned.m16n8k32.row.col.s32.s8.s8.s32 {%0,%1,%2,%3},{%4,%5,%6,%7},{%8,%9},{%0,%1,%2,%3};"
2591 :"+r"(c[mi][ni][0]),"+r"(c[mi][ni][1]),"+r"(c[mi][ni][2]),"+r"(c[mi][ni][3])
2592 :"r"(a[mi][0]),"r"(a[mi][1]),"r"(a[mi][2]),"r"(a[mi][3]),"r"(b[ni][0]),"r"(b[ni][1]));
2593 __syncthreads();
2594 int nxt=s+STAGES-1; if(nxt<nstages) LOAD_STAGE(nxt%STAGES, nxt); else __pipeline_commit();
2595 }
2596 #pragma unroll
2597 for (int mi=0; mi<WM/16; ++mi)
2598 #pragma unroll
2599 for (int ni=0; ni<WN/8; ++ni) {
2600 int rb=block_row+warp_row+mi*16, cb=block_col+warp_col+ni*8;
2601 C[(rb+gid)*N+cb+tg*2]=c[mi][ni][0]; C[(rb+gid)*N+cb+tg*2+1]=c[mi][ni][1];
2602 C[(rb+gid+8)*N+cb+tg*2]=c[mi][ni][2]; C[(rb+gid+8)*N+cb+tg*2+1]=c[mi][ni][3];
2603 }
2604}
2605"#;
2606
2607pub fn emit_mma_tn_k32db_bench(
2613 bm: usize,
2614 bn: usize,
2615 warps_m: usize,
2616 warps_n: usize,
2617 stages: usize,
2618 m: usize,
2619 k: usize,
2620 n: usize,
2621) -> String {
2622 let defs = format!("#define BM {bm}\n#define BN {bn}\n#define BK 32\n#define WARPS_M {warps_m}\n#define WARPS_N {warps_n}\n#define STAGES {stages}\n");
2623 let kernel = format!("{defs}{}", MMA_TN_K32DB_BODY);
2624 format!(
2625 r#"{kernel}
2626#include <cstdio>
2627#include <cstdlib>
2628int main() {{ const int M={m},K={k},N={n};
2629 signed char *dA,*dB; int *dC;
2630 cudaMalloc(&dA,(size_t)M*K);cudaMalloc(&dB,(size_t)K*N);cudaMalloc(&dC,(size_t)M*N*4);
2631 cudaMemset(dA,1,(size_t)M*K);cudaMemset(dB,1,(size_t)K*N);
2632 dim3 grid(N/{bn}, M/{bm});
2633 gemm_mma_tn_k32db<<<grid,{threads}>>>(dA,dB,dC,M,N,K);
2634 cudaError_t err=cudaDeviceSynchronize(); if(err){{printf("LAUNCH_ERROR %s\n",cudaGetErrorString(err));return 1;}}
2635 int *hC=(int*)malloc((size_t)M*N*4); cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
2636 long step=((long)M*N)/4096+1; for(long i=0;i<(long)M*N;i+=step) if(hC[i]!=K){{printf("INCORRECT %ld: %d!=%d\n",i,hC[i],K);return 1;}}
2637 cudaEvent_t s,e; cudaEventCreate(&s); cudaEventCreate(&e); int IT=30; float total=0;
2638 for(int it=0;it<IT;it++){{ cudaEventRecord(s); gemm_mma_tn_k32db<<<grid,{threads}>>>(dA,dB,dC,M,N,K); cudaEventRecord(e); cudaEventSynchronize(e); float ms=0; cudaEventElapsedTime(&ms,s,e); total+=ms; }}
2639 printf("GOPS %.1f\n", 2.0*M*N*K/((total/IT)/1e3)/1e9); return 0; }}
2640"#,
2641 kernel = kernel,
2642 m = m,
2643 k = k,
2644 n = n,
2645 bm = bm,
2646 bn = bn,
2647 threads = warps_m * warps_n * 32
2648 )
2649}
2650
2651pub fn emit_mma_tn_k32db_program(
2652 bm: usize,
2653 bn: usize,
2654 warps_m: usize,
2655 warps_n: usize,
2656 stages: usize,
2657 m: usize,
2658 k: usize,
2659 n: usize,
2660) -> String {
2661 let defs = format!("#define BM {bm}\n#define BN {bn}\n#define BK 32\n#define WARPS_M {warps_m}\n#define WARPS_N {warps_n}\n#define STAGES {stages}\n");
2662 let kernel = format!("{defs}{}", MMA_TN_K32DB_BODY);
2663 format!(
2664 r#"{kernel}
2665#include <cstdio>
2666#include <cstdlib>
2667int main() {{ const int M={m},K={k},N={n};
2668 signed char *hA=(signed char*)malloc((size_t)M*K),*hB=(signed char*)malloc((size_t)K*N);
2669 for(long i=0;i<(long)M*K;++i) hA[i]=(signed char)(((i*7+3)%251)-125);
2670 for(long nn=0;nn<N;++nn) for(long kk=0;kk<K;++kk){{ long bkn=(long)kk*N+nn; hB[nn*K+kk]=(signed char)(((bkn*5+1)%251)-125); }}
2671 signed char *dA,*dB; int *dC,*hC=(int*)malloc((size_t)M*N*4);
2672 cudaMalloc(&dA,(size_t)M*K);cudaMalloc(&dB,(size_t)K*N);cudaMalloc(&dC,(size_t)M*N*4);
2673 cudaMemcpy(dA,hA,(size_t)M*K,cudaMemcpyHostToDevice);cudaMemcpy(dB,hB,(size_t)K*N,cudaMemcpyHostToDevice);
2674 dim3 grid(N/{bn},M/{bm}); gemm_mma_tn_k32db<<<grid,{threads}>>>(dA,dB,dC,M,N,K);
2675 cudaError_t e=cudaDeviceSynchronize(); if(e){{printf("ERR %s\n",cudaGetErrorString(e));return 1;}}
2676 cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
2677 for(long i=0;i<(long)M*N;++i) printf("%d\n",hC[i]); return 0; }}
2678"#,
2679 kernel = kernel,
2680 m = m,
2681 k = k,
2682 n = n,
2683 bm = bm,
2684 bn = bn,
2685 threads = warps_m * warps_n * 32
2686 )
2687}
2688
2689const MMA_TN_K32DB_BODY: &str = r#"#include <cuda_pipeline.h>
2690#define WM (BM / WARPS_M)
2691#define WN (BN / WARPS_N)
2692#define NTHREAD (WARPS_M * WARPS_N * 32)
2693#define SW(row) ((((row)>>2)&1)*16)
2694#define LDA(buf, dst) do { \
2695 for (int mi=0; mi<WM/16; ++mi){ int mtx=lane/8,row=lane%8; int shrow=warp_row+mi*16+(mtx&1)*8+row; int koff=(mtx>>1)*16; \
2696 unsigned aA=(unsigned)__cvta_generic_to_shared(&As[buf][shrow*BK + (koff ^ SW(shrow))]); \
2697 asm volatile("ldmatrix.sync.aligned.m8n8.x4.shared.b16 {%0,%1,%2,%3},[%4];":"=r"(af[dst][mi][0]),"=r"(af[dst][mi][1]),"=r"(af[dst][mi][2]),"=r"(af[dst][mi][3]):"r"(aA)); } \
2698 for (int ni=0; ni<WN/8; ++ni){ int mtx=(lane>>3)&1,row=lane&7; int shrow=warp_col+ni*8+row; int koff=mtx*16; \
2699 unsigned bA=(unsigned)__cvta_generic_to_shared(&Bs[buf][shrow*BK + (koff ^ SW(shrow))]); \
2700 asm volatile("ldmatrix.sync.aligned.m8n8.x2.shared.b16 {%0,%1},[%2];":"=r"(bf[dst][ni][0]),"=r"(bf[dst][ni][1]):"r"(bA)); } \
2701 } while(0)
2702extern "C" __global__ void gemm_mma_tn_k32db(const signed char* A, const signed char* Bcol, int* C, int M, int N, int K) {
2703 __shared__ signed char As[STAGES][BM * BK];
2704 __shared__ signed char Bs[STAGES][BN * BK];
2705 const int tid = threadIdx.x, warpId = tid / 32, lane = tid % 32;
2706 const int gid = lane / 4, tg = lane % 4;
2707 const int warp_row = (warpId / WARPS_N) * WM, warp_col = (warpId % WARPS_N) * WN;
2708 const int block_row = blockIdx.y * BM, block_col = blockIdx.x * BN;
2709 const int nstages = K / BK;
2710 int c[WM/16][WN/8][4];
2711 int af[2][WM/16][4], bf[2][WN/8][2];
2712 #pragma unroll
2713 for (int mi=0; mi<WM/16; ++mi)
2714 #pragma unroll
2715 for (int ni=0; ni<WN/8; ++ni) { c[mi][ni][0]=c[mi][ni][1]=c[mi][ni][2]=c[mi][ni][3]=0; }
2716 #define LOAD_STAGE(buf, st) do { int _k0=(st)*BK; \
2717 for (int idx=tid; idx<(BM*BK)/16; idx+=NTHREAD){ int r=(idx*16)/BK, cc=(idx*16)%BK; \
2718 __pipeline_memcpy_async(&As[buf][r*BK + (cc ^ SW(r))], &A[(block_row+r)*K + _k0+cc], 16); } \
2719 for (int idx=tid; idx<(BN*BK)/16; idx+=NTHREAD){ int r=(idx*16)/BK, cc=(idx*16)%BK; \
2720 __pipeline_memcpy_async(&Bs[buf][r*BK + (cc ^ SW(r))], &Bcol[(block_col+r)*K + _k0+cc], 16); } \
2721 __pipeline_commit(); } while(0)
2722 #pragma unroll
2723 for (int i=0;i<STAGES-1;++i){ if(i<nstages) LOAD_STAGE(i,i); else __pipeline_commit(); }
2724 __pipeline_wait_prior(STAGES-2);
2725 __syncthreads();
2726 LDA(0, 0); // ldmatrix stage 0 -> af[0]/bf[0]
2727 for (int s=0; s<nstages; ++s) {
2728 int cur = s & 1, nxt = (s+1) & 1;
2729 if (s+1 < nstages) {
2730 __pipeline_wait_prior(STAGES-2);
2731 __syncthreads();
2732 LDA((s+1)%STAGES, nxt); // prefetch next fragments (overlaps the mma below)
2733 }
2734 int fetch = s+STAGES-1; if (fetch<nstages) LOAD_STAGE(fetch%STAGES, fetch); else __pipeline_commit();
2735 #pragma unroll
2736 for (int mi=0; mi<WM/16; ++mi)
2737 #pragma unroll
2738 for (int ni=0; ni<WN/8; ++ni)
2739 asm volatile("mma.sync.aligned.m16n8k32.row.col.s32.s8.s8.s32 {%0,%1,%2,%3},{%4,%5,%6,%7},{%8,%9},{%0,%1,%2,%3};"
2740 :"+r"(c[mi][ni][0]),"+r"(c[mi][ni][1]),"+r"(c[mi][ni][2]),"+r"(c[mi][ni][3])
2741 :"r"(af[cur][mi][0]),"r"(af[cur][mi][1]),"r"(af[cur][mi][2]),"r"(af[cur][mi][3]),"r"(bf[cur][ni][0]),"r"(bf[cur][ni][1]));
2742 }
2743 #pragma unroll
2744 for (int mi=0; mi<WM/16; ++mi)
2745 #pragma unroll
2746 for (int ni=0; ni<WN/8; ++ni) {
2747 int rb=block_row+warp_row+mi*16, cb=block_col+warp_col+ni*8;
2748 C[(rb+gid)*N+cb+tg*2]=c[mi][ni][0]; C[(rb+gid)*N+cb+tg*2+1]=c[mi][ni][1];
2749 C[(rb+gid+8)*N+cb+tg*2]=c[mi][ni][2]; C[(rb+gid+8)*N+cb+tg*2+1]=c[mi][ni][3];
2750 }
2751}
2752"#;
2753
2754pub fn emit_mma_tn_k32swz_kernel(
2757 bm: usize,
2758 bn: usize,
2759 warps_m: usize,
2760 warps_n: usize,
2761 stages: usize,
2762) -> String {
2763 let defs = format!("#define BM {bm}\n#define BN {bn}\n#define BK 32\n#define WARPS_M {warps_m}\n#define WARPS_N {warps_n}\n#define STAGES {stages}\n");
2764 format!("{defs}{}", MMA_TN_K32SWZ_BODY)
2765}
2766
2767pub fn emit_mma_tn_k64_bench(
2773 bm: usize,
2774 bn: usize,
2775 warps_m: usize,
2776 warps_n: usize,
2777 stages: usize,
2778 m: usize,
2779 k: usize,
2780 n: usize,
2781) -> String {
2782 mma_tn_k64_harness(bm, bn, warps_m, warps_n, stages, m, k, n, true)
2783}
2784pub fn emit_mma_tn_k64_program(
2785 bm: usize,
2786 bn: usize,
2787 warps_m: usize,
2788 warps_n: usize,
2789 stages: usize,
2790 m: usize,
2791 k: usize,
2792 n: usize,
2793) -> String {
2794 mma_tn_k64_harness(bm, bn, warps_m, warps_n, stages, m, k, n, false)
2795}
2796
2797fn mma_tn_k64_harness(
2798 bm: usize,
2799 bn: usize,
2800 warps_m: usize,
2801 warps_n: usize,
2802 stages: usize,
2803 m: usize,
2804 k: usize,
2805 n: usize,
2806 bench: bool,
2807) -> String {
2808 let defs = format!("#define BM {bm}\n#define BN {bn}\n#define BK 64\n#define WARPS_M {warps_m}\n#define WARPS_N {warps_n}\n#define STAGES {stages}\n");
2809 let kernel = format!("{defs}{}", MMA_TN_K64_BODY);
2810 let threads = warps_m * warps_n * 32;
2811 let body = if bench {
2812 format!(
2813 r#"
2814#include <cstdio>
2815#include <cstdlib>
2816int main(){{ const int M={m},K={k},N={n};
2817 signed char *dA,*dB; int *dC;
2818 cudaMalloc(&dA,(size_t)M*K);cudaMalloc(&dB,(size_t)K*N);cudaMalloc(&dC,(size_t)M*N*4);
2819 cudaMemset(dA,1,(size_t)M*K);cudaMemset(dB,1,(size_t)K*N);
2820 dim3 grid(N/{bn},M/{bm});
2821 gemm_mma_tn_k64<<<grid,{threads}>>>(dA,dB,dC,M,N,K);
2822 cudaError_t err=cudaDeviceSynchronize(); if(err){{printf("LAUNCH_ERROR %s\n",cudaGetErrorString(err));return 1;}}
2823 int *hC=(int*)malloc((size_t)M*N*4); cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
2824 long step=((long)M*N)/4096+1; for(long i=0;i<(long)M*N;i+=step) if(hC[i]!=K){{printf("INCORRECT %ld: %d!=%d\n",i,hC[i],K);return 1;}}
2825 cudaEvent_t s,e; cudaEventCreate(&s); cudaEventCreate(&e); int IT=30; float total=0;
2826 for(int it=0;it<IT;it++){{ cudaEventRecord(s); gemm_mma_tn_k64<<<grid,{threads}>>>(dA,dB,dC,M,N,K); cudaEventRecord(e); cudaEventSynchronize(e); float ms=0; cudaEventElapsedTime(&ms,s,e); total+=ms; }}
2827 printf("GOPS %.1f\n", 2.0*M*N*K/((total/IT)/1e3)/1e9); return 0; }}
2828"#,
2829 bn = bn,
2830 bm = bm,
2831 threads = threads,
2832 m = m,
2833 k = k,
2834 n = n
2835 )
2836 } else {
2837 format!(
2838 r#"
2839#include <cstdio>
2840#include <cstdlib>
2841int main(){{ const int M={m},K={k},N={n};
2842 signed char *hA=(signed char*)malloc((size_t)M*K),*hB=(signed char*)malloc((size_t)K*N);
2843 for(long i=0;i<(long)M*K;++i) hA[i]=(signed char)(((i*7+3)%251)-125);
2844 for(long nn=0;nn<N;++nn) for(long kk=0;kk<K;++kk){{ long bkn=(long)kk*N+nn; hB[nn*K+kk]=(signed char)(((bkn*5+1)%251)-125); }}
2845 signed char *dA,*dB; int *dC,*hC=(int*)malloc((size_t)M*N*4);
2846 cudaMalloc(&dA,(size_t)M*K);cudaMalloc(&dB,(size_t)K*N);cudaMalloc(&dC,(size_t)M*N*4);
2847 cudaMemcpy(dA,hA,(size_t)M*K,cudaMemcpyHostToDevice);cudaMemcpy(dB,hB,(size_t)K*N,cudaMemcpyHostToDevice);
2848 dim3 grid(N/{bn},M/{bm}); gemm_mma_tn_k64<<<grid,{threads}>>>(dA,dB,dC,M,N,K);
2849 cudaError_t e=cudaDeviceSynchronize(); if(e){{printf("ERR %s\n",cudaGetErrorString(e));return 1;}}
2850 cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
2851 for(long i=0;i<(long)M*N;++i) printf("%d\n",hC[i]); return 0; }}
2852"#,
2853 bn = bn,
2854 bm = bm,
2855 threads = threads,
2856 m = m,
2857 k = k,
2858 n = n
2859 )
2860 };
2861 format!("{kernel}{body}")
2862}
2863
2864const MMA_TN_K64_BODY: &str = r#"#include <cuda_pipeline.h>
2865#define WM (BM/WARPS_M)
2866#define WN (BN/WARPS_N)
2867#define NTHREAD (WARPS_M*WARPS_N*32)
2868#define SW(row) ((((row)>>1)&3)*16)
2869extern "C" __global__ void gemm_mma_tn_k64(const signed char* A, const signed char* Bcol, int* C, int M, int N, int K){
2870 __shared__ signed char As[STAGES][BM*BK]; __shared__ signed char Bs[STAGES][BN*BK];
2871 const int tid=threadIdx.x,warpId=tid/32,lane=tid%32; const int gid=lane/4,tg=lane%4;
2872 const int warp_row=(warpId/WARPS_N)*WM,warp_col=(warpId%WARPS_N)*WN;
2873 const int block_row=blockIdx.y*BM,block_col=blockIdx.x*BN; const int nstages=K/BK;
2874 int c[WM/16][WN/8][4];
2875 for(int mi=0;mi<WM/16;++mi)for(int ni=0;ni<WN/8;++ni){c[mi][ni][0]=c[mi][ni][1]=c[mi][ni][2]=c[mi][ni][3]=0;}
2876 #define LS(buf,st) do{int _k0=(st)*BK; for(int idx=tid;idx<(BM*BK)/16;idx+=NTHREAD){int r=(idx*16)/BK,cc=(idx*16)%BK;__pipeline_memcpy_async(&As[buf][r*BK+(cc^SW(r))],&A[(block_row+r)*K+_k0+cc],16);} for(int idx=tid;idx<(BN*BK)/16;idx+=NTHREAD){int r=(idx*16)/BK,cc=(idx*16)%BK;__pipeline_memcpy_async(&Bs[buf][r*BK+(cc^SW(r))],&Bcol[(block_col+r)*K+_k0+cc],16);}__pipeline_commit();}while(0)
2877 for(int i=0;i<STAGES-1;++i){if(i<nstages)LS(i,i);else __pipeline_commit();}
2878 for(int s=0;s<nstages;++s){__pipeline_wait_prior(STAGES-2);__syncthreads();int buf=s%STAGES;
2879 // ILP hoist: load BOTH k-steps' fragments, THEN issue all mma — so the 2nd k-step's ldmatrix overlaps the
2880 // 1st k-step's tensor-core work (ptxas keeps 222 regs, no spill). +1 pt of cuBLAS parity (90.2->91.2%).
2881 { int a[2][WM/16][4],b[2][WN/8][2];
2882 #pragma unroll
2883 for(int ks=0;ks<2;ks++){int kk=ks*32;
2884 for(int mi=0;mi<WM/16;++mi){int mtx=lane/8,row=lane%8;int shrow=warp_row+mi*16+(mtx&1)*8+row;int koff=kk+(mtx>>1)*16;unsigned aA=(unsigned)__cvta_generic_to_shared(&As[buf][shrow*BK+(koff^SW(shrow))]);asm volatile("ldmatrix.sync.aligned.m8n8.x4.shared.b16 {%0,%1,%2,%3},[%4];":"=r"(a[ks][mi][0]),"=r"(a[ks][mi][1]),"=r"(a[ks][mi][2]),"=r"(a[ks][mi][3]):"r"(aA));}
2885 for(int ni=0;ni<WN/8;++ni){int mtx=(lane>>3)&1,row=lane&7;int shrow=warp_col+ni*8+row;int koff=kk+mtx*16;unsigned bA=(unsigned)__cvta_generic_to_shared(&Bs[buf][shrow*BK+(koff^SW(shrow))]);asm volatile("ldmatrix.sync.aligned.m8n8.x2.shared.b16 {%0,%1},[%2];":"=r"(b[ks][ni][0]),"=r"(b[ks][ni][1]):"r"(bA));}
2886 }
2887 #pragma unroll
2888 for(int ks=0;ks<2;ks++)for(int mi=0;mi<WM/16;++mi)for(int ni=0;ni<WN/8;++ni)asm volatile("mma.sync.aligned.m16n8k32.row.col.s32.s8.s8.s32 {%0,%1,%2,%3},{%4,%5,%6,%7},{%8,%9},{%0,%1,%2,%3};":"+r"(c[mi][ni][0]),"+r"(c[mi][ni][1]),"+r"(c[mi][ni][2]),"+r"(c[mi][ni][3]):"r"(a[ks][mi][0]),"r"(a[ks][mi][1]),"r"(a[ks][mi][2]),"r"(a[ks][mi][3]),"r"(b[ks][ni][0]),"r"(b[ks][ni][1]));
2889 }
2890 // No post-mma __syncthreads: the next iteration's pre-ldmatrix barrier already orders every thread's read
2891 // of this buffer before the cp.async that overwrites it (verified byte-exact vs the oracle). One fewer
2892 // barrier per stage => fewer overhead cycles between IMMA bursts => +2.6 pts of cuBLAS parity (87.6->90.2%).
2893 int nxt=s+STAGES-1;if(nxt<nstages)LS(nxt%STAGES,nxt);else __pipeline_commit();}
2894 for(int mi=0;mi<WM/16;++mi)for(int ni=0;ni<WN/8;++ni){int rb=block_row+warp_row+mi*16,cb=block_col+warp_col+ni*8;C[(rb+gid)*N+cb+tg*2]=c[mi][ni][0];C[(rb+gid)*N+cb+tg*2+1]=c[mi][ni][1];C[(rb+gid+8)*N+cb+tg*2]=c[mi][ni][2];C[(rb+gid+8)*N+cb+tg*2+1]=c[mi][ni][3];}
2895}
2896"#;
2897
2898pub fn emit_mma_tn_k64_kernel(
2901 bm: usize,
2902 bn: usize,
2903 warps_m: usize,
2904 warps_n: usize,
2905 stages: usize,
2906) -> String {
2907 let defs = format!("#define BM {bm}\n#define BN {bn}\n#define BK 64\n#define WARPS_M {warps_m}\n#define WARPS_N {warps_n}\n#define STAGES {stages}\n");
2908 format!("{defs}{}", MMA_TN_K64_BODY)
2909}
2910
2911#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2918pub struct CutlassI8Gemm {
2919 pub tb_m: usize,
2920 pub tb_n: usize,
2921 pub tb_k: usize,
2922 pub warp_m: usize,
2923 pub warp_n: usize,
2924 pub warp_k: usize,
2925 pub inst_m: usize,
2926 pub inst_n: usize,
2927 pub inst_k: usize,
2928 pub stages: usize,
2929}
2930
2931impl CutlassI8Gemm {
2932 pub fn cublas_reference() -> Self {
2935 Self {
2936 tb_m: 256,
2937 tb_n: 128,
2938 tb_k: 64,
2939 warp_m: 64,
2940 warp_n: 64,
2941 warp_k: 64,
2942 inst_m: 16,
2943 inst_n: 8,
2944 inst_k: 32,
2945 stages: 3,
2946 }
2947 }
2948 pub fn is_legal(&self) -> bool {
2950 self.tb_m % self.warp_m == 0
2951 && self.tb_n % self.warp_n == 0
2952 && self.tb_k % self.warp_k == 0
2953 && self.warp_m % self.inst_m == 0
2954 && self.warp_n % self.inst_n == 0
2955 && self.warp_k % self.inst_k == 0
2956 && self.stages >= 2
2957 }
2958}
2959
2960#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2964pub enum CutlassEpilogue {
2965 LinearCombination,
2967 Relu,
2969}
2970
2971impl CutlassEpilogue {
2972 fn cutlass(&self) -> (&'static str, &'static str) {
2974 match self {
2975 CutlassEpilogue::LinearCombination => (
2976 "#include <cutlass/epilogue/thread/linear_combination.h>\n",
2977 "cutlass::epilogue::thread::LinearCombination<int32_t,4,int32_t,int32_t>",
2978 ),
2979 CutlassEpilogue::Relu => (
2980 "#include <cutlass/epilogue/thread/linear_combination_relu.h>\n",
2981 "cutlass::epilogue::thread::LinearCombinationRelu<int32_t,4,int32_t,int32_t>",
2982 ),
2983 }
2984 }
2985}
2986
2987pub fn emit_cutlass_i8_gemm_ep(cfg: &CutlassI8Gemm, ep: CutlassEpilogue) -> String {
2991 let (ep_inc, ep_ty) = ep.cutlass();
2992 format!(
2993 r#"#include <cutlass/gemm/device/gemm.h>
2994{ep_inc}using Gemm = cutlass::gemm::device::Gemm<
2995 int8_t, cutlass::layout::RowMajor,
2996 int8_t, cutlass::layout::ColumnMajor,
2997 int32_t, cutlass::layout::RowMajor,
2998 int32_t, cutlass::arch::OpClassTensorOp, cutlass::arch::Sm80,
2999 cutlass::gemm::GemmShape<{tbm},{tbn},{tbk}>,
3000 cutlass::gemm::GemmShape<{wm},{wn},{wk}>,
3001 cutlass::gemm::GemmShape<{im},{inn},{ik}>,
3002 {ep_ty},
3003 cutlass::gemm::threadblock::GemmIdentityThreadblockSwizzle<>,
3004 {st}>;
3005extern "C" int run_gemm_k(const signed char* A, const signed char* B, int* C, int M, int N, int K){{
3006 Gemm gemm; Gemm::Arguments args({{M,N,K}},{{(int8_t*)A,K}},{{(int8_t*)B,K}},{{C,N}},{{C,N}},{{1,0}});
3007 return (int)gemm(args);
3008}}
3009"#,
3010 ep_inc = ep_inc,
3011 ep_ty = ep_ty,
3012 tbm = cfg.tb_m,
3013 tbn = cfg.tb_n,
3014 tbk = cfg.tb_k,
3015 wm = cfg.warp_m,
3016 wn = cfg.warp_n,
3017 wk = cfg.warp_k,
3018 im = cfg.inst_m,
3019 inn = cfg.inst_n,
3020 ik = cfg.inst_k,
3021 st = cfg.stages,
3022 )
3023}
3024
3025pub fn emit_cutlass_i8_gemm(cfg: &CutlassI8Gemm) -> String {
3027 emit_cutlass_i8_gemm_ep(cfg, CutlassEpilogue::LinearCombination)
3028}
3029
3030pub fn emit_cutlass_i8_gemm_relu_bench(
3034 cfg: &CutlassI8Gemm,
3035 m: usize,
3036 k: usize,
3037 n: usize,
3038) -> String {
3039 let kernel = emit_cutlass_i8_gemm_ep(cfg, CutlassEpilogue::Relu);
3040 format!(
3041 r#"{kernel}#include <cublas_v2.h>
3042#include <cstdio>
3043#include <cstdlib>
3044__global__ void relu_kernel(int* C, long nel){{long i=(long)blockIdx.x*blockDim.x+threadIdx.x; if(i<nel) C[i]=C[i]>0?C[i]:0;}}
3045static double bst(float*x,int c){{double b=1e30;for(int i=0;i<c;i++)if(x[i]<b)b=x[i];return b;}}
3046int main(){{const int M={m},K={k},N={n};signed char*dA,*dB;int*dC,*dC2;
3047 cudaMalloc(&dA,(size_t)M*K);cudaMalloc(&dB,(size_t)K*N);cudaMalloc(&dC,(size_t)M*N*4);cudaMalloc(&dC2,(size_t)M*N*4);
3048 signed char*hA=(signed char*)malloc((size_t)M*K),*hB=(signed char*)malloc((size_t)K*N);
3049 for(long i=0;i<(long)M*K;++i)hA[i]=(signed char)(((i*7+3)%251)-125);
3050 for(long nn=0;nn<N;++nn)for(long kk=0;kk<K;++kk){{long bkn=(long)kk*N+nn;hB[nn*K+kk]=(signed char)(((bkn*5+1)%251)-125);}}
3051 cudaMemcpy(dA,hA,(size_t)M*K,cudaMemcpyHostToDevice);cudaMemcpy(dB,hB,(size_t)K*N,cudaMemcpyHostToDevice);
3052 run_gemm_k(dA,dB,dC,M,N,K); if(cudaDeviceSynchronize()){{printf("ERR\n");return 1;}}
3053 int*hC=(int*)malloc((size_t)M*N*4);cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
3054 for(long q=0;q<12;q++){{long i=(q*7919)%M,j=(q*104729)%N;long acc=0;for(long kk=0;kk<K;kk++){{long av=((i*K+kk)*7+3)%251-125;long bv=((kk*N+j)*5+1)%251-125;acc+=av*bv;}}long want=acc>0?acc:0;if(hC[i*N+j]!=want){{printf("INCORRECT\n");return 1;}}}}
3055 cublasHandle_t h;cublasCreate(&h);cublasSetMathMode(h,CUBLAS_TENSOR_OP_MATH);int al=1,be=0;
3056 long nel=(long)M*N; int TPB=256; long blk=(nel+TPB-1)/TPB;
3057 cudaEvent_t s,ev;cudaEventCreate(&s);cudaEventCreate(&ev);int IT=100;float mf[100],mb[100];
3058 for(int i=0;i<10;i++){{run_gemm_k(dA,dB,dC,M,N,K);cublasGemmEx(h,CUBLAS_OP_T,CUBLAS_OP_N,N,M,K,&al,dB,CUDA_R_8I,K,dA,CUDA_R_8I,K,&be,dC2,CUDA_R_32I,N,CUBLAS_COMPUTE_32I,CUBLAS_GEMM_DEFAULT_TENSOR_OP);relu_kernel<<<blk,TPB>>>(dC2,nel);}}
3059 cudaDeviceSynchronize();
3060 for(int i=0;i<IT;i++){{
3061 cudaEventRecord(s);run_gemm_k(dA,dB,dC,M,N,K);cudaEventRecord(ev);cudaEventSynchronize(ev);cudaEventElapsedTime(&mf[i],s,ev);
3062 cudaEventRecord(s);cublasGemmEx(h,CUBLAS_OP_T,CUBLAS_OP_N,N,M,K,&al,dB,CUDA_R_8I,K,dA,CUDA_R_8I,K,&be,dC2,CUDA_R_32I,N,CUBLAS_COMPUTE_32I,CUBLAS_GEMM_DEFAULT_TENSOR_OP);relu_kernel<<<blk,TPB>>>(dC2,nel);cudaEventRecord(ev);cudaEventSynchronize(ev);cudaEventElapsedTime(&mb[i],s,ev);}}
3063 int*hC2=(int*)malloc((size_t)M*N*4);cudaMemcpy(hC2,dC2,(size_t)M*N*4,cudaMemcpyDeviceToHost);
3064 for(long q=0;q<8;q++){{long idx=(q*99991)%nel;if(hC[idx]!=hC2[idx]){{printf("MISMATCH\n");return 1;}}}}
3065 double tf=bst(mf,IT),tb=bst(mb,IT);
3066 printf("FUSED ours %.3f ms cublas+relu %.3f ms SPEEDUP %.3fx\n",tf,tb,tb/tf);return 0;}}
3067"#,
3068 kernel = kernel,
3069 m = m,
3070 k = k,
3071 n = n
3072 )
3073}
3074
3075#[derive(Clone, Copy, Debug, PartialEq, Eq)]
3083pub struct CutlassNvfp4Gemm {
3084 pub tile_m: usize,
3085 pub tile_n: usize,
3086 pub tile_k: usize,
3087 pub cluster_m: usize,
3088 pub cluster_n: usize,
3089}
3090
3091impl CutlassNvfp4Gemm {
3092 pub fn b200_default() -> Self {
3094 Self {
3095 tile_m: 256,
3096 tile_n: 256,
3097 tile_k: 256,
3098 cluster_m: 2,
3099 cluster_n: 1,
3100 }
3101 }
3102}
3103
3104pub fn emit_cutlass_nvfp4_gemm(cfg: &CutlassNvfp4Gemm) -> String {
3107 format!(
3108 r#"#include <torch/extension.h>
3109#include <cutlass/cutlass.h>
3110#include <cutlass/float_subbyte.h>
3111#include <cutlass/gemm/collective/collective_builder.hpp>
3112#include <cutlass/epilogue/collective/collective_builder.hpp>
3113#include <cutlass/gemm/device/gemm_universal_adapter.h>
3114#include <cutlass/gemm/kernel/gemm_universal.hpp>
3115using namespace cute;
3116using ElementA = cutlass::float_e2m1_t;
3117using ElementB = cutlass::float_e2m1_t;
3118using ElementSF = cutlass::float_ue4m3_t;
3119using ElementC = cutlass::half_t;
3120using ElementAcc = float;
3121using LayoutA = cutlass::layout::RowMajor;
3122using LayoutB = cutlass::layout::ColumnMajor;
3123using LayoutC = cutlass::layout::RowMajor;
3124using ArchTag = cutlass::arch::Sm100;
3125using OpClass = cutlass::arch::OpClassBlockScaledTensorOp;
3126using TileShape = Shape<_{tm}, _{tn}, _{tk}>;
3127using ClusterShape = Shape<_{cm}, _{cn}, _1>;
3128using CollectiveEpilogue = typename cutlass::epilogue::collective::CollectiveBuilder<
3129 ArchTag, OpClass, TileShape, ClusterShape,
3130 cutlass::epilogue::collective::EpilogueTileAuto, ElementAcc, ElementAcc,
3131 ElementC, LayoutC, 8, ElementC, LayoutC, 8,
3132 cutlass::epilogue::collective::EpilogueScheduleAuto>::CollectiveOp;
3133using CollectiveMainloop = typename cutlass::gemm::collective::CollectiveBuilder<
3134 ArchTag, OpClass,
3135 cute::tuple<ElementA, ElementSF>, LayoutA, 32,
3136 cute::tuple<ElementB, ElementSF>, LayoutB, 32,
3137 ElementAcc, TileShape, ClusterShape,
3138 cutlass::gemm::collective::StageCountAutoCarveout<(int)sizeof(typename CollectiveEpilogue::SharedStorage)>,
3139 cutlass::gemm::collective::KernelScheduleAuto>::CollectiveOp;
3140using GemmKernel = cutlass::gemm::kernel::GemmUniversal<Shape<int,int,int,int>, CollectiveMainloop, CollectiveEpilogue>;
3141using Gemm = cutlass::gemm::device::GemmUniversalAdapter<GemmKernel>;
3142torch::Tensor run_nvfp4_gemm(torch::Tensor a, torch::Tensor b, torch::Tensor sfa, torch::Tensor sfb, torch::Tensor c, int64_t M, int64_t N, int64_t K, int64_t L){{
3143 for(int64_t l=0; l<L; ++l){{
3144 Gemm gemm;
3145 typename Gemm::Arguments args{{
3146 cutlass::gemm::GemmUniversalMode::kGemm, {{(int)M,(int)N,(int)K,1}},
3147 {{ (ElementA*)a[l].data_ptr(), (ElementSF*)sfa[l].data_ptr(),
3148 (ElementB*)b[l].data_ptr(), (ElementSF*)sfb[l].data_ptr() }},
3149 {{ {{1.0f, 0.0f}}, nullptr, {{}}, (ElementC*)c[l].data_ptr(), {{}} }} }};
3150 auto st = gemm.run(args);
3151 TORCH_CHECK(st==cutlass::Status::kSuccess, "nvfp4 gemm slice failed");
3152 }}
3153 return c;
3154}}
3155"#,
3156 tm = cfg.tile_m,
3157 tn = cfg.tile_n,
3158 tk = cfg.tile_k,
3159 cm = cfg.cluster_m,
3160 cn = cfg.cluster_n
3161 )
3162}
3163
3164pub fn emit_cutlass_nvfp4_group_gemm(cfg: &CutlassNvfp4Gemm) -> String {
3169 format!(
3170 r#"#include <torch/extension.h>
3171#include <vector>
3172#include <cutlass/cutlass.h>
3173#include <cutlass/float_subbyte.h>
3174#include <cutlass/gemm/group_array_problem_shape.hpp>
3175#include <cutlass/gemm/collective/collective_builder.hpp>
3176#include <cutlass/epilogue/collective/collective_builder.hpp>
3177#include <cutlass/gemm/device/gemm_universal_adapter.h>
3178#include <cutlass/gemm/kernel/gemm_universal.hpp>
3179using namespace cute;
3180using ElementA=cutlass::float_e2m1_t; using ElementB=cutlass::float_e2m1_t;
3181using ElementSF=cutlass::float_ue4m3_t; using ElementC=cutlass::half_t; using ElementAcc=float;
3182using LayoutA=cutlass::layout::RowMajor; using LayoutB=cutlass::layout::ColumnMajor; using LayoutC=cutlass::layout::RowMajor;
3183using ArchTag=cutlass::arch::Sm100; using OpClass=cutlass::arch::OpClassBlockScaledTensorOp;
3184using ProblemShape=cutlass::gemm::GroupProblemShape<Shape<int,int,int>>;
3185using TileShape=Shape<_{tm},_{tn},_{tk}>; using ClusterShape=Shape<_{cm},_{cn},_1>;
3186using CollectiveEpilogue = typename cutlass::epilogue::collective::CollectiveBuilder<
3187 ArchTag,OpClass,TileShape,ClusterShape,cutlass::epilogue::collective::EpilogueTileAuto,ElementAcc,ElementAcc,
3188 ElementC,LayoutC,8,ElementC,LayoutC,8,cutlass::epilogue::collective::EpilogueScheduleAuto>::CollectiveOp;
3189using CollectiveMainloop = typename cutlass::gemm::collective::CollectiveBuilder<
3190 ArchTag,OpClass,cute::tuple<ElementA,ElementSF>,LayoutA,32,cute::tuple<ElementB,ElementSF>,LayoutB,32,
3191 ElementAcc,TileShape,ClusterShape,
3192 cutlass::gemm::collective::StageCountAutoCarveout<(int)sizeof(typename CollectiveEpilogue::SharedStorage)>,
3193 cutlass::gemm::collective::KernelScheduleAuto>::CollectiveOp;
3194using GemmKernel=cutlass::gemm::kernel::GemmUniversal<ProblemShape,CollectiveMainloop,CollectiveEpilogue>;
3195using Gemm=cutlass::gemm::device::GemmUniversalAdapter<GemmKernel>;
3196// One grouped launch over all experts. Pointer/scale/problem arrays are device buffers the host fills per group.
3197void run_nvfp4_group_gemm(std::vector<torch::Tensor> a, std::vector<torch::Tensor> b,
3198 std::vector<torch::Tensor> sfa, std::vector<torch::Tensor> sfb,
3199 std::vector<torch::Tensor> c, std::vector<std::vector<int64_t>> sizes){{
3200 int G=(int)a.size();
3201 std::vector<ProblemShape::UnderlyingProblemShape> ps(G);
3202 std::vector<const ElementA*> pa(G); std::vector<const ElementB*> pb(G);
3203 std::vector<const ElementSF*> psa(G), psb(G); std::vector<ElementC*> pc(G);
3204 for(int i=0;i<G;++i){{ ps[i]={{(int)sizes[i][0],(int)sizes[i][1],(int)sizes[i][2]}};
3205 pa[i]=(const ElementA*)a[i].data_ptr(); pb[i]=(const ElementB*)b[i].data_ptr();
3206 psa[i]=(const ElementSF*)sfa[i].data_ptr(); psb[i]=(const ElementSF*)sfb[i].data_ptr(); pc[i]=(ElementC*)c[i].data_ptr(); }}
3207 Gemm gemm;
3208 typename Gemm::Arguments args{{ cutlass::gemm::GemmUniversalMode::kGrouped, {{G, ps.data(), nullptr}},
3209 {{ pa.data(), psa.data(), pb.data(), psb.data() }},
3210 {{ {{1.0f,0.0f}}, nullptr, {{}}, pc.data(), {{}} }} }};
3211 TORCH_CHECK(gemm.run(args)==cutlass::Status::kSuccess,"nvfp4 grouped gemm");
3212}}
3213"#,
3214 tm = cfg.tile_m,
3215 tn = cfg.tile_n,
3216 tk = cfg.tile_k,
3217 cm = cfg.cluster_m,
3218 cn = cfg.cluster_n
3219 )
3220}
3221
3222pub fn emit_cutlass_i8_gemm_bench(cfg: &CutlassI8Gemm, m: usize, k: usize, n: usize) -> String {
3225 let kernel = emit_cutlass_i8_gemm(cfg);
3226 format!(
3227 r#"{kernel}#include <cublas_v2.h>
3228#include <cstdio>
3229#include <cstdlib>
3230static double bst(float*x,int c){{double b=1e30;for(int i=0;i<c;i++)if(x[i]<b)b=x[i];return b;}}
3231int main(){{const int M={m},K={k},N={n};signed char*dA,*dB;int*dC;
3232 cudaMalloc(&dA,(size_t)M*K);cudaMalloc(&dB,(size_t)K*N);cudaMalloc(&dC,(size_t)M*N*4);
3233 signed char*hA=(signed char*)malloc((size_t)M*K),*hB=(signed char*)malloc((size_t)K*N);
3234 for(long i=0;i<(long)M*K;++i)hA[i]=(signed char)(((i*7+3)%251)-125);
3235 for(long nn=0;nn<N;++nn)for(long kk=0;kk<K;++kk){{long bkn=(long)kk*N+nn;hB[nn*K+kk]=(signed char)(((bkn*5+1)%251)-125);}}
3236 cudaMemcpy(dA,hA,(size_t)M*K,cudaMemcpyHostToDevice);cudaMemcpy(dB,hB,(size_t)K*N,cudaMemcpyHostToDevice);
3237 if(run_gemm_k(dA,dB,dC,M,N,K)!=0){{printf("CUTLASS_ERR\n");return 1;}}
3238 if(cudaDeviceSynchronize()){{printf("ERR\n");return 1;}}
3239 int*hC=(int*)malloc((size_t)M*N*4);cudaMemcpy(hC,dC,(size_t)M*N*4,cudaMemcpyDeviceToHost);
3240 for(long q=0;q<12;q++){{long i=(q*7919)%M,j=(q*104729)%N;long acc=0;for(long kk=0;kk<K;kk++){{long av=((i*K+kk)*7+3)%251-125;long bv=((kk*N+j)*5+1)%251-125;acc+=av*bv;}}if(hC[i*N+j]!=acc){{printf("INCORRECT\n");return 1;}}}}
3241 cudaMemset(dA,1,(size_t)M*K);cudaMemset(dB,1,(size_t)K*N);
3242 cublasHandle_t h;cublasCreate(&h);cublasSetMathMode(h,CUBLAS_TENSOR_OP_MATH);int al=1,be=0;
3243 cudaEvent_t s,ev;cudaEventCreate(&s);cudaEventCreate(&ev);int IT=100;float mo[100],mc[100];
3244 for(int i=0;i<10;i++){{run_gemm_k(dA,dB,dC,M,N,K);cublasGemmEx(h,CUBLAS_OP_T,CUBLAS_OP_N,N,M,K,&al,dB,CUDA_R_8I,K,dA,CUDA_R_8I,K,&be,dC,CUDA_R_32I,N,CUBLAS_COMPUTE_32I,CUBLAS_GEMM_DEFAULT_TENSOR_OP);}}
3245 cudaDeviceSynchronize();
3246 for(int i=0;i<IT;i++){{cudaEventRecord(s);run_gemm_k(dA,dB,dC,M,N,K);cudaEventRecord(ev);cudaEventSynchronize(ev);cudaEventElapsedTime(&mo[i],s,ev);
3247 cudaEventRecord(s);cublasGemmEx(h,CUBLAS_OP_T,CUBLAS_OP_N,N,M,K,&al,dB,CUDA_R_8I,K,dA,CUDA_R_8I,K,&be,dC,CUDA_R_32I,N,CUBLAS_COMPUTE_32I,CUBLAS_GEMM_DEFAULT_TENSOR_OP);cudaEventRecord(ev);cudaEventSynchronize(ev);cudaEventElapsedTime(&mc[i],s,ev);}}
3248 double go=2.0*M*N*K/(bst(mo,IT)/1e3)/1e9,gc=2.0*M*N*K/(bst(mc,IT)/1e3)/1e9;
3249 printf("PARITY %.1f OURS %.0f CUBLAS %.0f\n",100*go/gc,go,gc);return 0;}}
3250"#,
3251 kernel = kernel,
3252 m = m,
3253 k = k,
3254 n = n
3255 )
3256}