Buckets:

lvwerra's picture
download
raw
8.46 kB
"""NVRTC compile + launch + verify + bench for the K1 drafter megakernel (v1).
Adds: per-stage clock64 table, barrier microbench, stream queried at launch
time (so CUDA-graph capture works).
"""
from __future__ import annotations
import ctypes
import time
import torch
try:
from cuda.bindings import driver as cu
from cuda.bindings import nvrtc
except ImportError: # older cuda-python
from cuda import cuda as cu # type: ignore
from cuda import nvrtc # type: ignore
STAGE_NAMES = [
"S0 embed+preproj",
"L0 qrows", "L0 qnorm", "L0 attn", "L0 comb+oproj", "L0 mlp1", "L0 mlp2",
"L1 qrows", "L1 qnorm", "L1 attn", "L1 comb+oproj", "L1 mlp1", "L1 mlp2",
"L2 qrows", "L2 qnorm", "L2 attn", "L2 comb+oproj", "L2 mlp1", "L2 mlp2",
"L3 qrows", "L3 qnorm", "L3 attn", "L3 comb+oproj", "L3 mlp1", "L3 mlp2",
"S6 fnorm+cent", "S7 topk||postproj", "S8 logits", "S9 argmax",
]
def _ck(res):
err = res[0]
ok = (err.value == 0) if hasattr(err, "value") else (int(err) == 0)
if not ok:
raise RuntimeError(f"CUDA error: {res}")
return res[1:] if len(res) > 2 else (res[1] if len(res) == 2 else None)
def compile_kernel(src_path: str, arch: str = "compute_86") -> bytes:
import os as _os
src = open(src_path, "rb").read()
prog = _ck(nvrtc.nvrtcCreateProgram(src, b"megakernel.cu", 0, [], []))
opts = [f"--gpu-architecture={arch}".encode(), b"--std=c++17", b"-default-device"]
bns = _os.environ.get("BARSPIN_NS")
if bns is not None:
opts.append(f"-DBARSPIN_NS={bns}".encode())
res = nvrtc.nvrtcCompileProgram(prog, len(opts), opts)
log_size = _ck(nvrtc.nvrtcGetProgramLogSize(prog))
log = b" " * log_size
nvrtc.nvrtcGetProgramLog(prog, log)
logtxt = log.decode(errors="replace").strip()
if logtxt and logtxt != "\x00":
print(f"[mega] nvrtc log:\n{logtxt}")
err = res[0]
bad = (err.value != 0) if hasattr(err, "value") else (int(err) != 0)
if bad:
raise RuntimeError("NVRTC compilation failed")
ptx_size = _ck(nvrtc.nvrtcGetPTXSize(prog))
ptx = b" " * ptx_size
nvrtc.nvrtcGetPTX(prog, ptx)
return ptx
def _make_args(ptab, nblk, a, b, c):
arg_ptab = ctypes.c_void_p(ptab.data_ptr())
arg1 = ctypes.c_int(nblk)
arg2 = ctypes.c_int(a)
arg3 = ctypes.c_int(b)
arg4 = ctypes.c_int(c)
holders = [arg_ptab, arg1, arg2, arg3, arg4]
arr = (ctypes.c_void_p * 5)(
*[ctypes.cast(ctypes.pointer(x), ctypes.c_void_p) for x in holders]
)
return holders, arr
def run_mega(w, kv, cs_caches, first, hidden0, pos, seq, kcent, ref_tokens,
ref_hidden, k_spec=7):
dev = torch.device("cuda")
_ck(cu.cuInit(0))
ptx = compile_kernel("megakernel.cu")
module = _ck(cu.cuModuleLoadData(ptx))
func = _ck(cu.cuModuleGetFunction(module, b"drafter_megakernel"))
func_bar = _ck(cu.cuModuleGetFunction(module, b"bench_barrier"))
max_per_sm = _ck(cu.cuOccupancyMaxActiveBlocksPerMultiprocessor(func, 256, 0))
n_sm = torch.cuda.get_device_properties(0).multi_processor_count
nblk = min(n_sm, max_per_sm * n_sm)
import os as _os
if _os.environ.get("MEGA_NBLK"):
nblk = min(int(_os.environ["MEGA_NBLK"]), max_per_sm * n_sm)
print(f"[mega] SMs={n_sm} maxBlocksPerSM={max_per_sm} -> launching {nblk} blocks "
f"(BARSPIN_NS={_os.environ.get('BARSPIN_NS','64')})")
if nblk < 8:
raise RuntimeError("not enough co-resident blocks for grid barrier")
scratch_f = torch.zeros(70000, dtype=torch.float32, device=dev)
scratch_i = torch.zeros(32768, dtype=torch.int32, device=dev)
out_tokens = torch.zeros(k_spec, dtype=torch.int64, device=dev)
out_bb = torch.zeros(2560, dtype=torch.bfloat16, device=dev)
barrier = torch.zeros(2, dtype=torch.int32, device=dev)
clocks = torch.zeros(256, dtype=torch.int64, device=dev)
ptab = torch.zeros(128, dtype=torch.int64, device=dev)
host = torch.zeros(128, dtype=torch.int64)
host[0] = w["embed"].data_ptr()
host[1] = w["pre_proj"].data_ptr()
host[2] = w["post_proj"].data_ptr()
host[3] = w["final_norm"].data_ptr()
host[4] = w["cent_w"].data_ptr()
host[5] = w["lm_head"].data_ptr()
host[6] = w["token_ordering"].data_ptr()
for li in range(4):
lw = w["layers"][li]
base = 8 + li * 13
for off, key in enumerate(
["in_norm", "q_proj", "q_norm", "o_proj", "post_attn_norm",
"pre_ffn_norm", "gate", "up", "down", "post_ffn_norm",
"layer_scalar"]
):
host[base + off] = lw[key].data_ptr()
host[base + 11] = kv[li][0].data_ptr()
host[base + 12] = kv[li][1].data_ptr()
host[60] = first.data_ptr()
host[61] = hidden0.data_ptr()
host[62] = out_tokens.data_ptr()
host[63] = scratch_f.data_ptr()
host[64] = scratch_i.data_ptr()
host[65] = out_bb.data_ptr()
host[66] = barrier.data_ptr()
host[67] = clocks.data_ptr()
for li in range(4):
host[108 + li] = cs_caches[li].data_ptr()
host[112] = 0 # f32 caches in the microbench
ptab.copy_(host)
holders, arg_array = _make_args(ptab, nblk, pos, seq, kcent)
def launch():
stream = torch.cuda.current_stream().cuda_stream
_ck(cu.cuLaunchKernel(func, nblk, 1, 1, 256, 1, 1, 0, stream,
ctypes.addressof(arg_array), 0))
# correctness
launch()
torch.cuda.synchronize()
mt = out_tokens.tolist()
rt = ref_tokens.tolist()
match = sum(int(a == b) for a, b in zip(mt, rt))
hdiff = (out_bb.float() - ref_hidden.float()).abs().max().item()
print(f"[mega] tokens ref =[{', '.join(map(str, rt))}]")
print(f"[mega] tokens mega=[{', '.join(map(str, mt))}] match={match}/{k_spec}")
print(f"[mega] backbone hidden max|diff|={hdiff:.4e}")
# stage clock table (mean over iters 1..k_spec-1, skip warm iter 0)
rate_khz = _ck(cu.cuDeviceGetAttribute(
cu.CUdevice_attribute.CU_DEVICE_ATTRIBUTE_CLOCK_RATE, 0))
cl = clocks.cpu().tolist()
n_st = len(STAGE_NAMES)
sums = [0.0] * n_st
cnt = 0
for it in range(1, k_spec):
base = it * 32
if cl[base + n_st] == 0:
continue
cnt += 1
for s in range(n_st):
sums[s] += (cl[base + s + 1] - cl[base + s]) / (rate_khz / 1000.0)
if cnt:
print(f"[mega] per-stage us (mean over {cnt} iters, clock={rate_khz}kHz):")
tot = 0.0
for s in range(n_st):
v = sums[s] / cnt
tot += v
print(f"[mega] {STAGE_NAMES[s]:<20s} {v:8.2f} us")
print(f"[mega] {'TOTAL':<20s} {tot:8.2f} us/iter")
# barrier microbench: 1000 syncs
holders_b, arr_b = _make_args(ptab, nblk, 1000, 0, 0)
stream = torch.cuda.current_stream().cuda_stream
_ck(cu.cuLaunchKernel(func_bar, nblk, 1, 1, 256, 1, 1, 0, stream,
ctypes.addressof(arr_b), 0))
torch.cuda.synchronize()
t0 = time.perf_counter()
for _ in range(5):
_ck(cu.cuLaunchKernel(func_bar, nblk, 1, 1, 256, 1, 1, 0, stream,
ctypes.addressof(arr_b), 0))
torch.cuda.synchronize()
per_sync = (time.perf_counter() - t0) / 5 / 1000 * 1e6
print(f"[mega] grid_sync cost: {per_sync:.3f} us/sync ({nblk} blocks)")
# bench
for _ in range(10):
launch()
torch.cuda.synchronize()
t0 = time.perf_counter()
n_iter = 50
for _ in range(n_iter):
launch()
torch.cuda.synchronize()
t = (time.perf_counter() - t0) / n_iter * 1e3
print(f"[mega] mega : {t:8.3f} ms/loop {t/k_spec:7.3f} ms/iter")
# graph capture (stream now resolved inside launch())
try:
g = torch.cuda.CUDAGraph()
with torch.cuda.graph(g):
launch()
g.replay()
torch.cuda.synchronize()
mt2 = out_tokens.tolist()
for _ in range(10):
g.replay()
torch.cuda.synchronize()
t0 = time.perf_counter()
for _ in range(n_iter):
g.replay()
torch.cuda.synchronize()
tg = (time.perf_counter() - t0) / n_iter * 1e3
print(f"[mega] mega_graph: {tg:8.3f} ms/loop {tg/k_spec:7.3f} ms/iter "
f"(replay tokens match={sum(int(a==b) for a,b in zip(mt2, rt))}/{k_spec})")
except Exception as exc:
print(f"[mega] graph capture failed (non-fatal): {exc!r}")
return mt, t

Xet Storage Details

Size:
8.46 kB
·
Xet hash:
04f2222d571550c3a2842a6ab186a9a8b35c16b3c18f5931a8380cdba282fb65

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.