Buckets:

aymous's picture
download
raw
6.66 kB
#!/usr/bin/env python
"""claudecode — GRAPH-MODE decode profiler (real serving config), via torch.profiler.
Run3 (eager) showed decode compute ~83% weight-GEMM, attention ~4%, sampling ~0.1% — but
eager masks the real overhead gap. This run, with CUDA graphs ON (the real config), answers
the go/no-go for a high-ceiling sub-4-bit kernel:
- GPU-busy vs wall -> is there non-kernel (scheduler/python) overhead to claw back?
- GEMM share of GPU-busy -> the size of the prize a sub-4-bit kernel could capture.
torch.profiler (CUPTI) captures DEVICE kernels even under graph replay (graphs only hide the
CPU-side dispatch). We use self_device_time per entry => clean GPU-busy with no parent/child
double-counting. VLLM_ENABLE_V1_MULTIPROCESSING=0 keeps the engine in-process so the profiler
sees it (proven in run3). Writes JSON + summary to /state and stdout; exits.
"""
from __future__ import annotations
import json
import os
import sys
import time
import torch
MODEL_ID = os.environ.get("MODEL_ID", "google/gemma-4-E4B-it-qat-w4a16-ct")
STATE = os.environ.get("STATE_DIR", "/state")
TPS_TOKENS = int(os.environ.get("TPS_TOKENS", "256"))
PROFILE_TOKENS = int(os.environ.get("PROFILE_TOKENS", "64"))
CATEGORIES = [
("attention", ["attn", "_fwd", "flash", "paged", "unified_attention",
"reshape_and_cache", "rotary", "rope"]),
("sampling_lmhead", ["log_softmax", "logsoftmax", "argmax", "topk", "top_k",
"softmax", "sample", "logits", "cumsum", "sort"]),
("matmul_gemm", ["marlin", "gptq", "gemm", "cutlass", "wmma", "gemv", "splitk",
"split_k", "ampere", "s16816", "dot", "_C::"]),
("norm", ["rms", "layernorm", "layer_norm", "norm_kernel"]),
("activation", ["silu", "gelu", "swiglu", "act_and_mul"]),
("elementwise_copy", ["elementwise", "copy", "cast", "convert", "memcpy",
"fill", "add", "mul", "index", "vectorized", "to"]),
]
def categorize(name: str) -> str:
n = name.lower()
if "marlin" in n or "gemv" in n or "gemm" in n:
return "matmul_gemm"
for cat, subs in CATEGORIES:
if any(s in n for s in subs):
return cat
return "other"
def self_dev(e) -> float:
for attr in ("self_device_time_total", "self_cuda_time_total"):
v = getattr(e, attr, None)
if v is not None:
return float(v)
return 0.0
def main() -> None:
os.makedirs(STATE, exist_ok=True)
os.environ["VLLM_ENABLE_V1_MULTIPROCESSING"] = "0"
print(f"[gprof] torch {torch.__version__}; dev {torch.cuda.get_device_name(0)}; graphs ON",
flush=True)
from vllm import LLM, SamplingParams
t0 = time.time()
llm = LLM(
model=MODEL_ID, quantization="compressed-tensors", dtype="bfloat16",
max_model_len=4096, gpu_memory_utilization=0.90, max_num_batched_tokens=512,
max_num_seqs=1, enforce_eager=False, trust_remote_code=True, disable_log_stats=True,
)
print(f"[gprof] model+graphs ready in {time.time()-t0:.1f}s", flush=True)
prompt = ("Explain, step by step, how a transformer language model generates text "
"one token at a time, and why decode is memory-bandwidth bound.")
sp = lambda n: SamplingParams(temperature=0.0, max_tokens=n, ignore_eos=True)
llm.generate([prompt], sp(16)) # warmup
torch.cuda.synchronize()
# 1) Clean graph-mode TPS (no profiler).
t0 = time.time()
out = llm.generate([prompt], sp(TPS_TOKENS))
torch.cuda.synchronize()
wall = time.time() - t0
n = len(out[0].outputs[0].token_ids)
tps = n / wall
print(f"[gprof] GRAPH-MODE TPS: {n} tok / {wall:.3f}s = {tps:.2f} tok/s "
f"(single-stream, base int4)", flush=True)
# 2) Profiled generate (graphs on; CUPTI still captures device kernels).
from torch.profiler import profile, ProfilerActivity
tp0 = time.time()
with profile(activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA]) as prof:
pout = llm.generate([prompt], sp(PROFILE_TOKENS))
torch.cuda.synchronize()
prof_wall = time.time() - tp0
pn = len(pout[0].outputs[0].token_ids)
rows = [(e.key, self_dev(e), int(getattr(e, "count", 0))) for e in prof.key_averages()]
rows = [r for r in rows if r[1] > 0]
rows.sort(key=lambda r: r[1], reverse=True)
busy_us = sum(r[1] for r in rows)
cats: dict[str, float] = {}
for name, us, _ in rows:
cats[categorize(name)] = cats.get(categorize(name), 0.0) + us
busy_ms = busy_us / 1000
busy_share = 100 * (busy_ms / 1000) / prof_wall if prof_wall else 0.0
print("\n==== GRAPH-MODE PROFILE (self-device time; de-duped) ====", flush=True)
print(f" graph-mode TPS (base int4): {tps:.2f} tok/s", flush=True)
print(f" profiled: {pn} tok / {prof_wall*1000:.0f} ms wall", flush=True)
print(f" GPU-busy (sum self-device): {busy_ms:.1f} ms "
f"= {busy_share:.1f}% of wall -> non-kernel overhead ~{max(0,100-busy_share):.1f}%",
flush=True)
print(f" GPU-busy / token: {busy_ms/pn:.3f} ms", flush=True)
print(" --- GPU-busy by category (share of kernel time) ---", flush=True)
for c, us in sorted(cats.items(), key=lambda x: -x[1]):
print(f" {c:18s} {us/1000:9.2f} ms {100*us/busy_us:5.1f}%", flush=True)
print(" --- top 18 kernels by self-device time ---", flush=True)
for name, us, cnt in rows[:18]:
print(f" {100*us/busy_us:5.1f}% {us/1000:8.2f}ms x{cnt:<6d} [{categorize(name)}] "
f"{name[:66]}", flush=True)
result = {
"model": MODEL_ID, "mode": "graphs_on", "graph_tps": tps,
"tps_tokens": n, "tps_wall_s": wall, "profile_tokens": pn,
"profile_wall_s": prof_wall, "gpu_busy_ms": busy_ms,
"gpu_busy_share_of_wall_pct": busy_share, "gpu_busy_per_token_ms": busy_ms / pn,
"category_ms": {k: v / 1000 for k, v in cats.items()},
"category_pct": {k: 100 * v / busy_us for k, v in cats.items()},
"top_kernels": [{"kernel": nm, "ms": us / 1000, "count": c,
"pct": 100 * us / busy_us, "category": categorize(nm)}
for nm, us, c in rows[:25]],
}
try:
with open(os.path.join(STATE, "graph_profile.json"), "w") as f:
json.dump(result, f, indent=2)
print(f"\n[gprof] wrote {STATE}/graph_profile.json", flush=True)
except Exception as e:
print(f"[gprof] could not write /state ({e})", flush=True)
print("\n[gprof] done — exiting (harness 'not ready' is expected).", flush=True)
sys.exit(0)
if __name__ == "__main__":
main()

Xet Storage Details

Size:
6.66 kB
·
Xet hash:
54b809d42a50521b2e2991292043bf50d958cd8ed6d106e80406080c7894650b

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