Allanatrix commited on
Commit
77d32ff
·
verified ·
1 Parent(s): 43ff7e4

Publish ada_tensor_core_fp16 kernel and performance card

Browse files

CUDA source, short description, performance table, and plot.

Files changed (3) hide show
  1. README.md +40 -0
  2. kernel.cu +327 -0
  3. performance.svg +8 -0
README.md ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: apache-2.0
3
+ tags:
4
+ - cuda
5
+ - kernel
6
+ - gpu-optimization
7
+ - hpc
8
+ ---
9
+
10
+ # ada_tensor_core_fp16
11
+
12
+ Ada Tensor Core FP16 GEMM prototype with WMMA-backed 32x64x16 correctness and timing harness.
13
+
14
+ This repository contains the standalone CUDA source for the `ada_tensor_core_fp16` lane from
15
+ the PyC kernel lab. It is a source artifact for inspection and benchmarking;
16
+ it is not a precompiled binary and the result below is not a universal ranking.
17
+
18
+ ## Performance
19
+
20
+ | Kernel | GPU / architecture | Shape | Best recorded result | Evidence |
21
+ |---|---|---|---|---|
22
+ | `ada_tensor_core_fp16` | not recorded | 1024x1024x1024 | 0.134 ms | Measured on not recorded, shape 1024x1024x1024; evidence `hopper-tensorcore-bringup-20260421T191817Z.json`. |
23
+
24
+ ![Performance plot](performance.svg)
25
+
26
+ The result is reported with the original campaign's timing and correctness
27
+ context. Compare kernels only when GPU, CUDA version, matrix shape, warmup,
28
+ repeats, and reference/correctness mode match.
29
+
30
+ ## Source
31
+
32
+ - `kernel.cu` — copied from `kernels/prototypes/ada/tensor_core/kernel.cu`.
33
+ - Original lane tags: `cuda, matmul, ada, sm89, prototype, tensor-core, fp16`.
34
+
35
+ ## Build/run contract
36
+
37
+ ```text
38
+ {nvcc} -O3 -std=c++17 -lineinfo -DPYC_ADA_TENSOR_CORE_USE_BF16=0 -gencode arch=compute_89,code=sm_89 -gencode arch=compute_89,code=compute_89 {source} -o {build_dir}/{name}
39
+ {build_dir}/{name} 1024 1024 1024 10 50
40
+ ```
kernel.cu ADDED
@@ -0,0 +1,327 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #include <cuda_runtime.h>
2
+ #include <mma.h>
3
+
4
+ #include <math.h>
5
+ #include <stdint.h>
6
+ #include <stdio.h>
7
+ #include <stdlib.h>
8
+
9
+ #if defined(PYC_ADA_TENSOR_CORE_USE_BF16) && PYC_ADA_TENSOR_CORE_USE_BF16
10
+ #include <cuda_bf16.h>
11
+ typedef __nv_bfloat16 pyc_tc_scalar_t;
12
+ #define PYC_TC_LANE_NAME "bf16"
13
+ static __host__ __device__ inline pyc_tc_scalar_t pyc_tc_make_scalar(float value) {
14
+ return __float2bfloat16(value);
15
+ }
16
+ static __host__ __device__ inline float pyc_tc_scalar_to_float(pyc_tc_scalar_t value) {
17
+ return __bfloat162float(value);
18
+ }
19
+ #else
20
+ #include <cuda_fp16.h>
21
+ typedef half pyc_tc_scalar_t;
22
+ #define PYC_TC_LANE_NAME "fp16"
23
+ static __host__ __device__ inline pyc_tc_scalar_t pyc_tc_make_scalar(float value) {
24
+ return __float2half(value);
25
+ }
26
+ static __host__ __device__ inline float pyc_tc_scalar_to_float(pyc_tc_scalar_t value) {
27
+ return __half2float(value);
28
+ }
29
+ #endif
30
+
31
+ namespace wmma = nvcuda::wmma;
32
+
33
+ #define PYC_TC_CTA_M 32
34
+ #define PYC_TC_CTA_N 64
35
+ #define PYC_TC_CTA_K 16
36
+ #define PYC_TC_WARPS_PER_BLOCK 8
37
+ #define PYC_TC_THREADS_PER_BLOCK 256
38
+
39
+ typedef struct {
40
+ int m;
41
+ int n;
42
+ int k;
43
+ int warmup;
44
+ int iters;
45
+ } pyc_tc_config;
46
+
47
+ static int check_cuda(cudaError_t status, const char* what) {
48
+ if (status != cudaSuccess) {
49
+ fprintf(stderr, "%s failed: %s\n", what, cudaGetErrorString(status));
50
+ return -1;
51
+ }
52
+ return 0;
53
+ }
54
+
55
+ static int parse_int_arg(const char* text, int* out_value) {
56
+ char* end = NULL;
57
+ long parsed;
58
+ if (!text || !out_value) {
59
+ return -1;
60
+ }
61
+ parsed = strtol(text, &end, 10);
62
+ if (end == text || *end != '\0' || parsed <= 0 || parsed > INT32_MAX) {
63
+ return -1;
64
+ }
65
+ *out_value = (int)parsed;
66
+ return 0;
67
+ }
68
+
69
+ static void fill_matrix(pyc_tc_scalar_t* data, int rows, int cols, float scale) {
70
+ int i;
71
+ for (i = 0; i < rows * cols; ++i) {
72
+ int pattern = (i * 19 + rows * 11 + cols * 7) % 29;
73
+ data[i] = pyc_tc_make_scalar(((float)pattern - 14.0f) * scale);
74
+ }
75
+ }
76
+
77
+ static void reference_gemm(
78
+ const pyc_tc_scalar_t* a,
79
+ const pyc_tc_scalar_t* b,
80
+ float* c,
81
+ int m,
82
+ int n,
83
+ int k) {
84
+ int row;
85
+ for (row = 0; row < m; ++row) {
86
+ int col;
87
+ for (col = 0; col < n; ++col) {
88
+ float acc = 0.0f;
89
+ int kk;
90
+ for (kk = 0; kk < k; ++kk) {
91
+ acc += pyc_tc_scalar_to_float(a[row * k + kk]) * pyc_tc_scalar_to_float(b[kk * n + col]);
92
+ }
93
+ c[row * n + col] = acc;
94
+ }
95
+ }
96
+ }
97
+
98
+ __launch_bounds__(PYC_TC_THREADS_PER_BLOCK, 2)
99
+ __global__ void pyc_tc_gemm_kernel(
100
+ const pyc_tc_scalar_t* __restrict__ a,
101
+ const pyc_tc_scalar_t* __restrict__ b,
102
+ float* __restrict__ c,
103
+ int m,
104
+ int n,
105
+ int k) {
106
+ __shared__ pyc_tc_scalar_t shared_a[PYC_TC_CTA_M][PYC_TC_CTA_K];
107
+ __shared__ pyc_tc_scalar_t shared_b[PYC_TC_CTA_N][PYC_TC_CTA_K];
108
+
109
+ const int warp_id = threadIdx.x / 32;
110
+ const int block_row = blockIdx.y * PYC_TC_CTA_M;
111
+ const int block_col = blockIdx.x * PYC_TC_CTA_N;
112
+ const int warp_row = (warp_id / 4) * 16;
113
+ const int warp_col = (warp_id % 4) * 16;
114
+ const int c_row = block_row + warp_row;
115
+ const int c_col = block_col + warp_col;
116
+
117
+ wmma::fragment<wmma::accumulator, 16, 16, 16, float> acc;
118
+ wmma::fill_fragment(acc, 0.0f);
119
+
120
+ if (warp_id >= PYC_TC_WARPS_PER_BLOCK) {
121
+ return;
122
+ }
123
+
124
+ for (int kk = 0; kk < k; kk += PYC_TC_CTA_K) {
125
+ int idx;
126
+
127
+ for (idx = threadIdx.x; idx < PYC_TC_CTA_M * PYC_TC_CTA_K; idx += blockDim.x) {
128
+ const int row = idx / PYC_TC_CTA_K;
129
+ const int col = idx % PYC_TC_CTA_K;
130
+ const int g_row = block_row + row;
131
+ const int g_col = kk + col;
132
+ if (g_row < m && g_col < k) {
133
+ shared_a[row][col] = a[g_row * k + g_col];
134
+ } else {
135
+ shared_a[row][col] = pyc_tc_make_scalar(0.0f);
136
+ }
137
+ }
138
+
139
+ for (idx = threadIdx.x; idx < PYC_TC_CTA_N * PYC_TC_CTA_K; idx += blockDim.x) {
140
+ const int row = idx / PYC_TC_CTA_K;
141
+ const int col = idx % PYC_TC_CTA_K;
142
+ const int g_row = kk + col;
143
+ const int g_col = block_col + row;
144
+ if (g_row < k && g_col < n) {
145
+ shared_b[row][col] = b[g_row * n + g_col];
146
+ } else {
147
+ shared_b[row][col] = pyc_tc_make_scalar(0.0f);
148
+ }
149
+ }
150
+
151
+ __syncthreads();
152
+
153
+ {
154
+ wmma::fragment<wmma::matrix_a, 16, 16, 16, pyc_tc_scalar_t, wmma::row_major> a_frag;
155
+ wmma::fragment<wmma::matrix_b, 16, 16, 16, pyc_tc_scalar_t, wmma::col_major> b_frag;
156
+ wmma::load_matrix_sync(a_frag, &shared_a[warp_row][0], PYC_TC_CTA_K);
157
+ wmma::load_matrix_sync(b_frag, &shared_b[warp_col][0], PYC_TC_CTA_K);
158
+ wmma::mma_sync(acc, a_frag, b_frag, acc);
159
+ }
160
+
161
+ __syncthreads();
162
+ }
163
+
164
+ if (c_row < m && c_col < n) {
165
+ wmma::store_matrix_sync(&c[c_row * n + c_col], acc, n, wmma::mem_row_major);
166
+ }
167
+ }
168
+
169
+ static int set_kernel_attributes(void) {
170
+ cudaError_t status;
171
+
172
+ status = cudaFuncSetAttribute(
173
+ pyc_tc_gemm_kernel,
174
+ cudaFuncAttributePreferredSharedMemoryCarveout,
175
+ 100);
176
+ if (status != cudaSuccess && status != cudaErrorNotSupported) {
177
+ fprintf(stderr, "cudaFuncSetAttribute failed: %s\n", cudaGetErrorString(status));
178
+ return -1;
179
+ }
180
+
181
+ return 0;
182
+ }
183
+
184
+ static int parse_config(int argc, char** argv, pyc_tc_config* cfg) {
185
+ if (!cfg) {
186
+ return -1;
187
+ }
188
+
189
+ cfg->m = 1024;
190
+ cfg->n = 1024;
191
+ cfg->k = 1024;
192
+ cfg->warmup = 10;
193
+ cfg->iters = 50;
194
+
195
+ if (argc > 1 && parse_int_arg(argv[1], &cfg->m) != 0) return -1;
196
+ if (argc > 2 && parse_int_arg(argv[2], &cfg->n) != 0) return -1;
197
+ if (argc > 3 && parse_int_arg(argv[3], &cfg->k) != 0) return -1;
198
+ if (argc > 4 && parse_int_arg(argv[4], &cfg->warmup) != 0) return -1;
199
+ if (argc > 5 && parse_int_arg(argv[5], &cfg->iters) != 0) return -1;
200
+
201
+ return 0;
202
+ }
203
+
204
+ int main(int argc, char** argv) {
205
+ pyc_tc_config cfg;
206
+ cudaDeviceProp props;
207
+ pyc_tc_scalar_t* host_a = NULL;
208
+ pyc_tc_scalar_t* host_b = NULL;
209
+ float* host_c = NULL;
210
+ float* ref_c = NULL;
211
+ pyc_tc_scalar_t* dev_a = NULL;
212
+ pyc_tc_scalar_t* dev_b = NULL;
213
+ float* dev_c = NULL;
214
+ cudaEvent_t start = NULL;
215
+ cudaEvent_t stop = NULL;
216
+ size_t a_bytes;
217
+ size_t b_bytes;
218
+ size_t c_bytes;
219
+ dim3 block;
220
+ dim3 grid;
221
+ float elapsed_ms = 0.0f;
222
+ double best_ms = 0.0;
223
+ int iter;
224
+ double max_abs_diff = 0.0;
225
+
226
+ if (parse_config(argc, argv, &cfg) != 0) {
227
+ fprintf(stderr, "usage: %s [m] [n] [k] [warmup] [iters]\n", argv[0]);
228
+ return 2;
229
+ }
230
+
231
+ if ((cfg.m % PYC_TC_CTA_M) != 0 || (cfg.n % PYC_TC_CTA_N) != 0 || (cfg.k % PYC_TC_CTA_K) != 0) {
232
+ fprintf(stderr, "Tensor Core lane requires %dx%dx%d-aligned shapes\n", PYC_TC_CTA_M, PYC_TC_CTA_N, PYC_TC_CTA_K);
233
+ return 2;
234
+ }
235
+
236
+ if (check_cuda(cudaGetDeviceProperties(&props, 0), "cudaGetDeviceProperties") != 0) {
237
+ return 1;
238
+ }
239
+ if (props.major < 8 || (props.major == 8 && props.minor < 9)) {
240
+ fprintf(stderr, "Ada Tensor Core prototype requires sm_89-class hardware\n");
241
+ return 1;
242
+ }
243
+
244
+ a_bytes = (size_t)cfg.m * (size_t)cfg.k * sizeof(pyc_tc_scalar_t);
245
+ b_bytes = (size_t)cfg.k * (size_t)cfg.n * sizeof(pyc_tc_scalar_t);
246
+ c_bytes = (size_t)cfg.m * (size_t)cfg.n * sizeof(float);
247
+
248
+ host_a = (pyc_tc_scalar_t*)malloc(a_bytes);
249
+ host_b = (pyc_tc_scalar_t*)malloc(b_bytes);
250
+ host_c = (float*)malloc(c_bytes);
251
+ ref_c = (float*)malloc(c_bytes);
252
+ if (!host_a || !host_b || !host_c || !ref_c) {
253
+ fprintf(stderr, "host allocation failed\n");
254
+ return 1;
255
+ }
256
+
257
+ fill_matrix(host_a, cfg.m, cfg.k, 0.03125f);
258
+ fill_matrix(host_b, cfg.k, cfg.n, 0.0625f);
259
+ reference_gemm(host_a, host_b, ref_c, cfg.m, cfg.n, cfg.k);
260
+
261
+ if (check_cuda(cudaMalloc((void**)&dev_a, a_bytes), "cudaMalloc(a)") != 0) return 1;
262
+ if (check_cuda(cudaMalloc((void**)&dev_b, b_bytes), "cudaMalloc(b)") != 0) return 1;
263
+ if (check_cuda(cudaMalloc((void**)&dev_c, c_bytes), "cudaMalloc(c)") != 0) return 1;
264
+
265
+ if (check_cuda(cudaMemcpy(dev_a, host_a, a_bytes, cudaMemcpyHostToDevice), "cudaMemcpy(a)") != 0) return 1;
266
+ if (check_cuda(cudaMemcpy(dev_b, host_b, b_bytes, cudaMemcpyHostToDevice), "cudaMemcpy(b)") != 0) return 1;
267
+
268
+ if (set_kernel_attributes() != 0) return 1;
269
+
270
+ block = dim3(PYC_TC_THREADS_PER_BLOCK, 1, 1);
271
+ grid = dim3(
272
+ (unsigned int)((cfg.n + PYC_TC_CTA_N - 1) / PYC_TC_CTA_N),
273
+ (unsigned int)((cfg.m + PYC_TC_CTA_M - 1) / PYC_TC_CTA_M),
274
+ 1);
275
+
276
+ if (check_cuda(cudaEventCreate(&start), "cudaEventCreate(start)") != 0) return 1;
277
+ if (check_cuda(cudaEventCreate(&stop), "cudaEventCreate(stop)") != 0) return 1;
278
+
279
+ for (iter = 0; iter < cfg.warmup; ++iter) {
280
+ pyc_tc_gemm_kernel<<<grid, block>>>(dev_a, dev_b, dev_c, cfg.m, cfg.n, cfg.k);
281
+ }
282
+ if (check_cuda(cudaGetLastError(), "kernel launch warmup") != 0) return 1;
283
+ if (check_cuda(cudaDeviceSynchronize(), "cudaDeviceSynchronize warmup") != 0) return 1;
284
+
285
+ best_ms = 0.0;
286
+ for (iter = 0; iter < cfg.iters; ++iter) {
287
+ if (check_cuda(cudaEventRecord(start), "cudaEventRecord(start)") != 0) return 1;
288
+ pyc_tc_gemm_kernel<<<grid, block>>>(dev_a, dev_b, dev_c, cfg.m, cfg.n, cfg.k);
289
+ if (check_cuda(cudaEventRecord(stop), "cudaEventRecord(stop)") != 0) return 1;
290
+ if (check_cuda(cudaEventSynchronize(stop), "cudaEventSynchronize(stop)") != 0) return 1;
291
+ if (check_cuda(cudaEventElapsedTime(&elapsed_ms, start, stop), "cudaEventElapsedTime") != 0) return 1;
292
+ if (iter == 0 || elapsed_ms < (float)best_ms) {
293
+ best_ms = elapsed_ms;
294
+ }
295
+ }
296
+
297
+ if (check_cuda(cudaMemcpy(host_c, dev_c, c_bytes, cudaMemcpyDeviceToHost), "cudaMemcpy(c)") != 0) return 1;
298
+
299
+ for (iter = 0; iter < cfg.m * cfg.n; ++iter) {
300
+ double diff = fabs((double)host_c[iter] - (double)ref_c[iter]);
301
+ if (diff > max_abs_diff) {
302
+ max_abs_diff = diff;
303
+ }
304
+ }
305
+
306
+ printf("lane=%s\n", PYC_TC_LANE_NAME);
307
+ printf("shape=%dx%dx%d\n", cfg.m, cfg.n, cfg.k);
308
+ printf("tile=%dx%dx%d threads=%d\n", PYC_TC_CTA_M, PYC_TC_CTA_N, PYC_TC_CTA_K, PYC_TC_THREADS_PER_BLOCK);
309
+ printf("best_ms=%.3f\n", best_ms);
310
+ printf("max_abs_diff=%.6f\n", max_abs_diff);
311
+ if (best_ms > 0.0) {
312
+ double flops = 2.0 * (double)cfg.m * (double)cfg.n * (double)cfg.k;
313
+ double gflops = flops / (best_ms * 1.0e6);
314
+ printf("gflops=%.3f\n", gflops);
315
+ }
316
+
317
+ cudaEventDestroy(start);
318
+ cudaEventDestroy(stop);
319
+ cudaFree(dev_a);
320
+ cudaFree(dev_b);
321
+ cudaFree(dev_c);
322
+ free(host_a);
323
+ free(host_b);
324
+ free(host_c);
325
+ free(ref_c);
326
+ return max_abs_diff <= 0.2 ? 0 : 1;
327
+ }
performance.svg ADDED