Skip to content

Commit cb3ede5

Browse files
grissom.wangclaude
andcommitted
fix(ncu_profile): auto-add __global__ to kernels that omit it
Some .cu files declare "extern \"C\" void solve(...)" without __global__. Without __global__, nvcc compiles the function as a host function and the triple-chevron launch syntax fails. Add _ensure_global() preprocessing (mirrors benchmark.py) to insert __global__ when missing. Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 618a6cf commit cb3ede5

2 files changed

Lines changed: 97 additions & 2 deletions

File tree

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Auto-generated NCU profiling bench — self-contained, no subprocess.
2+
#include <stdio.h>
3+
#include <stdlib.h>
4+
#include <string.h>
5+
#include <cuda_runtime.h>
6+
7+
// User kernel source
8+
#include "/Users/wangtianqing/Project/skills/cuda-code-skill/examples/vectorAdd/solution.cu"
9+
10+
int main(int argc, char **argv) {
11+
int warmup = 10, repeat = 22;
12+
int N = 1000000;
13+
14+
for (int i = 1; i < argc; i++) {
15+
if (strncmp(argv[i], "--N=", 4) == 0) N = atoi(argv[i] + 4);
16+
if (strncmp(argv[i], "--warmup=", 9) == 0) warmup = atoi(argv[i] + 9);
17+
if (strncmp(argv[i], "--repeat=", 9) == 0) repeat = atoi(argv[i] + 9);
18+
}
19+
20+
int threads = 256, blocks = (N + 255) / 256;
21+
22+
// Allocate device buffers
23+
size_t bytes_A = (size_t)N * 4;
24+
float *d_A;
25+
cudaMalloc(&d_A, bytes_A);
26+
size_t bytes_B = (size_t)N * 4;
27+
float *d_B;
28+
cudaMalloc(&d_B, bytes_B);
29+
size_t bytes_C = (size_t)N * 4;
30+
float *d_C;
31+
cudaMalloc(&d_C, bytes_C);
32+
33+
// Initialize: non-zero pattern for inputs, zero for outputs
34+
cudaMemset(d_A, 205, bytes_A);
35+
cudaMemset(d_B, 205, bytes_B);
36+
cudaMemset(d_C, 0, bytes_C);
37+
38+
cudaDeviceSynchronize();
39+
40+
// Warmup
41+
for (int i = 0; i < warmup; i++)
42+
solve<<<blocks, threads>>>(d_A, d_B, d_C, N);
43+
cudaDeviceSynchronize();
44+
45+
// Timed iterations
46+
cudaEvent_t start, stop;
47+
cudaEventCreate(&start); cudaEventCreate(&stop);
48+
double total = 0.0;
49+
for (int i = 0; i < repeat; i++) {
50+
cudaEventRecord(start, 0);
51+
solve<<<blocks, threads>>>(d_A, d_B, d_C, N);
52+
cudaEventRecord(stop, 0);
53+
cudaEventSynchronize(stop);
54+
float ms; cudaEventElapsedTime(&ms, start, stop);
55+
total += ms;
56+
if (i < 2 || i >= repeat - 2) printf(" iter %d: %.4f ms\n", i, ms);
57+
}
58+
printf(" avg: %.4f ms\n", total / repeat);
59+
60+
// Cleanup
61+
cudaEventDestroy(start); cudaEventDestroy(stop);
62+
cudaFree(d_A);
63+
cudaFree(d_B);
64+
cudaFree(d_C);
65+
printf("done\n");
66+
return 0;
67+
}

‎skills/kernel-benchmarker/scripts/ncu_profile.py‎

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,28 @@ def parse_solve_signature(cu_file: str):
9696
return params
9797

9898

99+
def _ensure_global(cu_file: str) -> str:
100+
"""If solve lacks __global__, return path to a temp copy with __global__ added."""
101+
with open(cu_file, "r") as f:
102+
src = f.read()
103+
104+
if re.search(r'extern\s+"C"\s+__global__\s+void\s+solve', src):
105+
return cu_file
106+
107+
new_src = re.sub(
108+
r'(extern\s+"C"\s+)void\s+solve',
109+
r'\1__global__ void solve',
110+
src
111+
)
112+
if new_src == src:
113+
return cu_file
114+
115+
tmp = cu_file + ".global.cu"
116+
with open(tmp, "w") as f:
117+
f.write(new_src)
118+
return tmp
119+
120+
99121
def detect_arch() -> str:
100122
try:
101123
import torch
@@ -289,10 +311,13 @@ def main():
289311
except ValueError:
290312
pass
291313

292-
cu_abs = os.path.abspath(args.cu_file)
314+
# Preprocess: add __global__ if missing (some kernels omit it)
315+
cu_file = _ensure_global(args.cu_file)
316+
cu_abs = os.path.abspath(cu_file)
293317
arch = args.arch or detect_arch()
294318

295-
# Parse kernel signature
319+
# Parse kernel signature (from original file since _ensure_global
320+
# only adds __global__, doesn't change the parameter list)
296321
try:
297322
params = parse_solve_signature(args.cu_file)
298323
except ValueError as e:
@@ -323,6 +348,9 @@ def main():
323348
print(f"[compile] {' '.join(cmd)}", flush=True)
324349
result = subprocess.run(cmd, capture_output=True, text=True)
325350
os.remove(wrapper_path)
351+
# Clean up temp file from _ensure_global if one was created
352+
if cu_file != args.cu_file and os.path.exists(cu_file):
353+
os.remove(cu_file)
326354

327355
if result.returncode != 0:
328356
print(f"Compilation failed:\n{result.stderr}", file=sys.stderr)

0 commit comments

Comments
 (0)