phanerozoic commited on
Commit
8aed2e2
·
verified ·
1 Parent(s): c34937e

Uploaded using `kernel-builder`.

Browse files
Files changed (39) hide show
  1. .gitattributes +6 -0
  2. build/torch212-cxx11-cu126-x86_64-linux/__init__.py +935 -300
  3. build/torch212-cxx11-cu126-x86_64-linux/{_metakernel_cuda_b62a1ce.abi3.so → _metakernel_cuda_0b9de42.abi3.so} +2 -2
  4. build/torch212-cxx11-cu126-x86_64-linux/_ops.py +3 -3
  5. build/torch212-cxx11-cu126-x86_64-linux/_pinned.py +194 -102
  6. build/torch212-cxx11-cu126-x86_64-linux/metadata.json +8 -8
  7. build/torch212-cxx11-cu126-x86_64-linux/metakernel/__init__.py +0 -26
  8. build/torch212-cxx11-cu130-x86_64-linux/__init__.py +935 -300
  9. build/{torch212-cxx11-cu132-x86_64-linux/_metakernel_cuda_b62a1ce.abi3.so → torch212-cxx11-cu130-x86_64-linux/_metakernel_cuda_0b9de42.abi3.so} +2 -2
  10. build/torch212-cxx11-cu130-x86_64-linux/_ops.py +3 -3
  11. build/torch212-cxx11-cu130-x86_64-linux/_pinned.py +194 -102
  12. build/torch212-cxx11-cu130-x86_64-linux/metadata.json +8 -8
  13. build/torch212-cxx11-cu130-x86_64-linux/metakernel/__init__.py +0 -26
  14. build/torch212-cxx11-cu132-x86_64-linux/__init__.py +935 -300
  15. build/{torch212-cxx11-cu130-x86_64-linux/_metakernel_cuda_b62a1ce.abi3.so → torch212-cxx11-cu132-x86_64-linux/_metakernel_cuda_0b9de42.abi3.so} +2 -2
  16. build/torch212-cxx11-cu132-x86_64-linux/_ops.py +3 -3
  17. build/torch212-cxx11-cu132-x86_64-linux/_pinned.py +194 -102
  18. build/torch212-cxx11-cu132-x86_64-linux/metadata.json +8 -8
  19. build/torch212-cxx11-cu132-x86_64-linux/metakernel/__init__.py +0 -26
  20. build/torch213-cxx11-cu126-x86_64-linux/__init__.py +935 -300
  21. build/torch213-cxx11-cu126-x86_64-linux/{_metakernel_cuda_b62a1ce.abi3.so → _metakernel_cuda_0b9de42.abi3.so} +2 -2
  22. build/torch213-cxx11-cu126-x86_64-linux/_ops.py +3 -3
  23. build/torch213-cxx11-cu126-x86_64-linux/_pinned.py +194 -102
  24. build/torch213-cxx11-cu126-x86_64-linux/metadata.json +8 -8
  25. build/torch213-cxx11-cu126-x86_64-linux/metakernel/__init__.py +0 -26
  26. build/torch213-cxx11-cu130-x86_64-linux/__init__.py +935 -300
  27. build/torch213-cxx11-cu130-x86_64-linux/_metakernel_cuda_0b9de42.abi3.so +3 -0
  28. build/torch213-cxx11-cu130-x86_64-linux/_metakernel_cuda_b62a1ce.abi3.so +0 -3
  29. build/torch213-cxx11-cu130-x86_64-linux/_ops.py +3 -3
  30. build/torch213-cxx11-cu130-x86_64-linux/_pinned.py +194 -102
  31. build/torch213-cxx11-cu130-x86_64-linux/metadata.json +8 -8
  32. build/torch213-cxx11-cu130-x86_64-linux/metakernel/__init__.py +0 -26
  33. build/torch213-cxx11-cu132-x86_64-linux/__init__.py +935 -300
  34. build/torch213-cxx11-cu132-x86_64-linux/_metakernel_cuda_0b9de42.abi3.so +3 -0
  35. build/torch213-cxx11-cu132-x86_64-linux/_metakernel_cuda_b62a1ce.abi3.so +0 -3
  36. build/torch213-cxx11-cu132-x86_64-linux/_ops.py +3 -3
  37. build/torch213-cxx11-cu132-x86_64-linux/_pinned.py +194 -102
  38. build/torch213-cxx11-cu132-x86_64-linux/metadata.json +8 -8
  39. build/torch213-cxx11-cu132-x86_64-linux/metakernel/__init__.py +0 -26
.gitattributes CHANGED
@@ -96,3 +96,9 @@ build/torch212-cxx11-cu132-x86_64-linux/_metakernel_cuda_b62a1ce.abi3.so filter=
96
  build/torch213-cxx11-cu126-x86_64-linux/_metakernel_cuda_b62a1ce.abi3.so filter=lfs diff=lfs merge=lfs -text
97
  build/torch213-cxx11-cu130-x86_64-linux/_metakernel_cuda_b62a1ce.abi3.so filter=lfs diff=lfs merge=lfs -text
98
  build/torch213-cxx11-cu132-x86_64-linux/_metakernel_cuda_b62a1ce.abi3.so filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
96
  build/torch213-cxx11-cu126-x86_64-linux/_metakernel_cuda_b62a1ce.abi3.so filter=lfs diff=lfs merge=lfs -text
97
  build/torch213-cxx11-cu130-x86_64-linux/_metakernel_cuda_b62a1ce.abi3.so filter=lfs diff=lfs merge=lfs -text
98
  build/torch213-cxx11-cu132-x86_64-linux/_metakernel_cuda_b62a1ce.abi3.so filter=lfs diff=lfs merge=lfs -text
99
+ build/torch212-cxx11-cu126-x86_64-linux/_metakernel_cuda_0b9de42.abi3.so filter=lfs diff=lfs merge=lfs -text
100
+ build/torch212-cxx11-cu130-x86_64-linux/_metakernel_cuda_0b9de42.abi3.so filter=lfs diff=lfs merge=lfs -text
101
+ build/torch212-cxx11-cu132-x86_64-linux/_metakernel_cuda_0b9de42.abi3.so filter=lfs diff=lfs merge=lfs -text
102
+ build/torch213-cxx11-cu126-x86_64-linux/_metakernel_cuda_0b9de42.abi3.so filter=lfs diff=lfs merge=lfs -text
103
+ build/torch213-cxx11-cu130-x86_64-linux/_metakernel_cuda_0b9de42.abi3.so filter=lfs diff=lfs merge=lfs -text
104
+ build/torch213-cxx11-cu132-x86_64-linux/_metakernel_cuda_0b9de42.abi3.so filter=lfs diff=lfs merge=lfs -text
build/torch212-cxx11-cu126-x86_64-linux/__init__.py CHANGED
@@ -1,20 +1,4 @@
1
- """Measuring instrument for the kernel-design loop: timing, adversarial
2
- correctness comparison, and measured device facts, as loadable ops.
3
-
4
- from kernels import get_kernel
5
- mk = get_kernel("phanerozoic/metakernel", version=1,
6
- trust_remote_code=True)
7
-
8
- dossier = mk.probe_device() # measured facts
9
- report = mk.bench(lambda: op(x), iters=200) # throttle-rejected
10
- verdict = mk.compare(ref, cand, gen) # ULP evidence
11
- table = mk.sweep(factory, {"BLOCK": [64, 128]}) # config search
12
-
13
- Every op returns plain dicts and tensors. Every dossier entry is
14
- measured by a dedicated probe kernel, carries its SM clock, and labels
15
- throttled samples; no hardware counters, so entries carry a confidence
16
- field.
17
- """
18
  import math
19
  import os
20
  import time
@@ -24,9 +8,7 @@ import torch
24
  try:
25
  from ._ops import ops
26
  except ImportError: # pragma: no cover - spawned child in a source tree
27
- # Subprocess-isolation children re-import this package; in a source
28
- # checkout there is no generated _ops module, so rebuild the op table
29
- # from the (cached) JIT extension that load_local advertised.
30
  _root = os.environ.get("METAKERNEL_DEV_ROOT")
31
  if not _root:
32
  raise
@@ -48,24 +30,25 @@ try:
48
  except ImportError: # pragma: no cover - fresh tree before first pin
49
  _PINNED = {}
50
 
51
- __all__ = ["probe_device", "bench", "compare", "order_shuffles",
52
- "fuzz_shapes", "sweep", "alloc_stamps", "read_stamps",
53
- "stamped_demo", "measure_clock", "ulp_diff", "dossier_drift",
54
- "pinned_dossiers", "ops"]
55
-
56
- # name -> (kind, M*N of the C tile, nominal K, work multiplier, nominal
57
- # K-lanes per unrolled iteration). The accumulator read-back yields
58
- # executed K-lanes per op (column sums of B equal K), so flops =
59
- # 2 x M*N x gain x multiplier with nothing declared trusted; multiplier
60
- # 2.0 is the sparse dense-equivalent convention. Lanes/iter: wmma K x 4
61
- # chained ops; PTX kinds x 8 over two chains.
62
  _MMA_KINDS = {
63
- "fp16": (0, 16 * 16, 16, 1.0, 64.0),
64
- "bf16": (1, 16 * 16, 16, 1.0, 64.0),
65
- "tf32": (2, 16 * 16, 8, 1.0, 32.0),
66
- "int8": (3, 16 * 16, 16, 1.0, 64.0),
67
- "fp8_e4m3": (4, 16 * 8, 32, 1.0, 256.0),
68
- "fp16_sparse_2_4": (5, 16 * 8, 32, 2.0, 128.0), # dense-equivalent
 
69
  }
70
 
71
 
@@ -74,7 +57,7 @@ def _sync():
74
 
75
 
76
  def _events(fn, reps):
77
- """Time reps calls of fn with one event pair each; returns ms list."""
78
  starts = [torch.cuda.Event(enable_timing=True) for _ in range(reps)]
79
  stops = [torch.cuda.Event(enable_timing=True) for _ in range(reps)]
80
  for s, e in zip(starts, stops):
@@ -86,8 +69,7 @@ def _events(fn, reps):
86
 
87
 
88
  def measure_clock(device="cuda", ticks=4_000_000, blocks=None):
89
- """Effective SM clock in GHz under an FMA spin of `ticks` clock64 ticks.
90
- Wall time over elapsed ticks is the frequency the ticks ran at."""
91
  props = torch.cuda.get_device_properties(device)
92
  if blocks is None:
93
  blocks = props.multi_processor_count
@@ -120,8 +102,7 @@ def _entry(value, units, method, clock_ghz, confidence, note=""):
120
 
121
 
122
  def _sattolo_ring(n, device, generator=None):
123
- """A random single-cycle permutation as an int32 next-pointer table, so
124
- a pointer chase visits every slot before repeating."""
125
  perm = torch.randperm(n, generator=generator)
126
  ring = torch.empty(n, dtype=torch.int32)
127
  ring[perm] = torch.roll(perm, -1).to(torch.int32)
@@ -129,8 +110,7 @@ def _sattolo_ring(n, device, generator=None):
129
 
130
 
131
  def _smi_settings(index):
132
- """ECC mode, driver, and power limit via nvidia-smi; None fields
133
- when the tool is unavailable."""
134
  import subprocess
135
  try:
136
  out = subprocess.run(
@@ -150,10 +130,19 @@ def _smi_settings(index):
150
  return {"ecc": None, "driver": None, "power_limit_w": None}
151
 
152
 
 
 
 
 
 
 
 
 
 
 
 
153
  def mma_tile_check(device="cuda", trials=8):
154
- """Random small-integer m16n16k16 tiles through the wmma fragment
155
- path, compared exactly to the reference GEMM; pins M, N, K, and
156
- operand mapping (integer operands make fp32 accumulation exact)."""
157
  worst = 0.0
158
  for t in range(trials):
159
  g = torch.Generator(device=device).manual_seed(1000 + t)
@@ -171,10 +160,83 @@ def mma_tile_check(device="cuda", trials=8):
171
  "trials": trials}
172
 
173
 
174
- def probe_device(device="cuda", quick=False):
175
- """Empirical device dossier. Each entry is measured by a dedicated
176
- probe kernel and carries units, method, the SM clock it ran under, and
177
- a confidence field."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
  props = torch.cuda.get_device_properties(device)
179
  sm = props.multi_processor_count
180
  l2 = props.L2_cache_size
@@ -187,8 +249,7 @@ def probe_device(device="cuda", quick=False):
187
  "vram_bytes": props.total_memory,
188
  "torch": torch.__version__,
189
  "cuda_runtime": torch.version.cuda,
190
- # instrument settings: readings depend on them, so the
191
- # dossier records them (None when nvidia-smi is unavailable)
192
  **_smi_settings(torch.device(device).index or 0),
193
  }
194
  }
@@ -213,9 +274,8 @@ def probe_device(device="cuda", quick=False):
213
  }
214
  clk = max(traj)
215
 
216
- # ---- device-memory bandwidth: triad per access width, plus read-only,
217
- # write-only, and random-gather
218
- n4 = 16 << 20 # 16M float4 = 256 MB per buffer
219
  if props.total_memory < 4 << 30:
220
  n4 = 4 << 20
221
  a = torch.rand(n4 * 4, device=device)
@@ -239,7 +299,6 @@ def probe_device(device="cuda", quick=False):
239
  dtype=torch.int32).to(device)
240
  ops.mk_gather(a, idx, c, 256, 0)
241
  ms = _median(_events(lambda: ops.mk_gather(a, idx, c, 256, 0), reps))
242
- # traffic accounting: idx reads + 16B random loads + streamed stores
243
  hbm["random_gather_16B"] = round(
244
  (idx.numel() * 4 + 2 * n4 * 16) / (ms * 1e-3) / 1e9, 1)
245
  d["dram_bw"] = _entry(hbm, "GB/s", "streaming triad c=a+s*b per access "
@@ -250,9 +309,9 @@ def probe_device(device="cuda", quick=False):
250
  "so its nominal number is the achievable rate for "
251
  "random access, not a line-efficiency claim")
252
 
253
- # ---- L2 bandwidth (working set 3 buffers inside L2)
254
  ws = max(l2 // 4, 1 << 20)
255
- nl2 = ws // 16 # float4 elems per buffer
256
  al = a[:nl2 * 4].contiguous()
257
  bl = b[:nl2 * 4].contiguous()
258
  cl = c[:nl2 * 4].contiguous()
@@ -267,62 +326,61 @@ def probe_device(device="cuda", quick=False):
267
  "medium", "first pass streams from HBM; amortized "
268
  f"over {passes} passes")
269
 
270
- # ---- latencies: pointer chase per level. The 2 MB L2 ring exceeds
271
- # L1, fits L2 everywhere, and stays TLB-resident after a warm walk;
272
- # the DRAM chain touches 2x L2 in lines, so it evicts itself.
273
  lat = {}
274
  idle_clk = measure_clock(device, ticks=2_000_000, blocks=1)
275
- ring_s = _sattolo_ring(8192, device)
276
- ops.mk_chase_shared(ring_s, 1 << 14, out64)
277
- _sync()
278
- hops = 1 << 16
279
- ops.mk_chase_shared(ring_s, hops, out64)
280
- _sync()
281
- lat["smem_cycles"] = round(int(out64[0].item()) / hops, 1)
282
 
283
- l1_ring_n = (16 << 10) // 4 # 16 KB: L1-resident after a warm walk
284
- ring_l1 = _sattolo_ring(l1_ring_n, device)
285
- ops.mk_chase_global(ring_l1, l1_ring_n, out64)
286
- _sync()
287
- hops_l1 = 1 << 15
288
- ops.mk_chase_global(ring_l1, hops_l1, out64)
289
- _sync()
290
- lat["l1_cycles"] = round(int(out64[0].item()) / hops_l1, 1)
291
-
292
- l2_ring_n = (2 << 20) // 4
293
- ring_l2 = _sattolo_ring(l2_ring_n, device)
294
- ops.mk_chase_global(ring_l2, l2_ring_n, out64) # full-cycle warm walk
295
- _sync()
296
- hops_l2 = 1 << 14
297
- ops.mk_chase_global(ring_l2, hops_l2, out64)
298
- _sync()
299
- lat["l2_cycles"] = round(int(out64[0].item()) / hops_l2, 1)
300
 
 
 
 
 
 
 
 
 
 
 
 
 
301
  hbm_bytes = min(max(4 * l2, 64 << 20), props.total_memory // 8)
302
  ring_h = _sattolo_ring(int(hbm_bytes) // 4, device)
303
  hops_h = max((2 * l2) // 128, 1 << 17)
304
- ops.mk_chase_global(ring_h, 1 << 14, out64) # fault pages / warm TLB
305
- _sync()
306
- ops.mk_chase_global(ring_h, hops_h, out64)
307
- _sync()
308
- lat["dram_cycles"] = round(int(out64[0].item()) / hops_h, 1)
309
  lat["ns_per_cycle"] = round(1.0 / idle_clk, 3)
310
  d["latency"] = _entry(lat, "cycles (single-lane dependent loads)",
311
- "Sattolo-cycle pointer chase; 16 KB ring for L1, "
312
- "2 MB TLB-resident ring for L2, self-evicting "
313
- "chain (2x L2 in touched lines) for HBM",
314
- idle_clk, "high",
315
  "smem includes index arithmetic; HBM includes "
316
  "residual TLB effects at large footprints")
317
  del ring_h, ring_l2, ring_l1, ring_s
318
 
319
- # ---- fp32 FMA and tensor-core throughput
320
  blocks = sm * 4
321
  iters = 20_000 if quick else 60_000
322
  ops.mk_fma_f32(blocks, 256, iters, sink)
323
  ms = _median(_events(lambda: ops.mk_fma_f32(blocks, 256, iters, sink),
324
  reps))
325
- flops = blocks * 256 * iters * 16 * 2 * 2 # 2 chains x 2 flops x unroll
326
  d["fma_f32"] = _entry(round(flops / (ms * 1e-3) / 1e12, 2), "TFLOP/s",
327
  "register-resident FMA chains", clk, "high")
328
  sink64 = torch.zeros(2, dtype=torch.float64, device=device)
@@ -334,43 +392,88 @@ def probe_device(device="cuda", quick=False):
334
  d["fma_f64"] = _entry(round(flops64 / (ms * 1e-3) / 1e12, 3), "TFLOP/s",
335
  "register-resident double FMA chains (the DFMA "
336
  "pipe)", clk, "high")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
337
  mma = {}
338
  mma_klanes = {}
339
- for name, (kind, mn, k_nom, mult, nom_lanes) in _MMA_KINDS.items():
 
340
  it = 30_000 if quick else 100_000
341
  r = ops.mk_mma(kind, blocks, 8, 512, sink)
342
  if r != 0:
343
  mma[name] = "unsupported"
344
  continue
345
  _sync()
346
- # verification pass: with unit operands the accumulator gain IS
347
- # the executed K-lane count (exact at these magnitudes)
348
- cal_iters = 512
349
- sink[1] = 0.0
350
- ops.mk_mma(kind, 1, 1, cal_iters, sink)
351
- _sync()
352
- gain_per_iter = float(sink[1].item()) / cal_iters
353
  mma_klanes[name] = {
354
- "executed_k_per_iter": round(gain_per_iter, 2),
355
- "vs_nominal": round(gain_per_iter / nom_lanes, 3),
 
356
  }
 
 
357
  ms = _median(_events(
358
  lambda k=kind, i=it: ops.mk_mma(k, blocks, 8, i, sink), reps))
359
- flops = blocks * 8 * it * 2.0 * mn * gain_per_iter * mult
360
- mma[name] = round(flops / (ms * 1e-3) / 1e12, 1)
 
 
 
 
361
  d["mma"] = _entry(mma, "TFLOP/s (TOP/s for int8)",
362
  "register-fragment mma_sync loops (wmma; fp8 and "
363
- "2:4-sparse fp16 via inline PTX m16n8k32; sparse "
364
- "counted dense-equivalent, the vendor convention); "
365
- "flops derive from the mean accumulator read-back "
366
  "with every fragment element observed, never the "
367
- "declared loop shape",
 
368
  clk, "medium",
369
  "dense-tile rate without memory traffic; real "
370
  "kernels see this only when staging hides behind it")
371
  d["mma"]["verified_k_lanes"] = mma_klanes
 
 
372
 
373
- # ---- shared-memory bandwidth
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
374
  it_sm = 30_000 if quick else 100_000
375
  ops.mk_smem_bw(blocks, 256, it_sm, sink)
376
  ms = _median(_events(lambda: ops.mk_smem_bw(blocks, 256, it_sm, sink),
@@ -380,8 +483,23 @@ def probe_device(device="cuda", quick=False):
380
  "block-resident read+write streaming (8 B per "
381
  "thread-iteration), aggregated across SMs", clk,
382
  "medium",
383
- "bank-conflict-light access pattern; a conflicted "
384
- "pattern can be far lower")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
385
 
386
  # ---- PCIe / host transfer bandwidth
387
  nbytes = 256 << 20
@@ -412,9 +530,7 @@ def probe_device(device="cuda", quick=False):
412
  "d2d counts one direction of traffic")
413
  del host_pin, host_pag, dev_t, dev_u
414
 
415
- # ---- atomic contention curve: throughput vs number of distinct
416
- # addresses the whole grid hammers (slots = 1 is full contention;
417
- # slots = threads is contention-free), plus the shared-memory point
418
  n_thr = blocks * 256
419
  buf = torch.zeros(n_thr, dtype=torch.float32, device=device)
420
  at = {"curve_slots": {}}
@@ -424,7 +540,7 @@ def probe_device(device="cuda", quick=False):
424
  buf.zero_()
425
  ops.mk_atomics(1, blocks, 256, it_at, buf, slots)
426
  ms = _median(_events(
427
- lambda s=slots: ops.mk_atomics(1, blocks, 256, it_at, buf, s),
428
  reps))
429
  at["curve_slots"][str(slots)] = round(
430
  n_thr * it_at / (ms * 1e-3) / 1e9, 2)
@@ -437,7 +553,7 @@ def probe_device(device="cuda", quick=False):
437
  "of distinct global addresses from 1 (full "
438
  "contention) to one per thread", clk, "high")
439
 
440
- # ---- launch overhead
441
  anchor = sink
442
  n_launch = 500 if quick else 2000
443
  _sync()
@@ -452,10 +568,19 @@ def probe_device(device="cuda", quick=False):
452
  ops.mk_empty(anchor)
453
  _sync()
454
  rt = (time.perf_counter() - t0) / n_rt * 1e6
 
 
455
  d["launch_overhead"] = _entry(
456
- {"queued_us": round(queued, 2), "round_trip_us": round(rt, 2)},
457
- "us", "empty-kernel trains, queued vs launch+sync", clk, "high",
458
- "host-dependent: includes torch op dispatch")
 
 
 
 
 
 
 
459
 
460
  # ---- grid barrier
461
  bar_iters = 500 if quick else 2000
@@ -471,9 +596,7 @@ def probe_device(device="cuda", quick=False):
471
  "unavailable or grid too large", clk,
472
  "high")
473
 
474
- # ---- occupancy table (API) plus timed knees: the same random gather
475
- # at every (threads, dyn_smem) point; when the forced blocks/SM stop
476
- # covering memory latency, achieved bandwidth falls off the cliff
477
  occ = {}
478
  for threads in (128, 256, 512, 1024):
479
  row = {}
@@ -494,8 +617,8 @@ def probe_device(device="cuda", quick=False):
494
  ops.mk_fma_dep(knee_blocks, threads, smem_kb * 1024,
495
  knee_iters, sink)
496
  ms = _median(_events(
497
- lambda t=threads, s=smem_kb: ops.mk_fma_dep(
498
- knee_blocks, t, s * 1024, knee_iters, sink), reps))
499
  fl = knee_blocks * threads * knee_iters * 16 * 2
500
  row[f"{smem_kb}KB"] = round(fl / (ms * 1e-3) / 1e12, 2)
501
  knees[f"t{threads}"] = row
@@ -504,27 +627,160 @@ def probe_device(device="cuda", quick=False):
504
  "at forced (threads, dyn smem) occupancies over fixed total work; "
505
  "the cliff marks where occupancy stops covering latency",
506
  clk, "high", "timing-derived counterpart to the API table above")
 
 
 
507
  return d
508
 
509
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
510
  def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
511
  dtype="fp16", clock_guard=0.97, clock_stride=8, graph=False,
512
- device="cuda"):
513
- """Median-of-N cudaEvent timing with throttle rejection.
514
-
515
- The SM clock is probed every `clock_stride` samples; samples in a
516
- window below `clock_guard` x the best observed clock are rejected
517
- and counted. Declared `bytes`/`flops` yield achieved GB/s / TFLOP/s;
518
- a `dossier` adds roofline placement. `graph=True` times CUDA-graph
519
- replays, stripping Python and torch dispatch (fn must be graph-safe:
520
- no sync, no allocation growth, static shapes; a failed capture falls
521
- back to eager with the reason reported). `clock_stride=1` probes
522
- around every sample, ~0.3 ms each."""
523
  for _ in range(warmup):
524
  fn()
525
  _sync()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
526
  graph_state = "off"
527
  graph_reason = ""
 
 
 
 
 
 
528
  if graph:
529
  pre_rng = torch.cuda.get_rng_state(device)
530
  try:
@@ -539,10 +795,7 @@ def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
539
  except Exception as e: # noqa: BLE001 - fall back, say why
540
  graph_state = "fallback_eager"
541
  graph_reason = f"{type(e).__name__}: {str(e)[:120]}"
542
- # drain the invalidated stream so eager timing can proceed.
543
- # On some torch versions a failed capture also wedges the
544
- # device RNG with no in-process recovery; timing uses no RNG,
545
- # but the caller's later randn may raise.
546
  rng_ok = True
547
  for _attempt in range(5):
548
  try:
@@ -572,25 +825,51 @@ def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
572
  samples.append((_events(fn, n), len(clocks) - 1))
573
  clocks.append(measure_clock(device, ticks=probe_ticks))
574
  best_clk = max(clocks)
575
- kept, rejected = [], 0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
576
  flat = []
577
- for group, ci in samples:
578
- window_ok = (clocks[ci] >= clock_guard * best_clk and
579
- clocks[min(ci + 1, len(clocks) - 1)] >=
580
- clock_guard * best_clk)
581
- for ms in group:
582
- flat.append(ms)
583
- if window_ok:
 
 
 
 
 
 
 
 
 
 
 
 
584
  kept.append(ms)
585
  else:
586
  rejected += 1
587
- if not kept:
 
588
  kept = flat
589
  med = _median(kept)
590
  ks = sorted(kept)
591
  n_k = len(ks)
592
- # order-statistic 95% CI on the median (normal approximation to the
593
- # binomial ranks); degenerate at tiny n, where it spans the sample
594
  half = 1.959964 * (n_k ** 0.5) / 2.0
595
  lo_i = max(0, int(n_k / 2 - half) - 1)
596
  hi_i = min(n_k - 1, int(n_k / 2 + half))
@@ -605,12 +884,25 @@ def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
605
  "mean_ms": round(sum(kept) / n_k, 4),
606
  "samples": len(flat),
607
  "rejected_throttled": rejected,
 
608
  "graph_captured": graph_state == "captured",
609
  "clock_ghz": {"best": round(best_clk, 3),
610
  "worst": round(min(clocks), 3)},
611
  }
 
 
 
 
 
 
 
612
  if graph_reason:
613
  rep["graph_fallback_reason"] = graph_reason
 
 
 
 
 
614
  if bytes is not None:
615
  rep["achieved_gbs"] = round(bytes / (med * 1e-3) / 1e9, 1)
616
  if flops is not None:
@@ -620,6 +912,8 @@ def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
620
  if dossier is not None and bytes and flops:
621
  bw = max(dossier["dram_bw"]["value"].values()) * 1e9
622
  mma_v = dossier["mma"]["value"].get(dtype)
 
 
623
  peak = (mma_v if isinstance(mma_v, (int, float))
624
  else dossier["fma_f32"]["value"]) * 1e12
625
  ai = flops / bytes
@@ -630,13 +924,12 @@ def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
630
  "pct_of_roof": round(100 * (flops / (med * 1e-3)) / roof, 1),
631
  "peak_dtype": dtype,
632
  }
 
633
  return rep
634
 
635
 
636
  def _to_ordered_int(t):
637
- """Map float bit patterns to integers whose difference is the ULP
638
- distance: positive floats keep their bits, negative floats mirror
639
- below zero, so the mapping is monotone in the float value."""
640
  if t.dtype == torch.float32:
641
  u = t.view(torch.int32).to(torch.int64) & 0xFFFFFFFF
642
  half = 1 << 31
@@ -652,11 +945,12 @@ def _to_ordered_int(t):
652
 
653
 
654
  def ulp_diff(a, b):
655
- """Elementwise ULP distance between same-dtype tensors. For floats,
656
- NaNs in the same position count as 0 and a one-sided NaN counts as
657
- 2**62; for integer and bool tensors the distance is the absolute
658
- numeric difference (an exact op must be exactly equal)."""
659
- assert a.dtype == b.dtype and a.shape == b.shape
 
660
  if not a.is_floating_point():
661
  return (a.to(torch.int64) - b.to(torch.int64)).abs()
662
  ia, ib = _to_ordered_int(a), _to_ordered_int(b)
@@ -673,9 +967,7 @@ class order_shuffles:
673
 
674
  @staticmethod
675
  def last_dim():
676
- """Permute the last dim of every tensor arg identically: the
677
- accumulation order of a last-dim reduction changes, the value
678
- does not."""
679
  def shuffle(args, seed):
680
  g = torch.Generator(device=args[0].device).manual_seed(seed)
681
  p = torch.randperm(args[0].shape[-1], device=args[0].device,
@@ -685,8 +977,7 @@ class order_shuffles:
685
 
686
  @staticmethod
687
  def matmul_k():
688
- """Permute K of (A [.., M, K], B [.., K, N]) consistently: the
689
- product is unchanged, the accumulation order is not."""
690
  def shuffle(args, seed):
691
  a_m, b_m = args[0], args[1]
692
  g = torch.Generator(device=a_m.device).manual_seed(seed)
@@ -700,37 +991,70 @@ def _as_outputs(x):
700
  return list(x) if isinstance(x, (tuple, list)) else [x]
701
 
702
 
703
- def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
704
- ref_variants=None, ulp_tol=None, atol=None, rtol=None,
705
- band_margin=4.0, zero_atol=None, worst_k=8, seed=0):
706
- """Adversarial correctness comparison with ULP evidence.
707
-
708
- `gen(seed) -> args`; `ref(*args)` and `cand(*args)` return a tensor
709
- or tuple (worst output governs). With `order_shuffle(args, seed)`
710
- reordering contractual-noise dimensions (see `order_shuffles`), each
711
- element is judged against its own measured POINTWISE reduction-order
712
- band: pass is max(ulp / (own_band + 2)) <= band_margin; without a
713
- band, max ULP <= ulp_tol (default 2); `atol`/`rtol` are an
714
- alternative acceptance route. `zero_atol`: elements with
715
- |ref| < zero_atol are judged by absolute difference (ULP explodes
716
- across the denormal range). The report carries the top-`worst_k`
717
- offenders (index, ref, cand, ULP). Order shuffles cover the
718
- reference's own topology; a candidate with a different legitimate
719
- topology (split-K) needs `ref_variants`, callables `f(args) -> out`
720
- whose outputs widen the pointwise band."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
721
  worst = {"max_ulp": -1}
722
  total_hist = {}
723
  per_trial = []
724
  max_abs_seen = 0.0
725
  max_rel_seen = 0.0
 
726
  for t in range(trials):
727
  args = gen(seed + t)
 
 
 
 
728
  r_outs = _as_outputs(ref(*args))
729
  c_outs = _as_outputs(cand(*args))
730
- assert len(r_outs) == len(c_outs), "output arity mismatch"
731
- # pointwise band: each element is judged against its own measured
732
- # order sensitivity (cancellation makes near-zero outputs
733
- # legitimately order-sensitive while neighbors are stable)
 
 
 
 
 
 
 
 
 
 
 
734
  bands = None
735
  band = 0
736
  band_score = 0.0
@@ -754,8 +1078,11 @@ def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
754
  max_ulp = 0
755
  mean_ulp = 0.0
756
  first_div = -1
 
757
  n_el = 0
758
  n_above_band = 0
 
 
759
  nan_r = nan_c = inf_r = inf_c = 0
760
  worst_elements = []
761
  for oi, (r, c) in enumerate(zip(r_outs, c_outs)):
@@ -773,8 +1100,11 @@ def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
773
  mean_ulp += float(d.double().sum().item())
774
  n_el += d.numel()
775
  nz = (d > 0)
 
 
776
  if first_div < 0 and nz.any():
777
  first_div = int(nz.flatten().nonzero()[0].item())
 
778
  if nz.any():
779
  buckets = torch.clamp(
780
  torch.log2(d[nz].double()).floor(), 0, 62
@@ -791,6 +1121,7 @@ def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
791
  if val > 0:
792
  worst_elements.append({
793
  "output": oi, "index": int(ix),
 
794
  "ref": float(rf[ix].item()),
795
  "cand": float(cf[ix].item()),
796
  "ulp": int(val)})
@@ -810,23 +1141,27 @@ def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
810
  inf_r += int(torch.isinf(r).sum().item())
811
  inf_c += int(torch.isinf(c).sum().item())
812
  worst_elements.sort(key=lambda w: -w["ulp"])
 
813
  trial_rep = {
814
  "max_ulp": max_ulp,
815
  "mean_ulp": round(mean_ulp / max(n_el, 1), 3),
816
  "first_divergent_index": first_div,
 
817
  "nan_ref": nan_r, "nan_cand": nan_c,
818
  "inf_ref": inf_r, "inf_cand": inf_c,
819
  "order_band_ulp": band,
820
  "band_score": round(band_score, 2),
821
  "frac_above_band": round(n_above_band / max(n_el, 1), 6),
 
 
822
  "worst_elements": worst_elements[:worst_k],
823
  }
824
  per_trial.append(trial_rep)
825
  if max_ulp > worst["max_ulp"]:
826
  worst = trial_rep
827
- band_max = max(t["order_band_ulp"] for t in per_trial)
828
  max_seen = max(t["max_ulp"] for t in per_trial)
829
- if order_shuffle is not None or ref_variants:
 
830
  score = max(t["band_score"] for t in per_trial)
831
  ulp_ok = score <= band_margin
832
  criterion = (f"pointwise max(ulp / (order_band + 2)) = "
@@ -841,8 +1176,34 @@ def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
841
  max_rel_seen <= (rtol or 0) or max_abs_seen <= (atol or 0)
842
  criterion += (f"; OR max_abs {max_abs_seen:.3e} <= atol {atol} / "
843
  f"max_rel {max_rel_seen:.3e} <= rtol {rtol}")
844
- return {
845
- "pass": bool(ulp_ok or tol_ok),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
846
  "criterion": criterion,
847
  "worst_trial": worst,
848
  "max_abs": max_abs_seen,
@@ -850,6 +1211,9 @@ def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
850
  "trials": per_trial,
851
  "ulp_hist_log2": dict(sorted(total_hist.items())),
852
  }
 
 
 
853
 
854
 
855
  _FUZZ_VARIANTS = ("contig", "transposed", "strided_rows", "offset_slice",
@@ -865,6 +1229,30 @@ def _context_alive(device):
865
  return False
866
 
867
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
868
  def _fuzz_build(make, shape, dtype):
869
  try:
870
  out = make(tuple(shape), dtype)
@@ -881,7 +1269,7 @@ def _fuzz_variant(x, v):
881
  if v == "strided_rows":
882
  return x[::2] if x.dim() >= 1 and x.shape[0] >= 2 else x
883
  if v == "offset_slice":
884
- # a storage-offset-1 view: misaligned for vectorized loads
885
  return x.flatten()[1:] if x.numel() > 1 else x
886
  if v == "overflow_expand":
887
  if x.numel() == 0:
@@ -895,8 +1283,7 @@ def _fuzz_variant(x, v):
895
 
896
  def _fuzz_group(op, ref, make, shape, dtype, variants, ulp_tol, device,
897
  stop_on_context_loss=True):
898
- """All variant cases for one (shape, dtype) group; returns (rows,
899
- context_lost)."""
900
  rows = []
901
  bases = _fuzz_build(make, shape, dtype)
902
  lost = False
@@ -933,8 +1320,7 @@ def _fuzz_group(op, ref, make, shape, dtype, variants, ulp_tol, device,
933
  got = op(*xs)
934
  want = ref(*xs)
935
  if v == "overflow_expand":
936
- # bounded comparison: the head plus the high-index tail,
937
- # which is where int32 flat indexing wraps
938
  w = 1 << 20
939
  gf, wf = got.flatten(), want.flatten()
940
  pairs = ((gf[:w], wf[:w]), (gf[-w:], wf[-w:]))
@@ -959,20 +1345,30 @@ def _fuzz_group(op, ref, make, shape, dtype, variants, ulp_tol, device,
959
  return rows, lost
960
 
961
 
962
- # isolate child: a plain subprocess that bootstraps its own metakernel
963
- # (get_kernel, else the dev-root JIT fallback) and unpickles op/ref/make
964
- # by module reference, so it works under get_kernel's synthetic names
965
- _REPO_ID = "phanerozoic/metakernel"
966
- _REPO_VERSION = 1
967
-
968
- _ISOLATE_BOOTSTRAP = r"""
969
- import json
970
- import os
971
- import pickle
972
- import sys
973
-
 
 
 
 
 
 
 
 
 
 
974
  meta = json.load(open(sys.argv[1], "r"))
975
- sys.path[:0] = meta["sys_path"] # BEFORE unpickling module-ref functions
976
  try:
977
  import kernels
978
  _mk = kernels.get_kernel(meta["repo"], version=meta["version"],
@@ -985,85 +1381,165 @@ except Exception:
985
  import load_local
986
  _mk = load_local.load()
987
  import torch
988
- op, ref, make = pickle.load(open(sys.argv[2], "rb"))
989
- rows, _lost = _mk._fuzz_group(
990
- op, ref, make, tuple(meta["shape"]), getattr(torch, meta["dtype"]),
991
- tuple(meta["variants"]), meta["ulp_tol"], meta["device"],
992
- stop_on_context_loss=False)
993
- pickle.dump(rows, open(sys.argv[3], "wb"))
 
 
 
 
 
 
 
 
 
 
 
 
994
  """
995
 
996
 
997
- def fuzz_shapes(op, ref, make, shapes=None, variants=_FUZZ_VARIANTS,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
998
  dtypes=(torch.float32,), ulp_tol=64, isolate=False,
999
- isolate_timeout_s=180, device="cuda"):
1000
- """Run op vs ref across edge shapes, dtypes, and layout mutations;
1001
- report ok / mismatch / exception per case.
1002
-
1003
- `make(shape, dtype)` (or `make(shape)`) builds a tensor or tuple;
1004
- layout variants derive non-contiguous and misaligned views whose
1005
- content equals a contiguous copy. `overflow_expand` stride-0-expands
1006
- past 2**31 logical elements to catch int32 indexing (`skipped_vram`
1007
- when it would not fit). Every case carries a `repro` recipe.
1008
- In-process, the CUDA context is health-checked after exceptions and
1009
- remaining cases marked `context_lost` if killed; `isolate=True`
1010
- spawns each (shape, dtype) group in a subprocess, so a
1011
- context-killing candidate costs one group (`crashed_isolated`);
1012
- op/ref/make must be picklable."""
1013
  if shapes is None:
1014
  shapes = [(), (7,), (0, 8), (1, 1), (1, 17), (3, 63), (17, 129),
1015
  (128, 128), (1023, 5), (2, 4096), (2, 3, 5)]
 
 
1016
 
1017
  rows = []
1018
  lost = False
1019
  if isolate:
1020
  import json
1021
  import pickle
1022
- import subprocess
1023
  import sys as _sys
1024
  import tempfile
1025
  tmpdir = tempfile.mkdtemp(prefix="mk_fuzz_")
1026
  fns = os.path.join(tmpdir, "fns.pkl")
 
 
1027
  with open(fns, "wb") as f:
1028
- pickle.dump((op, ref, make), f)
1029
- for dtype in dtypes:
1030
- for shape in shapes:
1031
- meta = os.path.join(tmpdir, "meta.json")
1032
- out = os.path.join(tmpdir, "rows.pkl")
1033
- if os.path.exists(out):
1034
- os.remove(out)
1035
- with open(meta, "w", encoding="utf-8") as f:
1036
- json.dump({
1037
- "sys_path": [p for p in _sys.path
1038
- if isinstance(p, str)],
1039
- "repo": _REPO_ID, "version": _REPO_VERSION,
 
 
 
 
1040
  "shape": list(shape),
1041
  "dtype": str(dtype).replace("torch.", ""),
1042
  "variants": list(variants),
1043
  "ulp_tol": ulp_tol, "device": device,
1044
- }, f)
1045
- group = None
1046
- stderr_tail = ""
1047
- try:
1048
- r = subprocess.run(
1049
- [_sys.executable, "-c", _ISOLATE_BOOTSTRAP, meta,
1050
- fns, out], timeout=isolate_timeout_s,
1051
- capture_output=True)
1052
- if r.returncode == 0 and os.path.exists(out):
1053
  with open(out, "rb") as f:
1054
- group = pickle.load(f)
1055
- exitcode = r.returncode
1056
- stderr_tail = r.stderr.decode(
1057
- errors="replace")[-400:]
1058
- except subprocess.TimeoutExpired:
1059
- exitcode = "timeout"
1060
- if group is None:
1061
- group = [{"shape": tuple(shape), "variant": "*",
1062
- "dtype": str(dtype).replace("torch.", ""),
1063
- "status": "crashed_isolated",
1064
- "exitcode": exitcode,
1065
- "stderr_tail": stderr_tail}]
1066
- rows.extend(group)
1067
  else:
1068
  pending = [(dt, sh) for dt in dtypes for sh in shapes]
1069
  for gi, (dtype, shape) in enumerate(pending):
@@ -1080,21 +1556,27 @@ def fuzz_shapes(op, ref, make, shapes=None, variants=_FUZZ_VARIANTS,
1080
  n_bad = sum(1 for r in rows
1081
  if r["status"] not in ("ok", "skipped_vram",
1082
  "skipped_empty"))
1083
- return {"cases": rows, "failed": n_bad, "total": len(rows),
1084
- "context_lost": lost}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1085
 
1086
 
1087
  def sweep(factory, grid, iters=30, warmup=5, bytes=None, flops=None,
1088
  check=None, budget_s=None, race=False):
1089
- """Launch-configuration search: `factory(**point) -> callable`; one
1090
- bench row per grid point, sorted fastest first. A point whose factory
1091
- or run raises is reported, not skipped silently. `check(fn) -> bool`
1092
- is an optional correctness gate recorded per row (a fast wrong config
1093
- must not win a sweep). `budget_s` caps total sweep time; points not
1094
- reached are reported as `not_run` rather than silently dropped.
1095
- `race=True` runs successive halving: a quick pass over every point at
1096
- iters/4, then the full budget only on the fastest quartile; pruned
1097
- points keep their quick measurement and status `pruned_race`."""
1098
  keys = list(grid.keys())
1099
 
1100
  def expand(i, acc):
@@ -1159,13 +1641,71 @@ def sweep(factory, grid, iters=30, warmup=5, bytes=None, flops=None,
1159
  continue
1160
  rows.append(run_point(p, iters))
1161
  rows.sort(key=lambda r: (not r.get("check", True), r["median_ms"]))
 
 
 
 
 
 
 
 
 
 
 
1162
  return rows
1163
 
1164
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1165
  def alloc_stamps(n, blocks=None, device="cuda"):
1166
- """Device buffer for the MK_STAMP convention (metakernel_cuda/
1167
- stamps.cuh): the candidate kernel writes clock64() into slot i. With
1168
- `blocks`, allocates the [blocks, n] layout for MK_STAMP_BLOCK."""
1169
  if blocks is not None:
1170
  return torch.zeros(blocks, n, dtype=torch.int64, device=device)
1171
  return torch.zeros(n, dtype=torch.int64, device=device)
@@ -1175,8 +1715,7 @@ _DRIFT_SKIP = ("device", "clock", "launch_overhead", "transfer_bw")
1175
 
1176
 
1177
  def pinned_dossiers():
1178
- """Names of the dossiers pinned in this release (one per certified
1179
- architecture)."""
1180
  return sorted(_PINNED.keys())
1181
 
1182
 
@@ -1195,11 +1734,7 @@ def _numeric_leaves(d, prefix=""):
1195
 
1196
 
1197
  def dossier_drift(current, pinned=None, rel_tol=0.35, skip=_DRIFT_SKIP):
1198
- """Compare a fresh dossier against the release-pinned one for the
1199
- same architecture: per-leaf relative drift, worst drift, entries
1200
- over `rel_tol`. Host-dependent entries and raw clocks are skipped by
1201
- default (`skip=()` compares everything); `pinned=None` auto-selects
1202
- by sm_arch and raises if no pin exists."""
1203
  if pinned is None:
1204
  arch = current["device"]["sm_arch"]
1205
  matches = [k for k, v in _PINNED.items()
@@ -1222,16 +1757,19 @@ def dossier_drift(current, pinned=None, rel_tol=0.35, skip=_DRIFT_SKIP):
1222
  "rel": round(rel, 4)})
1223
  worst = max((e["rel"] for e in entries), default=0.0)
1224
  over = [e for e in entries if e["rel"] > rel_tol]
1225
- return {"entries": entries, "worst_rel": worst, "over_tol": over,
1226
- "rel_tol": rel_tol, "compared": len(entries),
1227
- "pass": len(over) == 0}
 
 
 
 
 
1228
 
1229
 
1230
  def stamped_demo(n=1 << 22, fma_iters=4000, blocks=None, threads=256,
1231
  device="cuda"):
1232
- """Run the shipped MK_STAMP_BLOCK example kernel (global load, FMA
1233
- compute, global store) and decode its stamps; a template for
1234
- instrumenting a candidate."""
1235
  props = torch.cuda.get_device_properties(device)
1236
  if blocks is None:
1237
  blocks = props.multi_processor_count
@@ -1244,11 +1782,7 @@ def stamped_demo(n=1 << 22, fma_iters=4000, blocks=None, threads=256,
1244
 
1245
 
1246
  def read_stamps(stamps, labels=None, clock_ghz=None):
1247
- """Decode MK_STAMP phases: consecutive deltas in ticks and in
1248
- microseconds under the measured (or provided) SM clock. A 1-D buffer
1249
- (MK_STAMP: block 0 only) yields one delta per phase; a 2-D [B, n]
1250
- buffer (MK_STAMP_BLOCK) yields min / median / max across blocks per
1251
- phase, with blocks whose stamps stayed zero excluded."""
1252
  if clock_ghz is None:
1253
  clock_ghz = measure_clock(stamps.device)
1254
 
@@ -1284,3 +1818,104 @@ def read_stamps(stamps, labels=None, clock_ghz=None):
1284
  return {"phases": phases, "clock_ghz": round(clock_ghz, 3),
1285
  "blocks_reporting": int(live.sum().item()),
1286
  "blocks_total": int(live.numel())}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Measuring instrument for the kernel-design loop: measured device facts, throttle-aware timing, adversarial correctness comparison, all as loadable ops returning plain data."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  import math
3
  import os
4
  import time
 
8
  try:
9
  from ._ops import ops
10
  except ImportError: # pragma: no cover - spawned child in a source tree
11
+ # children re-import the package; a source checkout rebuilds the op table from the cached JIT extension
 
 
12
  _root = os.environ.get("METAKERNEL_DEV_ROOT")
13
  if not _root:
14
  raise
 
30
  except ImportError: # pragma: no cover - fresh tree before first pin
31
  _PINNED = {}
32
 
33
+ __all__ = ["probe_device", "probe_all_devices", "bench", "compare", "judge",
34
+ "order_shuffles", "fuzz_shapes", "sweep", "alloc_stamps",
35
+ "read_stamps", "stamped_demo", "measure_clock", "ulp_diff",
36
+ "dossier_drift", "pinned_dossiers", "mma_tile_check", "schema",
37
+ "probe_capturable", "ops"]
38
+
39
+ _REPO_ID = "phanerozoic/metakernel"
40
+ _REPO_VERSION = 1
41
+ _PROBE_SCHEMA = 2
42
+
43
+ # kind id, C-tile M*N, nominal K, nominal K-lanes per unrolled loop iteration
44
  _MMA_KINDS = {
45
+ "fp16": (0, 16 * 16, 16, 64.0),
46
+ "bf16": (1, 16 * 16, 16, 64.0),
47
+ "tf32": (2, 16 * 16, 8, 32.0),
48
+ "int8": (3, 16 * 16, 16, 64.0),
49
+ "fp8_e4m3": (4, 16 * 8, 32, 256.0),
50
+ "fp16_sparse_2_4": (5, 16 * 8, 32, 128.0),
51
+ "fp8_e5m2": (6, 16 * 8, 32, 256.0),
52
  }
53
 
54
 
 
57
 
58
 
59
  def _events(fn, reps):
60
+ """Time reps calls of fn with one cudaEvent pair each; returns a ms list."""
61
  starts = [torch.cuda.Event(enable_timing=True) for _ in range(reps)]
62
  stops = [torch.cuda.Event(enable_timing=True) for _ in range(reps)]
63
  for s, e in zip(starts, stops):
 
69
 
70
 
71
  def measure_clock(device="cuda", ticks=4_000_000, blocks=None):
72
+ """Effective SM clock in GHz from clock64 ticks over wall time under an FMA spin."""
 
73
  props = torch.cuda.get_device_properties(device)
74
  if blocks is None:
75
  blocks = props.multi_processor_count
 
102
 
103
 
104
  def _sattolo_ring(n, device, generator=None):
105
+ """Random single-cycle permutation as an int32 next-pointer table."""
 
106
  perm = torch.randperm(n, generator=generator)
107
  ring = torch.empty(n, dtype=torch.int32)
108
  ring[perm] = torch.roll(perm, -1).to(torch.int32)
 
110
 
111
 
112
  def _smi_settings(index):
113
+ """ECC mode, driver, power limit via nvidia-smi; None fields when unavailable."""
 
114
  import subprocess
115
  try:
116
  out = subprocess.run(
 
130
  return {"ecc": None, "driver": None, "power_limit_w": None}
131
 
132
 
133
+ def _unravel(flat, shape):
134
+ """Multi-dimensional index for a flat index into shape."""
135
+ if not shape:
136
+ return []
137
+ out = []
138
+ for s in reversed(list(shape)):
139
+ out.append(int(flat % s))
140
+ flat //= s
141
+ return list(reversed(out))
142
+
143
+
144
  def mma_tile_check(device="cuda", trials=8):
145
+ """Random small-integer m16n16k16 tiles through the wmma path, compared exactly to the reference GEMM."""
 
 
146
  worst = 0.0
147
  for t in range(trials):
148
  g = torch.Generator(device=device).manual_seed(1000 + t)
 
160
  "trials": trials}
161
 
162
 
163
+ def _tenancy_canary(device, rel_tol=0.10, attempts=3):
164
+ """Fixed-cost triad timed in paired rounds; busy only when every attempt disagrees."""
165
+ n4 = 4 << 20
166
+ a = torch.rand(n4 * 4, device=device)
167
+ b = torch.rand(n4 * 4, device=device)
168
+ c = torch.empty(n4 * 4, device=device)
169
+ ops.mk_triad(a, b, c, 4, 1.5)
170
+ rels = []
171
+ for _ in range(attempts):
172
+ r1 = _median(_events(lambda: ops.mk_triad(a, b, c, 4, 1.5), 3))
173
+ r2 = _median(_events(lambda: ops.mk_triad(a, b, c, 4, 1.5), 3))
174
+ rel = abs(r1 - r2) / min(r1, r2)
175
+ rels.append(round(rel, 4))
176
+ if rel <= rel_tol:
177
+ return False, {"rel_diffs": rels, "rel_tol": rel_tol}
178
+ return True, {"rel_diffs": rels, "rel_tol": rel_tol}
179
+
180
+
181
+ def _cache_dir():
182
+ base = os.environ.get("LOCALAPPDATA") or os.path.expanduser("~/.cache")
183
+ d = os.path.join(base, "metakernel")
184
+ os.makedirs(d, exist_ok=True)
185
+ return d
186
+
187
+
188
+ def _cache_path(device, quick):
189
+ import re
190
+ props = torch.cuda.get_device_properties(device)
191
+ smi = _smi_settings(torch.device(device).index or 0)
192
+ slug = re.sub(r"[^a-z0-9]+", "", props.name.lower())[:24]
193
+ key = "sm%d%d_%s_drv%s_cu%s_q%d_s%d" % (
194
+ props.major, props.minor, slug, smi["driver"] or "na",
195
+ torch.version.cuda, int(quick), _PROBE_SCHEMA)
196
+ return os.path.join(_cache_dir(), key + ".json")
197
+
198
+
199
+ def _mma_calibrate(kind_id, sink):
200
+ """Executed K-lanes per loop iteration from a sub-saturation accumulator read-back, with a linearity check."""
201
+ gains = []
202
+ for cal in (4, 32):
203
+ sink[1] = 0.0
204
+ ops.mk_mma(kind_id, 1, 1, cal, sink)
205
+ _sync()
206
+ gains.append(float(sink[1].item()) / cal)
207
+ linear = gains[0] > 0 and abs(gains[1] - gains[0]) / gains[0] < 0.02
208
+ return gains[0], linear
209
+
210
+
211
+ def _fp8_accum_stall(kind_id, sink, k_exec):
212
+ """Growth curve of the fp8 accumulator; a stall reveals the effective accumulate mantissa width."""
213
+ vals = []
214
+ for it in (32, 64, 128, 256, 512):
215
+ sink[1] = 0.0
216
+ ops.mk_mma(kind_id, 1, 1, it, sink)
217
+ _sync()
218
+ vals.append(float(sink[1].item()) / 2.0)
219
+ if vals[-1] == vals[-2] and vals[-1] < vals[0] * 16 * 0.99:
220
+ bits = int(round(math.log2(vals[-1] / (2.0 * k_exec))))
221
+ return {"stall_element_value": vals[-1],
222
+ "effective_accum_mantissa_bits": bits}
223
+ return {"stall_element_value": None,
224
+ "effective_accum_mantissa_bits": None}
225
+
226
+
227
+ def probe_device(device="cuda", quick=False, cached=True,
228
+ tenancy_check=True):
229
+ """Empirical device dossier; every entry measured by a dedicated probe kernel and stamped with its SM clock."""
230
+ cpath = _cache_path(device, quick)
231
+ if cached and os.path.exists(cpath):
232
+ import json
233
+ return json.load(open(cpath, encoding="utf-8"))
234
+ if tenancy_check:
235
+ busy, ev = _tenancy_canary(device)
236
+ if busy:
237
+ raise RuntimeError("device_busy: canary rounds disagree %s; "
238
+ "idle the device or pass "
239
+ "tenancy_check=False" % ev)
240
  props = torch.cuda.get_device_properties(device)
241
  sm = props.multi_processor_count
242
  l2 = props.L2_cache_size
 
249
  "vram_bytes": props.total_memory,
250
  "torch": torch.__version__,
251
  "cuda_runtime": torch.version.cuda,
252
+ # instrument settings recorded because readings depend on them
 
253
  **_smi_settings(torch.device(device).index or 0),
254
  }
255
  }
 
274
  }
275
  clk = max(traj)
276
 
277
+ # ---- device-memory bandwidth per access width plus read/write/gather
278
+ n4 = 16 << 20
 
279
  if props.total_memory < 4 << 30:
280
  n4 = 4 << 20
281
  a = torch.rand(n4 * 4, device=device)
 
299
  dtype=torch.int32).to(device)
300
  ops.mk_gather(a, idx, c, 256, 0)
301
  ms = _median(_events(lambda: ops.mk_gather(a, idx, c, 256, 0), reps))
 
302
  hbm["random_gather_16B"] = round(
303
  (idx.numel() * 4 + 2 * n4 * 16) / (ms * 1e-3) / 1e9, 1)
304
  d["dram_bw"] = _entry(hbm, "GB/s", "streaming triad c=a+s*b per access "
 
309
  "so its nominal number is the achievable rate for "
310
  "random access, not a line-efficiency claim")
311
 
312
+ # ---- L2 bandwidth over an L2-resident working set
313
  ws = max(l2 // 4, 1 << 20)
314
+ nl2 = ws // 16
315
  al = a[:nl2 * 4].contiguous()
316
  bl = b[:nl2 * 4].contiguous()
317
  cl = c[:nl2 * 4].contiguous()
 
326
  "medium", "first pass streams from HBM; amortized "
327
  f"over {passes} passes")
328
 
329
+ # ---- latencies: min of 3 chases per level so a co-tenant burst cannot land in the dossier
 
 
330
  lat = {}
331
  idle_clk = measure_clock(device, ticks=2_000_000, blocks=1)
 
 
 
 
 
 
 
332
 
333
+ def chase(op, ring, warm_hops, hops, extra=None):
334
+ best = None
335
+ for _ in range(3):
336
+ if extra is None:
337
+ op(ring, warm_hops, out64)
338
+ _sync()
339
+ op(ring, hops, out64)
340
+ else:
341
+ op(ring, extra, warm_hops, out64)
342
+ _sync()
343
+ op(ring, extra, hops, out64)
344
+ _sync()
345
+ t = int(out64[0].item()) / hops
346
+ best = t if best is None else min(best, t)
347
+ return best
 
 
348
 
349
+ ring_s = _sattolo_ring(8192, device)
350
+ lat["smem_cycles"] = round(
351
+ chase(lambda r, h, o: ops.mk_chase_shared(r, h, o), ring_s,
352
+ 1 << 14, 1 << 16), 1)
353
+ ring_l1 = _sattolo_ring((16 << 10) // 4, device)
354
+ lat["l1_cycles"] = round(
355
+ chase(lambda r, h, o: ops.mk_chase_global(r, h, o), ring_l1,
356
+ ring_l1.numel(), 1 << 15), 1)
357
+ ring_l2 = _sattolo_ring((2 << 20) // 4, device)
358
+ lat["l2_cycles"] = round(
359
+ chase(lambda r, h, o: ops.mk_chase_global(r, h, o), ring_l2,
360
+ ring_l2.numel(), 1 << 14), 1)
361
  hbm_bytes = min(max(4 * l2, 64 << 20), props.total_memory // 8)
362
  ring_h = _sattolo_ring(int(hbm_bytes) // 4, device)
363
  hops_h = max((2 * l2) // 128, 1 << 17)
364
+ lat["dram_cycles"] = round(
365
+ chase(lambda r, h, o: ops.mk_chase_global(r, h, o), ring_h,
366
+ 1 << 14, hops_h), 1)
 
 
367
  lat["ns_per_cycle"] = round(1.0 / idle_clk, 3)
368
  d["latency"] = _entry(lat, "cycles (single-lane dependent loads)",
369
+ "Sattolo-cycle pointer chase, min of 3 runs; 16 "
370
+ "KB ring for L1, 2 MB TLB-resident ring for L2, "
371
+ "self-evicting chain (2x L2 in touched lines) "
372
+ "for HBM", idle_clk, "high",
373
  "smem includes index arithmetic; HBM includes "
374
  "residual TLB effects at large footprints")
375
  del ring_h, ring_l2, ring_l1, ring_s
376
 
377
+ # ---- fp32/fp64 FMA and tensor-core throughput
378
  blocks = sm * 4
379
  iters = 20_000 if quick else 60_000
380
  ops.mk_fma_f32(blocks, 256, iters, sink)
381
  ms = _median(_events(lambda: ops.mk_fma_f32(blocks, 256, iters, sink),
382
  reps))
383
+ flops = blocks * 256 * iters * 16 * 2 * 2
384
  d["fma_f32"] = _entry(round(flops / (ms * 1e-3) / 1e12, 2), "TFLOP/s",
385
  "register-resident FMA chains", clk, "high")
386
  sink64 = torch.zeros(2, dtype=torch.float64, device=device)
 
392
  d["fma_f64"] = _entry(round(flops64 / (ms * 1e-3) / 1e12, 3), "TFLOP/s",
393
  "register-resident double FMA chains (the DFMA "
394
  "pipe)", clk, "high")
395
+
396
+ # ---- ILP ladder: 1/2/4 chains at low occupancy separates latency-bound from pipe-bound
397
+ ilp = {}
398
+ it_ilp = 20_000 if quick else 60_000
399
+ for ch in (1, 2, 4):
400
+ ops.mk_fma_ilp(ch, sm, 64, it_ilp, sink)
401
+ ms = _median(_events(
402
+ lambda c2=ch: ops.mk_fma_ilp(c2, sm, 64, it_ilp, sink), reps))
403
+ ilp[f"ilp{ch}"] = round(
404
+ sm * 64 * it_ilp * 16 * 2 * ch / (ms * 1e-3) / 1e12, 2)
405
+ ilp["ratio_4_1"] = round(ilp["ilp4"] / ilp["ilp1"], 2)
406
+ d["fma_ilp"] = _entry(ilp, "TFLOP/s",
407
+ "1/2/4 independent dependent FMA chains per "
408
+ "thread at one block of 64 threads per SM; a "
409
+ "ratio near 4 means latency-bound at that "
410
+ "occupancy", clk, "high")
411
+
412
  mma = {}
413
  mma_klanes = {}
414
+ fp8_accum = {}
415
+ for name, (kind, mn, k_nom, nom_lanes) in _MMA_KINDS.items():
416
  it = 30_000 if quick else 100_000
417
  r = ops.mk_mma(kind, blocks, 8, 512, sink)
418
  if r != 0:
419
  mma[name] = "unsupported"
420
  continue
421
  _sync()
422
+ gain, linear = _mma_calibrate(kind, sink)
 
 
 
 
 
 
423
  mma_klanes[name] = {
424
+ "executed_k_per_iter": round(gain, 2),
425
+ "vs_nominal": round(gain / nom_lanes, 3),
426
+ "calibration_linear": linear,
427
  }
428
+ if name.startswith("fp8"):
429
+ fp8_accum[name] = _fp8_accum_stall(kind, sink, k_nom)
430
  ms = _median(_events(
431
  lambda k=kind, i=it: ops.mk_mma(k, blocks, 8, i, sink), reps))
432
+ tflops = blocks * 8 * it * 2.0 * mn * gain / (ms * 1e-3) / 1e12
433
+ if name == "fp16_sparse_2_4":
434
+ mma[name] = {"executed": round(tflops, 1),
435
+ "dense_equivalent": round(2 * tflops, 1)}
436
+ else:
437
+ mma[name] = round(tflops, 1)
438
  d["mma"] = _entry(mma, "TFLOP/s (TOP/s for int8)",
439
  "register-fragment mma_sync loops (wmma; fp8 and "
440
+ "2:4-sparse fp16 via inline PTX m16n8k32); flops "
441
+ "derive from a sub-saturation accumulator read-back "
 
442
  "with every fragment element observed, never the "
443
+ "declared loop shape; sparse reports executed and "
444
+ "dense-equivalent flops",
445
  clk, "medium",
446
  "dense-tile rate without memory traffic; real "
447
  "kernels see this only when staging hides behind it")
448
  d["mma"]["verified_k_lanes"] = mma_klanes
449
+ if fp8_accum:
450
+ d["mma"]["fp8_accum"] = fp8_accum
451
 
452
+ # ---- fed MMA: smem-staged fp16 tiles under cp.async double buffering
453
+ if props.major >= 8:
454
+ gbuf = torch.ones(16 << 20, dtype=torch.float16, device=device)
455
+ sink[1] = 0.0
456
+ r = ops.mk_mma_fed(gbuf, 1, 1, 64, sink)
457
+ if r == 0:
458
+ _sync()
459
+ fed_gain = float(sink[1].item()) / 64
460
+ it_fed = 6_000 if quick else 20_000
461
+ ops.mk_mma_fed(gbuf, sm * 2, 8, it_fed, sink)
462
+ ms = _median(_events(
463
+ lambda: ops.mk_mma_fed(gbuf, sm * 2, 8, it_fed, sink),
464
+ reps))
465
+ fed = sm * 2 * 8 * it_fed * 2.0 * 256 * fed_gain / \
466
+ (ms * 1e-3) / 1e12
467
+ d["mma_fed"] = _entry(
468
+ round(fed, 1), "TFLOP/s",
469
+ "m16n16k16 fp16 tiles fed from an L2-resident buffer "
470
+ "through cp.async double-buffered shared memory; "
471
+ "accumulator-verified", clk, "high",
472
+ "the achieved counterpart to the register peak: staging "
473
+ "exposed, one dependent accumulator chain per warp")
474
+ del gbuf
475
+
476
+ # ---- shared-memory bandwidth and the bank-conflict stride sweep
477
  it_sm = 30_000 if quick else 100_000
478
  ops.mk_smem_bw(blocks, 256, it_sm, sink)
479
  ms = _median(_events(lambda: ops.mk_smem_bw(blocks, 256, it_sm, sink),
 
483
  "block-resident read+write streaming (8 B per "
484
  "thread-iteration), aggregated across SMs", clk,
485
  "medium",
486
+ "bank-conflict-light access pattern; the stride "
487
+ "sweep below prices the conflicted case")
488
+ strides = (1, 8, 32) if quick else (1, 2, 4, 8, 16, 32)
489
+ it_st = 8_000 if quick else 20_000
490
+ st_ms = {}
491
+ for s2 in strides:
492
+ ops.mk_smem_stride(s2, blocks, 256, it_st, sink)
493
+ st_ms[s2] = _median(_events(
494
+ lambda s3=s2: ops.mk_smem_stride(s3, blocks, 256, it_st, sink),
495
+ reps))
496
+ d["smem_conflict_penalty"] = _entry(
497
+ {f"stride_{s2}": round(st_ms[s2] / st_ms[1], 2) for s2 in strides},
498
+ "x (time ratio vs stride 1)",
499
+ "dependent read-FMA-write per thread at warp-lane bank distance "
500
+ "`stride`; stride 32 puts every lane in one bank", clk, "high",
501
+ "the direct conflict measurement that disambiguates a "
502
+ "bank-conflict signature from an ILP stall")
503
 
504
  # ---- PCIe / host transfer bandwidth
505
  nbytes = 256 << 20
 
530
  "d2d counts one direction of traffic")
531
  del host_pin, host_pag, dev_t, dev_u
532
 
533
+ # ---- atomic contention curve
 
 
534
  n_thr = blocks * 256
535
  buf = torch.zeros(n_thr, dtype=torch.float32, device=device)
536
  at = {"curve_slots": {}}
 
540
  buf.zero_()
541
  ops.mk_atomics(1, blocks, 256, it_at, buf, slots)
542
  ms = _median(_events(
543
+ lambda s4=slots: ops.mk_atomics(1, blocks, 256, it_at, buf, s4),
544
  reps))
545
  at["curve_slots"][str(slots)] = round(
546
  n_thr * it_at / (ms * 1e-3) / 1e9, 2)
 
553
  "of distinct global addresses from 1 (full "
554
  "contention) to one per thread", clk, "high")
555
 
556
+ # ---- launch overhead: torch-dispatch path and the raw C++ path
557
  anchor = sink
558
  n_launch = 500 if quick else 2000
559
  _sync()
 
568
  ops.mk_empty(anchor)
569
  _sync()
570
  rt = (time.perf_counter() - t0) / n_rt * 1e6
571
+ raw_q = float(ops.mk_launch_raw(n_launch, 0, anchor))
572
+ raw_rt = float(ops.mk_launch_raw(n_rt, 1, anchor))
573
  d["launch_overhead"] = _entry(
574
+ {"torch_queued_us": round(queued, 2),
575
+ "torch_round_trip_us": round(rt, 2),
576
+ "raw_queued_us": round(raw_q / n_launch * 1e6, 2)
577
+ if raw_q >= 0 else None,
578
+ "raw_round_trip_us": round(raw_rt / n_rt * 1e6, 2)
579
+ if raw_rt >= 0 else None},
580
+ "us", "empty-kernel trains; torch path per-launch through op "
581
+ "dispatch, raw path looped inside one binding call", clk,
582
+ "medium", "host-state-dependent; the torch-minus-raw gap is the "
583
+ "dispatch cost a torch-launched candidate pays")
584
 
585
  # ---- grid barrier
586
  bar_iters = 500 if quick else 2000
 
596
  "unavailable or grid too large", clk,
597
  "high")
598
 
599
+ # ---- occupancy API table plus timed knees
 
 
600
  occ = {}
601
  for threads in (128, 256, 512, 1024):
602
  row = {}
 
617
  ops.mk_fma_dep(knee_blocks, threads, smem_kb * 1024,
618
  knee_iters, sink)
619
  ms = _median(_events(
620
+ lambda t=threads, s5=smem_kb: ops.mk_fma_dep(
621
+ knee_blocks, t, s5 * 1024, knee_iters, sink), reps))
622
  fl = knee_blocks * threads * knee_iters * 16 * 2
623
  row[f"{smem_kb}KB"] = round(fl / (ms * 1e-3) / 1e12, 2)
624
  knees[f"t{threads}"] = row
 
627
  "at forced (threads, dyn smem) occupancies over fixed total work; "
628
  "the cliff marks where occupancy stops covering latency",
629
  clk, "high", "timing-derived counterpart to the API table above")
630
+ if cached:
631
+ import json
632
+ json.dump(d, open(cpath, "w", encoding="utf-8"))
633
  return d
634
 
635
 
636
+ def probe_all_devices(quick=True, cached=True, tenancy_check=True):
637
+ """One dossier per visible device plus a pairwise P2P bandwidth matrix."""
638
+ n = torch.cuda.device_count()
639
+ devices = {}
640
+ for i in range(n):
641
+ devices[i] = probe_device(f"cuda:{i}", quick=quick, cached=cached,
642
+ tenancy_check=tenancy_check)
643
+ p2p = {}
644
+ if n > 1:
645
+ nbytes = 64 << 20
646
+ for i in range(n):
647
+ for j in range(n):
648
+ if i == j:
649
+ continue
650
+ src = torch.rand(nbytes // 4, device=f"cuda:{i}")
651
+ dst = torch.empty(nbytes // 4, device=f"cuda:{j}")
652
+ dst.copy_(src)
653
+ torch.cuda.synchronize(i)
654
+ torch.cuda.synchronize(j)
655
+ t0 = time.perf_counter()
656
+ for _ in range(5):
657
+ dst.copy_(src)
658
+ torch.cuda.synchronize(i)
659
+ torch.cuda.synchronize(j)
660
+ ms = (time.perf_counter() - t0) / 5 * 1e3
661
+ p2p[f"{i}->{j}"] = {
662
+ "gbs": round(nbytes / (ms * 1e-3) / 1e9, 1),
663
+ "peer_access": bool(
664
+ torch.cuda.can_device_access_peer(i, j)),
665
+ }
666
+ del src, dst
667
+ return {"devices": devices, "p2p": p2p}
668
+
669
+
670
+ _CAPTURE_PROBE = r"""
671
+ import pickle, sys, torch
672
+ fn = pickle.load(open(sys.argv[1], "rb"))
673
+ for _ in range(3):
674
+ fn()
675
+ torch.cuda.synchronize()
676
+ try:
677
+ g = torch.cuda.CUDAGraph()
678
+ with torch.cuda.graph(g):
679
+ fn()
680
+ print("capturable", flush=True)
681
+ except Exception as e:
682
+ print("uncapturable: %s: %s" % (type(e).__name__, str(e)[:120]),
683
+ flush=True)
684
+ """
685
+
686
+
687
+ def probe_capturable(fn, timeout_s=180):
688
+ """Test CUDA-graph capturability of fn in a child process, so a capture-wedged RNG never reaches the caller."""
689
+ import pickle
690
+ import subprocess
691
+ import sys as _sys
692
+ import tempfile
693
+ try:
694
+ blob = pickle.dumps(fn)
695
+ except Exception: # noqa: BLE001
696
+ return {"capturable": None,
697
+ "reason": "fn is not picklable; probe skipped (define fn "
698
+ "at module top level to enable it)"}
699
+ path = os.path.join(tempfile.mkdtemp(prefix="mk_cap_"), "fn.pkl")
700
+ with open(path, "wb") as f:
701
+ f.write(blob)
702
+ try:
703
+ r = subprocess.run([_sys.executable, "-c", _CAPTURE_PROBE, path],
704
+ capture_output=True, text=True,
705
+ timeout=timeout_s)
706
+ except Exception as e: # noqa: BLE001
707
+ return {"capturable": None,
708
+ "reason": f"probe child failed: {type(e).__name__}"}
709
+ out = (r.stdout or "").strip()
710
+ if out == "capturable":
711
+ return {"capturable": True, "reason": ""}
712
+ if out.startswith("uncapturable"):
713
+ return {"capturable": False, "reason": out}
714
+ return {"capturable": None,
715
+ "reason": "probe child exit %s: %s" % (
716
+ r.returncode, (r.stderr or "")[-200:])}
717
+
718
+
719
+ def _bench_verdict(rep):
720
+ parts = ["median %.4g ms" % rep["median_ms"]]
721
+ if "achieved_tflops" in rep:
722
+ parts.append("%.4g TFLOP/s" % rep["achieved_tflops"])
723
+ elif "achieved_gbs" in rep:
724
+ parts.append("%.4g GB/s" % rep["achieved_gbs"])
725
+ if "roofline" in rep:
726
+ parts.append("%.0f%% of %s roof" % (rep["roofline"]["pct_of_roof"],
727
+ rep["roofline"]["bound"]))
728
+ if rep["rejected_throttled"]:
729
+ parts.append("%d/%d rejected (%s)" % (
730
+ rep["rejected_throttled"], rep["samples"],
731
+ rep["rejection_basis"]))
732
+ if rep.get("no_clean_window"):
733
+ parts.append("no clean window: the whole run was throttled")
734
+ if rep.get("alloc_growth_bytes_per_call"):
735
+ parts.append("fn allocates %d B/call" %
736
+ rep["alloc_growth_bytes_per_call"])
737
+ if rep.get("host_overhead_suspected"):
738
+ parts.append("fn appears to sync or do host work inside")
739
+ return "; ".join(parts)
740
+
741
+
742
  def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
743
  dtype="fp16", clock_guard=0.97, clock_stride=8, graph=False,
744
+ graph_probe_isolated=False, device="cuda"):
745
+ """Median-of-N cudaEvent timing with throttle rejection, roofline placement, and footgun detection."""
746
+ for name, v in (("iters", iters), ("warmup", warmup)):
747
+ if not isinstance(v, int) or v < (1 if name == "iters" else 0):
748
+ raise ValueError(f"{name} must be a non-negative int; "
749
+ f"got {v!r}")
750
+ for name, v in (("bytes", bytes), ("flops", flops)):
751
+ if v is not None and (not isinstance(v, (int, float)) or v <= 0):
752
+ raise ValueError(f"{name} must be a positive number counting "
753
+ f"{name} per call of fn; got {v!r}")
 
754
  for _ in range(warmup):
755
  fn()
756
  _sync()
757
+ # footgun probes: per-call allocation growth and hidden host work
758
+ alloc0 = torch.cuda.memory_allocated(device)
759
+ fn()
760
+ fn()
761
+ _sync()
762
+ alloc_growth = max(0, (torch.cuda.memory_allocated(device) - alloc0)
763
+ // 2)
764
+ # a burst of enqueues that leaves the stream idle means fn drains it; only trustworthy above the launch-overhead floor
765
+ for _ in range(16):
766
+ fn()
767
+ stream_idle = bool(torch.cuda.current_stream(device).query())
768
+ _sync()
769
+ t0 = time.perf_counter()
770
+ pre = _events(fn, 4)
771
+ wall_ms = (time.perf_counter() - t0) * 1e3
772
+ ev_ms = sum(pre)
773
+ host_suspect = (stream_idle and ev_ms / 4 > 0.05) or \
774
+ wall_ms > ev_ms * 1.5 + 2.0
775
+
776
  graph_state = "off"
777
  graph_reason = ""
778
+ if graph and graph_probe_isolated:
779
+ cap = probe_capturable(fn)
780
+ if cap["capturable"] is False:
781
+ graph = False
782
+ graph_state = "fallback_eager"
783
+ graph_reason = "isolated probe: " + cap["reason"]
784
  if graph:
785
  pre_rng = torch.cuda.get_rng_state(device)
786
  try:
 
795
  except Exception as e: # noqa: BLE001 - fall back, say why
796
  graph_state = "fallback_eager"
797
  graph_reason = f"{type(e).__name__}: {str(e)[:120]}"
798
+ # drain the invalidated stream; a failed capture can wedge the device RNG on some torch versions
 
 
 
799
  rng_ok = True
800
  for _attempt in range(5):
801
  try:
 
825
  samples.append((_events(fn, n), len(clocks) - 1))
826
  clocks.append(measure_clock(device, ticks=probe_ticks))
827
  best_clk = max(clocks)
828
+
829
+ # rejection basis: memory-bound work is judged on its own rate, not the SM clock
830
+ ridge_memory = False
831
+ if dossier is not None and bytes and flops:
832
+ bw0 = max(dossier["dram_bw"]["value"].values()) * 1e9
833
+ mma_v0 = dossier["mma"]["value"].get(dtype)
834
+ if isinstance(mma_v0, dict):
835
+ mma_v0 = mma_v0.get("dense_equivalent")
836
+ peak0 = (mma_v0 if isinstance(mma_v0, (int, float))
837
+ else dossier["fma_f32"]["value"]) * 1e12
838
+ ridge_memory = (flops / bytes) * bw0 < peak0
839
+ basis = "bandwidth_consistency" if (
840
+ bytes is not None and (flops is None or ridge_memory)) \
841
+ else "sm_clock"
842
+
843
  flat = []
844
+ kept = []
845
+ rejected = 0
846
+ if basis == "sm_clock":
847
+ for group, ci in samples:
848
+ window_ok = (clocks[ci] >= clock_guard * best_clk and
849
+ clocks[min(ci + 1, len(clocks) - 1)] >=
850
+ clock_guard * best_clk)
851
+ for ms in group:
852
+ flat.append(ms)
853
+ if window_ok:
854
+ kept.append(ms)
855
+ else:
856
+ rejected += 1
857
+ else:
858
+ for group, _ci in samples:
859
+ flat.extend(group)
860
+ floor = sorted(flat)[max(0, int(0.05 * len(flat)) - 1)]
861
+ for ms in flat:
862
+ if ms <= floor * 1.15:
863
  kept.append(ms)
864
  else:
865
  rejected += 1
866
+ all_rejected = not kept
867
+ if all_rejected:
868
  kept = flat
869
  med = _median(kept)
870
  ks = sorted(kept)
871
  n_k = len(ks)
872
+ # order-statistic 95% CI on the median; degenerate at tiny n
 
873
  half = 1.959964 * (n_k ** 0.5) / 2.0
874
  lo_i = max(0, int(n_k / 2 - half) - 1)
875
  hi_i = min(n_k - 1, int(n_k / 2 + half))
 
884
  "mean_ms": round(sum(kept) / n_k, 4),
885
  "samples": len(flat),
886
  "rejected_throttled": rejected,
887
+ "rejection_basis": basis,
888
  "graph_captured": graph_state == "captured",
889
  "clock_ghz": {"best": round(best_clk, 3),
890
  "worst": round(min(clocks), 3)},
891
  }
892
+ no_clean = all_rejected
893
+ if basis == "sm_clock" and dossier is not None:
894
+ ref_clk = dossier["clock"]["max_ghz"]
895
+ if best_clk < clock_guard * ref_clk:
896
+ no_clean = True
897
+ if no_clean:
898
+ rep["no_clean_window"] = True
899
  if graph_reason:
900
  rep["graph_fallback_reason"] = graph_reason
901
+ if alloc_growth:
902
+ rep["alloc_growth_bytes_per_call"] = int(alloc_growth)
903
+ if host_suspect:
904
+ rep["host_overhead_suspected"] = True
905
+ rep["host_wall_over_event"] = round(wall_ms / max(ev_ms, 1e-9), 2)
906
  if bytes is not None:
907
  rep["achieved_gbs"] = round(bytes / (med * 1e-3) / 1e9, 1)
908
  if flops is not None:
 
912
  if dossier is not None and bytes and flops:
913
  bw = max(dossier["dram_bw"]["value"].values()) * 1e9
914
  mma_v = dossier["mma"]["value"].get(dtype)
915
+ if isinstance(mma_v, dict):
916
+ mma_v = mma_v.get("dense_equivalent")
917
  peak = (mma_v if isinstance(mma_v, (int, float))
918
  else dossier["fma_f32"]["value"]) * 1e12
919
  ai = flops / bytes
 
924
  "pct_of_roof": round(100 * (flops / (med * 1e-3)) / roof, 1),
925
  "peak_dtype": dtype,
926
  }
927
+ rep["verdict"] = _bench_verdict(rep)
928
  return rep
929
 
930
 
931
  def _to_ordered_int(t):
932
+ """Monotone integer mapping of float bit patterns; differences are ULP distances."""
 
 
933
  if t.dtype == torch.float32:
934
  u = t.view(torch.int32).to(torch.int64) & 0xFFFFFFFF
935
  half = 1 << 31
 
945
 
946
 
947
  def ulp_diff(a, b):
948
+ """Elementwise ULP distance; both-NaN counts 0, one-sided NaN 2**62, integers by absolute difference."""
949
+ if a.dtype != b.dtype or a.shape != b.shape:
950
+ raise ValueError("ulp_diff needs matching dtype and shape; got "
951
+ f"{a.dtype}{tuple(a.shape)} vs "
952
+ f"{b.dtype}{tuple(b.shape)}; cast or reshape the "
953
+ "candidate to the reference layout")
954
  if not a.is_floating_point():
955
  return (a.to(torch.int64) - b.to(torch.int64)).abs()
956
  ia, ib = _to_ordered_int(a), _to_ordered_int(b)
 
967
 
968
  @staticmethod
969
  def last_dim():
970
+ """Identical permutation of every arg's last dim: reduction order changes, value does not."""
 
 
971
  def shuffle(args, seed):
972
  g = torch.Generator(device=args[0].device).manual_seed(seed)
973
  p = torch.randperm(args[0].shape[-1], device=args[0].device,
 
977
 
978
  @staticmethod
979
  def matmul_k():
980
+ """Consistent K permutation of (A [.., M, K], B [.., K, N]): product unchanged, accumulation order not."""
 
981
  def shuffle(args, seed):
982
  a_m, b_m = args[0], args[1]
983
  g = torch.Generator(device=a_m.device).manual_seed(seed)
 
991
  return list(x) if isinstance(x, (tuple, list)) else [x]
992
 
993
 
994
+ def _compare_hints(trial):
995
+ hints = []
996
+ if trial.get("bias_one_sided_frac", 0) >= 0.9 and \
997
+ trial.get("frac_above_band", 0) > 0.5:
998
+ hints.append("systematic one-sided bias: check scale factors or "
999
+ "the accumulator dtype")
1000
+ if trial["nan_cand"] > trial["nan_ref"]:
1001
+ hints.append("candidate introduces NaN: check uninitialized "
1002
+ "memory or masking")
1003
+ nd = trial.get("first_divergent_nd")
1004
+ if nd and nd[-1] > 0 and any(nd[-1] % t == 0 for t in (16, 32, 64,
1005
+ 128)):
1006
+ hints.append("divergence starts on a tile-aligned index: check "
1007
+ "boundary indexing")
1008
+ return hints
1009
+
1010
+
1011
+ def compare(ref, cand, gen=None, trials=8, order_shuffle=None,
1012
+ order_trials=8, ref_variants=None, ulp_tol=None, atol=None,
1013
+ rtol=None, band_margin=4.0, zero_atol=None, worst_k=8, seed=0,
1014
+ inputs=None, frac_above_tol=0.5):
1015
+ """Adversarial correctness comparison with pointwise order bands, ULP evidence, and a one-sided-bias gate; the banded route cannot reject errors under band_margin*(band+2) ULP at order-stable elements."""
1016
+ if (gen is None) == (inputs is None):
1017
+ raise ValueError("pass exactly one of gen (a seed -> args "
1018
+ "callable) or inputs (a tuple of concrete "
1019
+ "tensors)")
1020
+ if inputs is not None:
1021
+ if isinstance(inputs, torch.Tensor):
1022
+ inputs = (inputs,)
1023
+ if not all(isinstance(x, torch.Tensor) for x in inputs):
1024
+ raise ValueError("inputs must be tensors; wrap scalars with "
1025
+ "torch.tensor first")
1026
+ fixed = tuple(inputs)
1027
+ gen = lambda seed: fixed # noqa: E731
1028
+ trials = 1
1029
  worst = {"max_ulp": -1}
1030
  total_hist = {}
1031
  per_trial = []
1032
  max_abs_seen = 0.0
1033
  max_rel_seen = 0.0
1034
+ out_shapes = []
1035
  for t in range(trials):
1036
  args = gen(seed + t)
1037
+ if not isinstance(args, (tuple, list)):
1038
+ raise ValueError("gen(seed) must return a tuple of args; got "
1039
+ f"{type(args).__name__}; return (x,) for a "
1040
+ "single-argument op")
1041
  r_outs = _as_outputs(ref(*args))
1042
  c_outs = _as_outputs(cand(*args))
1043
+ if len(r_outs) != len(c_outs):
1044
+ raise ValueError(f"reference returned {len(r_outs)} outputs, "
1045
+ f"candidate {len(c_outs)}; make the arities "
1046
+ "match")
1047
+ for oi, (r, c) in enumerate(zip(r_outs, c_outs)):
1048
+ if r.dtype != c.dtype:
1049
+ raise ValueError(f"output {oi} dtype mismatch: ref "
1050
+ f"{r.dtype}, cand {c.dtype}; cast the "
1051
+ "candidate to the reference dtype")
1052
+ if r.shape != c.shape:
1053
+ raise ValueError(f"output {oi} shape mismatch: ref "
1054
+ f"{tuple(r.shape)}, cand "
1055
+ f"{tuple(c.shape)}")
1056
+ out_shapes = [tuple(r.shape) for r in r_outs]
1057
+ # pointwise band: each element judged against its own measured order sensitivity
1058
  bands = None
1059
  band = 0
1060
  band_score = 0.0
 
1078
  max_ulp = 0
1079
  mean_ulp = 0.0
1080
  first_div = -1
1081
+ first_div_nd = None
1082
  n_el = 0
1083
  n_above_band = 0
1084
+ n_diverging = 0
1085
+ n_cand_high = 0
1086
  nan_r = nan_c = inf_r = inf_c = 0
1087
  worst_elements = []
1088
  for oi, (r, c) in enumerate(zip(r_outs, c_outs)):
 
1100
  mean_ulp += float(d.double().sum().item())
1101
  n_el += d.numel()
1102
  nz = (d > 0)
1103
+ n_diverging += int(nz.sum().item())
1104
+ n_cand_high += int(((c > r) & nz).sum().item())
1105
  if first_div < 0 and nz.any():
1106
  first_div = int(nz.flatten().nonzero()[0].item())
1107
+ first_div_nd = _unravel(first_div, r.shape)
1108
  if nz.any():
1109
  buckets = torch.clamp(
1110
  torch.log2(d[nz].double()).floor(), 0, 62
 
1121
  if val > 0:
1122
  worst_elements.append({
1123
  "output": oi, "index": int(ix),
1124
+ "index_nd": _unravel(int(ix), r.shape),
1125
  "ref": float(rf[ix].item()),
1126
  "cand": float(cf[ix].item()),
1127
  "ulp": int(val)})
 
1141
  inf_r += int(torch.isinf(r).sum().item())
1142
  inf_c += int(torch.isinf(c).sum().item())
1143
  worst_elements.sort(key=lambda w: -w["ulp"])
1144
+ frac_pos = n_cand_high / max(n_diverging, 1)
1145
  trial_rep = {
1146
  "max_ulp": max_ulp,
1147
  "mean_ulp": round(mean_ulp / max(n_el, 1), 3),
1148
  "first_divergent_index": first_div,
1149
+ "first_divergent_nd": first_div_nd,
1150
  "nan_ref": nan_r, "nan_cand": nan_c,
1151
  "inf_ref": inf_r, "inf_cand": inf_c,
1152
  "order_band_ulp": band,
1153
  "band_score": round(band_score, 2),
1154
  "frac_above_band": round(n_above_band / max(n_el, 1), 6),
1155
+ "bias_one_sided_frac": round(max(frac_pos, 1 - frac_pos), 4)
1156
+ if n_diverging else 0.0,
1157
  "worst_elements": worst_elements[:worst_k],
1158
  }
1159
  per_trial.append(trial_rep)
1160
  if max_ulp > worst["max_ulp"]:
1161
  worst = trial_rep
 
1162
  max_seen = max(t["max_ulp"] for t in per_trial)
1163
+ banded = order_shuffle is not None or ref_variants
1164
+ if banded:
1165
  score = max(t["band_score"] for t in per_trial)
1166
  ulp_ok = score <= band_margin
1167
  criterion = (f"pointwise max(ulp / (order_band + 2)) = "
 
1176
  max_rel_seen <= (rtol or 0) or max_abs_seen <= (atol or 0)
1177
  criterion += (f"; OR max_abs {max_abs_seen:.3e} <= atol {atol} / "
1178
  f"max_rel {max_rel_seen:.3e} <= rtol {rtol}")
1179
+ # one-sided bias gate: a uniform small error is above-band nearly everywhere in one direction
1180
+ bias_fail = False
1181
+ if banded and (ulp_ok or tol_ok):
1182
+ w_frac = max(t["frac_above_band"] for t in per_trial)
1183
+ w_side = max(t["bias_one_sided_frac"] for t in per_trial)
1184
+ if w_frac > frac_above_tol and w_side >= 0.9:
1185
+ bias_fail = True
1186
+ criterion += ("; FAILED bias gate: frac_above_band %.3f > %s "
1187
+ "with one-sided fraction %.2f" %
1188
+ (w_frac, frac_above_tol, w_side))
1189
+ passed = bool((ulp_ok or tol_ok) and not bias_fail)
1190
+ hints = [] if passed else _compare_hints(worst)
1191
+ if passed:
1192
+ verdict = "pass: " + (
1193
+ f"max {max_seen} ULP within the order band" if banded
1194
+ else f"max {max_seen} ULP")
1195
+ elif bias_fail:
1196
+ verdict = ("fail: uniform bias, %.0f%% of elements above their "
1197
+ "order band in one direction" %
1198
+ (100 * max(t["frac_above_band"] for t in per_trial)))
1199
+ else:
1200
+ w0 = worst["worst_elements"][0] if worst["worst_elements"] else None
1201
+ verdict = "fail: max %d ULP" % max_seen + (
1202
+ " at index %s (ref %.6g, cand %.6g)" %
1203
+ (w0["index_nd"], w0["ref"], w0["cand"]) if w0 else "")
1204
+ rep = {
1205
+ "pass": passed,
1206
+ "verdict": verdict,
1207
  "criterion": criterion,
1208
  "worst_trial": worst,
1209
  "max_abs": max_abs_seen,
 
1211
  "trials": per_trial,
1212
  "ulp_hist_log2": dict(sorted(total_hist.items())),
1213
  }
1214
+ if hints:
1215
+ rep["hints"] = hints
1216
+ return rep
1217
 
1218
 
1219
  _FUZZ_VARIANTS = ("contig", "transposed", "strided_rows", "offset_slice",
 
1229
  return False
1230
 
1231
 
1232
+ class _RandnMaker:
1233
+ """Picklable default maker: `arity` same-shape randn tensors (zeros for empty shapes)."""
1234
+
1235
+ def __init__(self, arity=1):
1236
+ self.arity = arity
1237
+
1238
+ def __call__(self, shape, dtype=torch.float32):
1239
+ n = math.prod(shape)
1240
+ if n:
1241
+ out = tuple(torch.randn(tuple(shape), device="cuda",
1242
+ dtype=dtype)
1243
+ for _ in range(self.arity))
1244
+ else:
1245
+ out = tuple(torch.zeros(tuple(shape), device="cuda",
1246
+ dtype=dtype)
1247
+ for _ in range(self.arity))
1248
+ return out if self.arity > 1 else out[0]
1249
+
1250
+
1251
+ def _default_make(arity=1):
1252
+ """Default maker instance; a class so isolate=True can transport it."""
1253
+ return _RandnMaker(arity)
1254
+
1255
+
1256
  def _fuzz_build(make, shape, dtype):
1257
  try:
1258
  out = make(tuple(shape), dtype)
 
1269
  if v == "strided_rows":
1270
  return x[::2] if x.dim() >= 1 and x.shape[0] >= 2 else x
1271
  if v == "offset_slice":
1272
+ # storage-offset-1 view: misaligned for vectorized loads
1273
  return x.flatten()[1:] if x.numel() > 1 else x
1274
  if v == "overflow_expand":
1275
  if x.numel() == 0:
 
1283
 
1284
  def _fuzz_group(op, ref, make, shape, dtype, variants, ulp_tol, device,
1285
  stop_on_context_loss=True):
1286
+ """All variant cases for one (shape, dtype) group; returns (rows, context_lost)."""
 
1287
  rows = []
1288
  bases = _fuzz_build(make, shape, dtype)
1289
  lost = False
 
1320
  got = op(*xs)
1321
  want = ref(*xs)
1322
  if v == "overflow_expand":
1323
+ # bounded comparison: head plus the high-index tail where int32 indexing wraps
 
1324
  w = 1 << 20
1325
  gf, wf = got.flatten(), want.flatten()
1326
  pairs = ((gf[:w], wf[:w]), (gf[-w:], wf[-w:]))
 
1345
  return rows, lost
1346
 
1347
 
1348
+ def _fn_spec(fn, name):
1349
+ """Picklable transport spec for a callable, with a source fallback for local defs."""
1350
+ import pickle
1351
+ try:
1352
+ return ("pickle", pickle.dumps(fn), None)
1353
+ except Exception: # noqa: BLE001
1354
+ import inspect
1355
+ import textwrap
1356
+ try:
1357
+ src = textwrap.dedent(inspect.getsource(fn))
1358
+ except (OSError, TypeError):
1359
+ src = ""
1360
+ if not src.startswith("def "):
1361
+ raise ValueError(
1362
+ f"{name} is not picklable and not a plain def; define "
1363
+ "op/ref/make as top-level functions (not lambdas or "
1364
+ "closures) to use isolate=True") from None
1365
+ return ("source", src, fn.__name__)
1366
+
1367
+
1368
+ _POOL_BOOTSTRAP = r"""
1369
+ import json, os, pickle, sys
1370
  meta = json.load(open(sys.argv[1], "r"))
1371
+ sys.path[:0] = meta["sys_path"]
1372
  try:
1373
  import kernels
1374
  _mk = kernels.get_kernel(meta["repo"], version=meta["version"],
 
1381
  import load_local
1382
  _mk = load_local.load()
1383
  import torch
1384
+ _ns = {"torch": torch}
1385
+ def _resolve(spec):
1386
+ kind, payload, name = spec
1387
+ if kind == "pickle":
1388
+ return pickle.loads(payload)
1389
+ exec(payload, _ns)
1390
+ return _ns[name]
1391
+ _op, _ref, _make = (_resolve(s) for s in
1392
+ pickle.load(open(meta["fns"], "rb")))
1393
+ print("ready", flush=True)
1394
+ for line in sys.stdin:
1395
+ job = json.loads(line)
1396
+ rows, _l = _mk._fuzz_group(
1397
+ _op, _ref, _make, tuple(job["shape"]),
1398
+ getattr(torch, job["dtype"]), tuple(job["variants"]),
1399
+ job["ulp_tol"], job["device"], stop_on_context_loss=False)
1400
+ pickle.dump(rows, open(job["out"], "wb"))
1401
+ print("done", flush=True)
1402
  """
1403
 
1404
 
1405
+ class _FuzzPool:
1406
+ """One persistent isolation worker fed groups over stdin, respawned only after a crash."""
1407
+
1408
+ def __init__(self, meta_path, stderr_path, timeout_s):
1409
+ self.meta_path = meta_path
1410
+ self.stderr_path = stderr_path
1411
+ self.timeout_s = timeout_s
1412
+ self.proc = None
1413
+ self.q = None
1414
+
1415
+ def _spawn(self):
1416
+ import queue
1417
+ import subprocess
1418
+ import sys as _sys
1419
+ import threading
1420
+ self.errf = open(self.stderr_path, "ab")
1421
+ self.proc = subprocess.Popen(
1422
+ [_sys.executable, "-c", _POOL_BOOTSTRAP, self.meta_path],
1423
+ stdin=subprocess.PIPE, stdout=subprocess.PIPE,
1424
+ stderr=self.errf, text=True)
1425
+ self.q = queue.Queue()
1426
+
1427
+ def reader(p, q):
1428
+ for line in p.stdout:
1429
+ q.put(line.strip())
1430
+ threading.Thread(target=reader, args=(self.proc, self.q),
1431
+ daemon=True).start()
1432
+ try:
1433
+ first = self.q.get(timeout=self.timeout_s)
1434
+ except Exception: # noqa: BLE001
1435
+ first = None
1436
+ return first == "ready"
1437
+
1438
+ def run(self, job_line):
1439
+ """Returns True when the worker reports done, False on crash or timeout."""
1440
+ import queue
1441
+ if self.proc is None or self.proc.poll() is not None:
1442
+ if not self._spawn():
1443
+ return False
1444
+ try:
1445
+ self.proc.stdin.write(job_line + "\n")
1446
+ self.proc.stdin.flush()
1447
+ except OSError:
1448
+ return False
1449
+ try:
1450
+ return self.q.get(timeout=self.timeout_s) == "done"
1451
+ except queue.Empty:
1452
+ if self.proc.poll() is None:
1453
+ self.proc.kill()
1454
+ return False
1455
+
1456
+ def stderr_tail(self):
1457
+ try:
1458
+ self.errf.flush()
1459
+ with open(self.stderr_path, "rb") as f:
1460
+ return f.read()[-400:].decode(errors="replace")
1461
+ except OSError:
1462
+ return ""
1463
+
1464
+ def close(self):
1465
+ if self.proc is not None and self.proc.poll() is None:
1466
+ self.proc.kill()
1467
+ try:
1468
+ self.errf.close()
1469
+ except Exception: # noqa: BLE001
1470
+ pass
1471
+
1472
+
1473
+ def _fuzz_hints(rows):
1474
+ hints = []
1475
+ bad = [c for c in rows if c["status"] == "mismatch"]
1476
+ if bad and all(c["variant"] in ("transposed", "strided_rows",
1477
+ "offset_slice") for c in bad):
1478
+ hints.append("mismatches only on non-contiguous variants: the op "
1479
+ "assumes contiguity; call .contiguous() or honor "
1480
+ "strides")
1481
+ if any(c["variant"] == "overflow_expand" and
1482
+ c["status"] == "mismatch" for c in rows):
1483
+ hints.append("overflow_expand mismatch: int32 indexing wraps past "
1484
+ "2^31 elements; use 64-bit index arithmetic")
1485
+ return hints
1486
+
1487
+
1488
+ def fuzz_shapes(op, ref, make=None, shapes=None, variants=_FUZZ_VARIANTS,
1489
  dtypes=(torch.float32,), ulp_tol=64, isolate=False,
1490
+ isolate_timeout_s=180, device="cuda", arity=1):
1491
+ """Edge shapes, dtypes, and layout mutations with per-case repro recipes; isolate=True survives context-killing candidates via a pooled worker."""
 
 
 
 
 
 
 
 
 
 
 
 
1492
  if shapes is None:
1493
  shapes = [(), (7,), (0, 8), (1, 1), (1, 17), (3, 63), (17, 129),
1494
  (128, 128), (1023, 5), (2, 4096), (2, 3, 5)]
1495
+ if make is None:
1496
+ make = _default_make(arity)
1497
 
1498
  rows = []
1499
  lost = False
1500
  if isolate:
1501
  import json
1502
  import pickle
 
1503
  import sys as _sys
1504
  import tempfile
1505
  tmpdir = tempfile.mkdtemp(prefix="mk_fuzz_")
1506
  fns = os.path.join(tmpdir, "fns.pkl")
1507
+ specs = [_fn_spec(f, n) for f, n in ((op, "op"), (ref, "ref"),
1508
+ (make, "make"))]
1509
  with open(fns, "wb") as f:
1510
+ pickle.dump(specs, f)
1511
+ meta_path = os.path.join(tmpdir, "meta.json")
1512
+ with open(meta_path, "w", encoding="utf-8") as f:
1513
+ json.dump({"sys_path": [p for p in _sys.path
1514
+ if isinstance(p, str)],
1515
+ "repo": _REPO_ID, "version": _REPO_VERSION,
1516
+ "fns": fns}, f)
1517
+ pool = _FuzzPool(meta_path, os.path.join(tmpdir, "stderr.log"),
1518
+ isolate_timeout_s)
1519
+ try:
1520
+ for dtype in dtypes:
1521
+ for shape in shapes:
1522
+ out = os.path.join(tmpdir, "rows.pkl")
1523
+ if os.path.exists(out):
1524
+ os.remove(out)
1525
+ job = json.dumps({
1526
  "shape": list(shape),
1527
  "dtype": str(dtype).replace("torch.", ""),
1528
  "variants": list(variants),
1529
  "ulp_tol": ulp_tol, "device": device,
1530
+ "out": out})
1531
+ ok = pool.run(job)
1532
+ if ok and os.path.exists(out):
 
 
 
 
 
 
1533
  with open(out, "rb") as f:
1534
+ rows.extend(pickle.load(f))
1535
+ else:
1536
+ rows.append({
1537
+ "shape": tuple(shape), "variant": "*",
1538
+ "dtype": str(dtype).replace("torch.", ""),
1539
+ "status": "crashed_isolated",
1540
+ "stderr_tail": pool.stderr_tail()})
1541
+ finally:
1542
+ pool.close()
 
 
 
 
1543
  else:
1544
  pending = [(dt, sh) for dt in dtypes for sh in shapes]
1545
  for gi, (dtype, shape) in enumerate(pending):
 
1556
  n_bad = sum(1 for r in rows
1557
  if r["status"] not in ("ok", "skipped_vram",
1558
  "skipped_empty"))
1559
+ rep = {"cases": rows, "failed": n_bad, "total": len(rows),
1560
+ "context_lost": lost}
1561
+ hints = _fuzz_hints(rows)
1562
+ if hints:
1563
+ rep["hints"] = hints
1564
+ if n_bad == 0:
1565
+ rep["verdict"] = "pass: %d/%d cases ok" % (len(rows) - n_bad,
1566
+ len(rows))
1567
+ else:
1568
+ worst_case = next(c for c in rows
1569
+ if c["status"] not in ("ok", "skipped_vram",
1570
+ "skipped_empty"))
1571
+ rep["verdict"] = "fail: %d/%d cases (%s at %s %s)" % (
1572
+ n_bad, len(rows), worst_case["status"], worst_case["shape"],
1573
+ worst_case["variant"])
1574
+ return rep
1575
 
1576
 
1577
  def sweep(factory, grid, iters=30, warmup=5, bytes=None, flops=None,
1578
  check=None, budget_s=None, race=False):
1579
+ """Launch-configuration search, fastest first, with a correctness gate per row and no silently dropped points."""
 
 
 
 
 
 
 
 
1580
  keys = list(grid.keys())
1581
 
1582
  def expand(i, acc):
 
1641
  continue
1642
  rows.append(run_point(p, iters))
1643
  rows.sort(key=lambda r: (not r.get("check", True), r["median_ms"]))
1644
+ if rows:
1645
+ n_pruned = sum(1 for r in rows if r.get("status") == "pruned_race")
1646
+ n_skip = sum(1 for r in rows if r.get("status") == "not_run")
1647
+ best = rows[0]
1648
+ rows_verdict = "fastest %s at %.4g ms" % (
1649
+ {k: best[k] for k in keys}, best["median_ms"])
1650
+ if n_pruned:
1651
+ rows_verdict += "; %d pruned" % n_pruned
1652
+ if n_skip:
1653
+ rows_verdict += "; %d not run" % n_skip
1654
+ best["verdict"] = rows_verdict
1655
  return rows
1656
 
1657
 
1658
+ def judge(cand, ref, inputs=None, gen=None, bytes=None, flops=None,
1659
+ dtype=None, order_shuffle=None, make=None, quick_fuzz=True,
1660
+ device="cuda"):
1661
+ """One-call verdict: compare, a quick fuzz, and a roofline bench against the cached dossier."""
1662
+ if (gen is None) == (inputs is None):
1663
+ raise ValueError("pass exactly one of inputs (concrete tensors) "
1664
+ "or gen (seed -> args)")
1665
+ if inputs is not None and isinstance(inputs, torch.Tensor):
1666
+ inputs = (inputs,)
1667
+ args0 = tuple(inputs) if inputs is not None else gen(0)
1668
+ arity = len(args0)
1669
+ if dtype is None:
1670
+ dt0 = args0[0].dtype
1671
+ dtype = {torch.float16: "fp16", torch.bfloat16: "bf16"}.get(
1672
+ dt0, "f32")
1673
+ dossier = probe_device(device, quick=True, cached=True,
1674
+ tenancy_check=False)
1675
+ cmp_rep = compare(ref, cand, gen=gen, inputs=inputs, trials=4,
1676
+ order_shuffle=order_shuffle)
1677
+ fuzz_rep = None
1678
+ if quick_fuzz:
1679
+ fuzz_rep = fuzz_shapes(
1680
+ cand, ref, make=make, arity=arity,
1681
+ shapes=[(1, 17), (3, 63), (128, 128)],
1682
+ variants=("contig", "transposed", "offset_slice"),
1683
+ dtypes=(args0[0].dtype,))
1684
+ bench_rep = None
1685
+ if inputs is not None:
1686
+ bench_rep = bench(lambda: cand(*inputs), iters=32, warmup=6,
1687
+ bytes=bytes, flops=flops, dossier=dossier,
1688
+ dtype=dtype, device=device)
1689
+ passed = cmp_rep["pass"] and (fuzz_rep is None or
1690
+ fuzz_rep["failed"] == 0)
1691
+ parts = [cmp_rep["verdict"]]
1692
+ if fuzz_rep is not None:
1693
+ parts.append(fuzz_rep["verdict"])
1694
+ if bench_rep is not None:
1695
+ parts.append(bench_rep["verdict"])
1696
+ rep = {"pass": passed, "verdict" : " | ".join(parts),
1697
+ "compare": cmp_rep, "fuzz": fuzz_rep, "bench": bench_rep,
1698
+ "device": dossier["device"]["name"]}
1699
+ hints = list(cmp_rep.get("hints", []))
1700
+ if fuzz_rep is not None:
1701
+ hints += fuzz_rep.get("hints", [])
1702
+ if hints:
1703
+ rep["hints"] = hints
1704
+ return rep
1705
+
1706
+
1707
  def alloc_stamps(n, blocks=None, device="cuda"):
1708
+ """Device buffer for MK_STAMP (1-D) or MK_STAMP_BLOCK ([blocks, n]) stamps."""
 
 
1709
  if blocks is not None:
1710
  return torch.zeros(blocks, n, dtype=torch.int64, device=device)
1711
  return torch.zeros(n, dtype=torch.int64, device=device)
 
1715
 
1716
 
1717
  def pinned_dossiers():
1718
+ """Names of the release-pinned dossiers, one per certified architecture."""
 
1719
  return sorted(_PINNED.keys())
1720
 
1721
 
 
1734
 
1735
 
1736
  def dossier_drift(current, pinned=None, rel_tol=0.35, skip=_DRIFT_SKIP):
1737
+ """Per-leaf relative drift of a fresh dossier against the release pin for the same architecture."""
 
 
 
 
1738
  if pinned is None:
1739
  arch = current["device"]["sm_arch"]
1740
  matches = [k for k, v in _PINNED.items()
 
1757
  "rel": round(rel, 4)})
1758
  worst = max((e["rel"] for e in entries), default=0.0)
1759
  over = [e for e in entries if e["rel"] > rel_tol]
1760
+ rep = {"entries": entries, "worst_rel": worst, "over_tol": over,
1761
+ "rel_tol": rel_tol, "compared": len(entries),
1762
+ "pass": len(over) == 0}
1763
+ rep["verdict"] = ("pass: worst drift %.3f over %d entries" %
1764
+ (worst, len(entries))) if rep["pass"] else \
1765
+ ("fail: %d entries over %.2f, worst %s at %.3f" %
1766
+ (len(over), rel_tol, over[0]["path"], worst))
1767
+ return rep
1768
 
1769
 
1770
  def stamped_demo(n=1 << 22, fma_iters=4000, blocks=None, threads=256,
1771
  device="cuda"):
1772
+ """Run the shipped MK_STAMP_BLOCK example (load, compute, drained store) and decode its stamps."""
 
 
1773
  props = torch.cuda.get_device_properties(device)
1774
  if blocks is None:
1775
  blocks = props.multi_processor_count
 
1782
 
1783
 
1784
  def read_stamps(stamps, labels=None, clock_ghz=None):
1785
+ """Decode MK_STAMP deltas to ticks and microseconds; 2-D buffers yield min/median/max across blocks."""
 
 
 
 
1786
  if clock_ghz is None:
1787
  clock_ghz = measure_clock(stamps.device)
1788
 
 
1818
  return {"phases": phases, "clock_ghz": round(clock_ghz, 3),
1819
  "blocks_reporting": int(live.sum().item()),
1820
  "blocks_total": int(live.numel())}
1821
+
1822
+
1823
+ _SCHEMAS = {
1824
+ "probe_device": {
1825
+ "device": "name, sm_arch, sms, l2_bytes, vram_bytes, torch, "
1826
+ "cuda_runtime, ecc, driver, power_limit_w",
1827
+ "clock": "max/min/final GHz under sustained FMA load plus sag_pct",
1828
+ "dram_bw": "GB/s: triad_4B/8B/16B, read_only, write_only, "
1829
+ "random_gather_16B",
1830
+ "l2_bw": "GB/s over an L2-resident working set",
1831
+ "latency": "cycles: smem, l1, l2, dram (min of 3 chases) plus "
1832
+ "ns_per_cycle",
1833
+ "fma_f32 / fma_f64": "TFLOP/s of the register-resident pipes",
1834
+ "fma_ilp": "TFLOP/s at 1/2/4 chains plus ratio_4_1; near 4 means "
1835
+ "latency-bound",
1836
+ "mma": "TFLOP/s per dtype; sparse carries executed and "
1837
+ "dense_equivalent; verified_k_lanes carries "
1838
+ "executed_k_per_iter, vs_nominal, calibration_linear; "
1839
+ "fp8_accum carries the accumulate-precision stall",
1840
+ "mma_fed": "TFLOP/s of smem-staged fp16 tiles (staging exposed)",
1841
+ "smem_bw": "GB/s aggregate, conflict-light pattern",
1842
+ "smem_conflict_penalty": "time ratio vs stride 1 per lane stride",
1843
+ "transfer_bw": "GB/s: h2d/d2h pinned and pageable, d2d",
1844
+ "atomics_f32": "Gop/s curve over distinct addresses plus "
1845
+ "shared_same_addr",
1846
+ "launch_overhead": "us: torch_queued, torch_round_trip, "
1847
+ "raw_queued, raw_round_trip",
1848
+ "grid_barrier": "us per grid.sync or 'unsupported'",
1849
+ "occupancy_blocks_per_sm": "API table by (threads, dyn smem)",
1850
+ "occupancy_knees_fma_tflops": "timed dependent-chain table",
1851
+ },
1852
+ "bench": {
1853
+ "median_ms": "median over accepted samples",
1854
+ "sustained_ms": "median over all samples",
1855
+ "median_ci95_ms / p05_ms / p95_ms / min_ms / mean_ms":
1856
+ "distribution of accepted samples",
1857
+ "samples / rejected_throttled / rejection_basis":
1858
+ "counts and whether rejection used sm_clock or "
1859
+ "bandwidth_consistency",
1860
+ "no_clean_window": "present when every window ran throttled; the "
1861
+ "median is then a throttled-regime number",
1862
+ "clock_ghz": "best and worst SM clock probes around the run",
1863
+ "achieved_gbs / achieved_tflops / arithmetic_intensity":
1864
+ "rates from declared traffic",
1865
+ "roofline": "bound, ridge_flops_per_byte, pct_of_roof, peak_dtype",
1866
+ "graph_captured / graph_fallback_reason": "CUDA-graph state",
1867
+ "alloc_growth_bytes_per_call": "present when fn leaks allocations",
1868
+ "host_overhead_suspected": "present when fn syncs or does host "
1869
+ "work inside; ops under ~50 us are "
1870
+ "below the detection floor",
1871
+ "verdict": "one-sentence summary",
1872
+ },
1873
+ "compare": {
1874
+ "pass": "final verdict incl. the one-sided bias gate",
1875
+ "verdict / criterion / hints": "summary, acceptance rule, and "
1876
+ "failure-pattern suggestions",
1877
+ "worst_trial": "max_ulp, mean_ulp, first_divergent_index(+_nd), "
1878
+ "NaN/Inf census, order_band_ulp, band_score, "
1879
+ "frac_above_band, bias_one_sided_frac, "
1880
+ "worst_elements (index, index_nd, ref, cand, ulp)",
1881
+ "max_abs / max_rel / ulp_hist_log2": "aggregate evidence",
1882
+ "floor": "the banded route cannot reject errors under "
1883
+ "band_margin*(band+2) ULP at order-stable elements",
1884
+ },
1885
+ "fuzz_shapes": {
1886
+ "cases": "per-case shape, variant, dtype, status, repro recipe",
1887
+ "status values": "ok | mismatch | exception | context_killed | "
1888
+ "context_lost | crashed_isolated | skipped_vram "
1889
+ "| skipped_empty",
1890
+ "failed / total / context_lost / verdict / hints": "aggregates",
1891
+ },
1892
+ "sweep": {
1893
+ "rows": "grid point plus median_ms, rejected_throttled, check?, "
1894
+ "achieved rates, status in pruned_race | not_run",
1895
+ "verdict": "on the fastest row",
1896
+ },
1897
+ "dossier_drift": {
1898
+ "entries": "path, current, pinned, rel per intersecting leaf",
1899
+ "worst_rel / over_tol / compared / pass / verdict": "aggregates",
1900
+ },
1901
+ "judge": {
1902
+ "pass / verdict / hints": "composite over compare, fuzz, bench",
1903
+ "compare / fuzz / bench": "the full sub-reports",
1904
+ },
1905
+ "read_stamps": {
1906
+ "phases": "ticks and us per phase; 2-D input adds min/median/max "
1907
+ "across blocks",
1908
+ "note": "stamps record issue; DRAIN variants include the store "
1909
+ "drain to the coherence point",
1910
+ },
1911
+ }
1912
+
1913
+
1914
+ def schema(name=None):
1915
+ """Field layout of every report, so an agent can look up what a number means."""
1916
+ if name is None:
1917
+ return _SCHEMAS
1918
+ if name not in _SCHEMAS:
1919
+ raise KeyError(f"no schema for {name!r}; have "
1920
+ f"{sorted(_SCHEMAS)}")
1921
+ return _SCHEMAS[name]
build/torch212-cxx11-cu126-x86_64-linux/{_metakernel_cuda_b62a1ce.abi3.so → _metakernel_cuda_0b9de42.abi3.so} RENAMED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:c32d09147828150170b96b8f23e17ed9b6a3400a9edf3231f8f0a7fa56a60921
3
- size 743832
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b143b103b2bf8dfa9129c62d7ef7b33155749eea06f7f0f3ee009a8e5e7736c5
3
+ size 900560
build/torch212-cxx11-cu126-x86_64-linux/_ops.py CHANGED
@@ -1,9 +1,9 @@
1
  import torch
2
- from . import _metakernel_cuda_b62a1ce
3
- ops = torch.ops._metakernel_cuda_b62a1ce
4
 
5
  def add_op_namespace_prefix(op_name: str):
6
  """
7
  Prefix op by namespace.
8
  """
9
- return f"_metakernel_cuda_b62a1ce::{op_name}"
 
1
  import torch
2
+ from . import _metakernel_cuda_0b9de42
3
+ ops = torch.ops._metakernel_cuda_0b9de42
4
 
5
  def add_op_namespace_prefix(op_name: str):
6
  """
7
  Prefix op by namespace.
8
  """
9
+ return f"_metakernel_cuda_0b9de42::{op_name}"
build/torch212-cxx11-cu126-x86_64-linux/_pinned.py CHANGED
@@ -1,8 +1,4 @@
1
- """Release-pinned device dossiers, one per certified architecture.
2
-
3
- Generated by dev/pin_dossier.py from full probe_device() runs on idle,
4
- single-tenant cards; consumed by metakernel.dossier_drift.
5
- """
6
 
7
  PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
8
  'confidence': 'high',
@@ -295,7 +291,7 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
295
  'd2h_pinned': 10.8,
296
  'h2d_pageable': 10.3,
297
  'h2d_pinned': 11.8}}},
298
- 'sm89_nvidiartx6000adagenerati': {'atomics_f32': {'clock_ghz': 2.706,
299
  'confidence': 'high',
300
  'method': 'atomicAdd '
301
  'hammer; curve '
@@ -308,21 +304,21 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
308
  'contention) to '
309
  'one per thread',
310
  'units': 'Gop/s',
311
- 'value': {'curve_slots': {'1': 0.72,
312
- '145408': 483.54,
313
- '4096': 209.84,
314
- '512': 55.6,
315
- '64': 13.82,
316
- '8': 5.77},
317
- 'shared_same_addr': 12.88}},
318
  'clock': {'confidence': 'high',
319
- 'final_ghz': 2.699,
320
- 'max_ghz': 2.706,
321
  'method': '24 back-to-back FMA '
322
  'spin windows of 30M '
323
  'ticks; wall/ticks '
324
  'per window',
325
- 'min_ghz': 2.501,
326
  'sag_pct': 0.3,
327
  'units': 'GHz'},
328
  'device': {'cuda_runtime': '12.6',
@@ -336,7 +332,7 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
336
  'sms': 142,
337
  'torch': '2.10.0+cu126',
338
  'vram_bytes': 48305274880},
339
- 'dram_bw': {'clock_ghz': 2.706,
340
  'confidence': 'high',
341
  'method': 'streaming triad '
342
  'c=a+s*b per access '
@@ -356,26 +352,41 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
356
  'line-efficiency '
357
  'claim',
358
  'units': 'GB/s',
359
- 'value': {'random_gather_16B': 218.5,
360
- 'read_only': 771.0,
361
- 'triad_16B': 663.1,
362
- 'triad_4B': 638.3,
363
- 'triad_8B': 705.3,
364
  'write_only': 766.5}},
365
- 'fma_f32': {'clock_ghz': 2.706,
366
  'confidence': 'high',
367
  'method': 'register-resident '
368
  'FMA chains',
369
  'units': 'TFLOP/s',
370
- 'value': 90.85},
371
- 'fma_f64': {'clock_ghz': 2.706,
372
  'confidence': 'high',
373
  'method': 'register-resident '
374
  'double FMA chains '
375
  '(the DFMA pipe)',
376
  'units': 'TFLOP/s',
377
- 'value': 1.282},
378
- 'grid_barrier': {'clock_ghz': 2.706,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
379
  'confidence': 'high',
380
  'method': 'cooperative '
381
  'kernel, 142 '
@@ -383,8 +394,8 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
383
  'threads',
384
  'units': 'us per '
385
  'grid.sync',
386
- 'value': 0.54},
387
- 'l2_bw': {'clock_ghz': 2.706,
388
  'confidence': 'medium',
389
  'method': 'in-kernel repeated '
390
  'triad passes over 72 '
@@ -394,14 +405,15 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
394
  'from HBM; amortized '
395
  'over 64 passes',
396
  'units': 'GB/s',
397
- 'value': 9325.3},
398
- 'latency': {'clock_ghz': 2.687,
399
  'confidence': 'high',
400
  'method': 'Sattolo-cycle '
401
- 'pointer chase; 16 '
402
- 'KB ring for L1, 2 '
403
- 'MB TLB-resident '
404
- 'ring for L2, '
 
405
  'self-evicting '
406
  'chain (2x L2 in '
407
  'touched lines) for '
@@ -413,40 +425,58 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
413
  'footprints',
414
  'units': 'cycles (single-lane '
415
  'dependent loads)',
416
- 'value': {'dram_cycles': 649.4,
417
  'l1_cycles': 45.8,
418
- 'l2_cycles': 291.5,
419
  'ns_per_cycle': 0.372,
420
  'smem_cycles': 30.0}},
421
- 'launch_overhead': {'clock_ghz': 2.706,
422
- 'confidence': 'high',
423
  'method': 'empty-kernel '
424
- 'trains, '
425
- 'queued vs '
426
- 'launch+sync',
427
- 'note': 'host-dependent: '
428
- 'includes '
429
- 'torch op '
430
- 'dispatch',
 
 
 
 
 
 
 
 
 
 
 
 
431
  'units': 'us',
432
- 'value': {'queued_us': 5.55,
433
- 'round_trip_us': 14.1}},
434
- 'mma': {'clock_ghz': 2.706,
 
 
435
  'confidence': 'medium',
 
 
 
 
436
  'method': 'register-fragment '
437
  'mma_sync loops (wmma; '
438
  'fp8 and 2:4-sparse '
439
  'fp16 via inline PTX '
440
- 'm16n8k32; sparse '
441
- 'counted '
442
- 'dense-equivalent, the '
443
- 'vendor convention); '
444
- 'flops derive from the '
445
- 'mean accumulator '
446
- 'read-back with every '
447
- 'fragment element '
448
- 'observed, never the '
449
- 'declared loop shape',
450
  'note': 'dense-tile rate without '
451
  'memory traffic; real '
452
  'kernels see this only '
@@ -454,25 +484,54 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
454
  'behind it',
455
  'units': 'TFLOP/s (TOP/s for '
456
  'int8)',
457
- 'value': {'bf16': 365.5,
458
- 'fp16': 375.9,
459
- 'fp16_sparse_2_4': 713.4,
460
- 'fp8_e4m3': 193.7,
461
- 'int8': 770.4,
462
- 'tf32': 177.1},
463
- 'verified_k_lanes': {'bf16': {'executed_k_per_iter': 64.0,
 
 
 
464
  'vs_nominal': 1.0},
465
- 'fp16': {'executed_k_per_iter': 64.0,
 
466
  'vs_nominal': 1.0},
467
- 'fp16_sparse_2_4': {'executed_k_per_iter': 128.0,
 
468
  'vs_nominal': 1.0},
469
- 'fp8_e4m3': {'executed_k_per_iter': 64.0,
470
- 'vs_nominal': 0.25},
471
- 'int8': {'executed_k_per_iter': 64.0,
 
 
 
 
 
472
  'vs_nominal': 1.0},
473
- 'tf32': {'executed_k_per_iter': 32.0,
 
474
  'vs_nominal': 1.0}}},
475
- 'occupancy_blocks_per_sm': {'clock_ghz': 2.706,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
476
  'confidence': 'high',
477
  'method': 'occupancy '
478
  'API '
@@ -501,7 +560,7 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
501
  '16KB': 3,
502
  '32KB': 3,
503
  '48KB': 2}}},
504
- 'occupancy_knees_fma_tflops': {'clock_ghz': 2.706,
505
  'confidence': 'high',
506
  'method': 'a '
507
  'single '
@@ -521,17 +580,6 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
521
  'fixed '
522
  'total '
523
  'work; '
524
- 'throughput '
525
- 'is '
526
- 'proportional '
527
- 'to '
528
- 'resident '
529
- 'threads '
530
- 'until '
531
- 'the '
532
- 'pipe '
533
- 'saturates, '
534
- 'so '
535
  'the '
536
  'cliff '
537
  'marks '
@@ -549,15 +597,15 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
549
  'above',
550
  'units': 'TFLOP/s',
551
  'value': {'t1024': {'0KB': 75.5,
552
- '32KB': 73.66,
553
- '48KB': 74.95},
554
- 't128': {'0KB': 79.86,
555
  '32KB': 60.31,
556
- '48KB': 44.57},
557
- 't256': {'0KB': 81.69,
558
- '32KB': 79.48,
559
- '48KB': 71.47}}},
560
- 'smem_bw': {'clock_ghz': 2.706,
561
  'confidence': 'medium',
562
  'method': 'block-resident '
563
  'read+write '
@@ -566,12 +614,56 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
566
  'aggregated across '
567
  'SMs',
568
  'note': 'bank-conflict-light '
569
- 'access pattern; a '
570
- 'conflicted pattern '
571
- 'can be far lower',
 
572
  'units': 'GB/s',
573
- 'value': 21250.0},
574
- 'transfer_bw': {'clock_ghz': 2.706,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
575
  'confidence': 'high',
576
  'method': '256 MB torch '
577
  'copies, '
@@ -583,8 +675,8 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
583
  'direction of '
584
  'traffic',
585
  'units': 'GB/s',
586
- 'value': {'d2d': 330.7,
587
- 'd2h_pageable': 15.6,
588
- 'd2h_pinned': 26.2,
589
- 'h2d_pageable': 16.6,
590
  'h2d_pinned': 24.0}}}}
 
1
+ '''Release-pinned device dossiers, one per certified architecture; generated by dev/pin_dossier.py from idle single-tenant runs, consumed by dossier_drift.'''
 
 
 
 
2
 
3
  PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
4
  'confidence': 'high',
 
291
  'd2h_pinned': 10.8,
292
  'h2d_pageable': 10.3,
293
  'h2d_pinned': 11.8}}},
294
+ 'sm89_nvidiartx6000adagenerati': {'atomics_f32': {'clock_ghz': 2.705,
295
  'confidence': 'high',
296
  'method': 'atomicAdd '
297
  'hammer; curve '
 
304
  'contention) to '
305
  'one per thread',
306
  'units': 'Gop/s',
307
+ 'value': {'curve_slots': {'1': 0.71,
308
+ '145408': 413.99,
309
+ '4096': 184.9,
310
+ '512': 49.84,
311
+ '64': 13.25,
312
+ '8': 5.74},
313
+ 'shared_same_addr': 12.23}},
314
  'clock': {'confidence': 'high',
315
+ 'final_ghz': 2.698,
316
+ 'max_ghz': 2.705,
317
  'method': '24 back-to-back FMA '
318
  'spin windows of 30M '
319
  'ticks; wall/ticks '
320
  'per window',
321
+ 'min_ghz': 2.5,
322
  'sag_pct': 0.3,
323
  'units': 'GHz'},
324
  'device': {'cuda_runtime': '12.6',
 
332
  'sms': 142,
333
  'torch': '2.10.0+cu126',
334
  'vram_bytes': 48305274880},
335
+ 'dram_bw': {'clock_ghz': 2.705,
336
  'confidence': 'high',
337
  'method': 'streaming triad '
338
  'c=a+s*b per access '
 
352
  'line-efficiency '
353
  'claim',
354
  'units': 'GB/s',
355
+ 'value': {'random_gather_16B': 218.1,
356
+ 'read_only': 766.5,
357
+ 'triad_16B': 682.1,
358
+ 'triad_4B': 654.8,
359
+ 'triad_8B': 670.9,
360
  'write_only': 766.5}},
361
+ 'fma_f32': {'clock_ghz': 2.705,
362
  'confidence': 'high',
363
  'method': 'register-resident '
364
  'FMA chains',
365
  'units': 'TFLOP/s',
366
+ 'value': 86.58},
367
+ 'fma_f64': {'clock_ghz': 2.705,
368
  'confidence': 'high',
369
  'method': 'register-resident '
370
  'double FMA chains '
371
  '(the DFMA pipe)',
372
  'units': 'TFLOP/s',
373
+ 'value': 1.257},
374
+ 'fma_ilp': {'clock_ghz': 2.705,
375
+ 'confidence': 'high',
376
+ 'method': '1/2/4 independent '
377
+ 'dependent FMA '
378
+ 'chains per thread '
379
+ 'at one block of 64 '
380
+ 'threads per SM; a '
381
+ 'ratio near 4 means '
382
+ 'latency-bound at '
383
+ 'that occupancy',
384
+ 'units': 'TFLOP/s',
385
+ 'value': {'ilp1': 11.75,
386
+ 'ilp2': 23.37,
387
+ 'ilp4': 43.03,
388
+ 'ratio_4_1': 3.66}},
389
+ 'grid_barrier': {'clock_ghz': 2.705,
390
  'confidence': 'high',
391
  'method': 'cooperative '
392
  'kernel, 142 '
 
394
  'threads',
395
  'units': 'us per '
396
  'grid.sync',
397
+ 'value': 0.55},
398
+ 'l2_bw': {'clock_ghz': 2.705,
399
  'confidence': 'medium',
400
  'method': 'in-kernel repeated '
401
  'triad passes over 72 '
 
405
  'from HBM; amortized '
406
  'over 64 passes',
407
  'units': 'GB/s',
408
+ 'value': 9590.6},
409
+ 'latency': {'clock_ghz': 2.691,
410
  'confidence': 'high',
411
  'method': 'Sattolo-cycle '
412
+ 'pointer chase, min '
413
+ 'of 3 runs; 16 KB '
414
+ 'ring for L1, 2 MB '
415
+ 'TLB-resident ring '
416
+ 'for L2, '
417
  'self-evicting '
418
  'chain (2x L2 in '
419
  'touched lines) for '
 
425
  'footprints',
426
  'units': 'cycles (single-lane '
427
  'dependent loads)',
428
+ 'value': {'dram_cycles': 656.9,
429
  'l1_cycles': 45.8,
430
+ 'l2_cycles': 292.0,
431
  'ns_per_cycle': 0.372,
432
  'smem_cycles': 30.0}},
433
+ 'launch_overhead': {'clock_ghz': 2.705,
434
+ 'confidence': 'medium',
435
  'method': 'empty-kernel '
436
+ 'trains; '
437
+ 'torch path '
438
+ 'per-launch '
439
+ 'through op '
440
+ 'dispatch, '
441
+ 'raw path '
442
+ 'looped '
443
+ 'inside one '
444
+ 'binding '
445
+ 'call',
446
+ 'note': 'host-state-dependent; '
447
+ 'the '
448
+ 'torch-minus-raw '
449
+ 'gap is the '
450
+ 'dispatch '
451
+ 'cost a '
452
+ 'torch-launched '
453
+ 'candidate '
454
+ 'pays',
455
  'units': 'us',
456
+ 'value': {'raw_queued_us': 6.75,
457
+ 'raw_round_trip_us': 13.87,
458
+ 'torch_queued_us': 7.09,
459
+ 'torch_round_trip_us': 18.53}},
460
+ 'mma': {'clock_ghz': 2.705,
461
  'confidence': 'medium',
462
+ 'fp8_accum': {'fp8_e4m3': {'effective_accum_mantissa_bits': 8,
463
+ 'stall_element_value': 16384.0},
464
+ 'fp8_e5m2': {'effective_accum_mantissa_bits': 8,
465
+ 'stall_element_value': 16384.0}},
466
  'method': 'register-fragment '
467
  'mma_sync loops (wmma; '
468
  'fp8 and 2:4-sparse '
469
  'fp16 via inline PTX '
470
+ 'm16n8k32); flops '
471
+ 'derive from a '
472
+ 'sub-saturation '
473
+ 'accumulator read-back '
474
+ 'with every fragment '
475
+ 'element observed, '
476
+ 'never the declared '
477
+ 'loop shape; sparse '
478
+ 'reports executed and '
479
+ 'dense-equivalent flops',
480
  'note': 'dense-tile rate without '
481
  'memory traffic; real '
482
  'kernels see this only '
 
484
  'behind it',
485
  'units': 'TFLOP/s (TOP/s for '
486
  'int8)',
487
+ 'value': {'bf16': 369.6,
488
+ 'fp16': 365.3,
489
+ 'fp16_sparse_2_4': {'dense_equivalent': 711.1,
490
+ 'executed': 355.6},
491
+ 'fp8_e4m3': 772.3,
492
+ 'fp8_e5m2': 771.2,
493
+ 'int8': 750.0,
494
+ 'tf32': 178.3},
495
+ 'verified_k_lanes': {'bf16': {'calibration_linear': True,
496
+ 'executed_k_per_iter': 64.0,
497
  'vs_nominal': 1.0},
498
+ 'fp16': {'calibration_linear': True,
499
+ 'executed_k_per_iter': 64.0,
500
  'vs_nominal': 1.0},
501
+ 'fp16_sparse_2_4': {'calibration_linear': True,
502
+ 'executed_k_per_iter': 128.0,
503
  'vs_nominal': 1.0},
504
+ 'fp8_e4m3': {'calibration_linear': True,
505
+ 'executed_k_per_iter': 256.0,
506
+ 'vs_nominal': 1.0},
507
+ 'fp8_e5m2': {'calibration_linear': True,
508
+ 'executed_k_per_iter': 256.0,
509
+ 'vs_nominal': 1.0},
510
+ 'int8': {'calibration_linear': True,
511
+ 'executed_k_per_iter': 64.0,
512
  'vs_nominal': 1.0},
513
+ 'tf32': {'calibration_linear': True,
514
+ 'executed_k_per_iter': 32.0,
515
  'vs_nominal': 1.0}}},
516
+ 'mma_fed': {'clock_ghz': 2.705,
517
+ 'confidence': 'high',
518
+ 'method': 'm16n16k16 fp16 '
519
+ 'tiles fed from an '
520
+ 'L2-resident buffer '
521
+ 'through cp.async '
522
+ 'double-buffered '
523
+ 'shared memory; '
524
+ 'accumulator-verified',
525
+ 'note': 'the achieved '
526
+ 'counterpart to the '
527
+ 'register peak: '
528
+ 'staging exposed, one '
529
+ 'dependent '
530
+ 'accumulator chain '
531
+ 'per warp',
532
+ 'units': 'TFLOP/s',
533
+ 'value': 85.2},
534
+ 'occupancy_blocks_per_sm': {'clock_ghz': 2.705,
535
  'confidence': 'high',
536
  'method': 'occupancy '
537
  'API '
 
560
  '16KB': 3,
561
  '32KB': 3,
562
  '48KB': 2}}},
563
+ 'occupancy_knees_fma_tflops': {'clock_ghz': 2.705,
564
  'confidence': 'high',
565
  'method': 'a '
566
  'single '
 
580
  'fixed '
581
  'total '
582
  'work; '
 
 
 
 
 
 
 
 
 
 
 
583
  'the '
584
  'cliff '
585
  'marks '
 
597
  'above',
598
  'units': 'TFLOP/s',
599
  'value': {'t1024': {'0KB': 75.5,
600
+ '32KB': 73.71,
601
+ '48KB': 72.69},
602
+ 't128': {'0KB': 79.72,
603
  '32KB': 60.31,
604
+ '48KB': 45.81},
605
+ 't256': {'0KB': 78.79,
606
+ '32KB': 78.85,
607
+ '48KB': 72.97}}},
608
+ 'smem_bw': {'clock_ghz': 2.705,
609
  'confidence': 'medium',
610
  'method': 'block-resident '
611
  'read+write '
 
614
  'aggregated across '
615
  'SMs',
616
  'note': 'bank-conflict-light '
617
+ 'access pattern; the '
618
+ 'stride sweep below '
619
+ 'prices the '
620
+ 'conflicted case',
621
  'units': 'GB/s',
622
+ 'value': 22244.0},
623
+ 'smem_conflict_penalty': {'clock_ghz': 2.705,
624
+ 'confidence': 'high',
625
+ 'method': 'dependent '
626
+ 'read-FMA-write '
627
+ 'per '
628
+ 'thread '
629
+ 'at '
630
+ 'warp-lane '
631
+ 'bank '
632
+ 'distance '
633
+ '`stride`; '
634
+ 'stride '
635
+ '32 '
636
+ 'puts '
637
+ 'every '
638
+ 'lane '
639
+ 'in '
640
+ 'one '
641
+ 'bank',
642
+ 'note': 'the '
643
+ 'direct '
644
+ 'conflict '
645
+ 'measurement '
646
+ 'that '
647
+ 'disambiguates '
648
+ 'a '
649
+ 'bank-conflict '
650
+ 'signature '
651
+ 'from '
652
+ 'an ILP '
653
+ 'stall',
654
+ 'units': 'x '
655
+ '(time '
656
+ 'ratio '
657
+ 'vs '
658
+ 'stride '
659
+ '1)',
660
+ 'value': {'stride_1': 1.0,
661
+ 'stride_16': 7.13,
662
+ 'stride_2': 1.07,
663
+ 'stride_32': 14.08,
664
+ 'stride_4': 2.04,
665
+ 'stride_8': 3.91}},
666
+ 'transfer_bw': {'clock_ghz': 2.705,
667
  'confidence': 'high',
668
  'method': '256 MB torch '
669
  'copies, '
 
675
  'direction of '
676
  'traffic',
677
  'units': 'GB/s',
678
+ 'value': {'d2d': 324.0,
679
+ 'd2h_pageable': 7.8,
680
+ 'd2h_pinned': 26.3,
681
+ 'h2d_pageable': 7.6,
682
  'h2d_pinned': 24.0}}}}
build/torch212-cxx11-cu126-x86_64-linux/metadata.json CHANGED
@@ -1,9 +1,10 @@
1
  {
2
  "name": "metakernel",
3
- "id": "_metakernel_cuda_b62a1ce",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "python-depends": [],
 
7
  "backend": {
8
  "type": "cuda",
9
  "archs": [
@@ -16,21 +17,20 @@
16
  "digest": {
17
  "algorithm": "sha256",
18
  "files": {
19
- "__init__.py": "e/P3LIoxZqzuhgyc26YJRkdLj4OilextyRiU8YbAvIA=",
20
- "_metakernel_cuda_b62a1ce.abi3.so": "wy0JFHgoFQFwuWuPI+F+2bajQAqe3zIx+PCn+lamCSE=",
21
- "_ops.py": "QokZXDYGh+rLEKO/wNDRcMkJPtPWK568RiTlCHopsHM=",
22
- "_pinned.py": "4i1FvV3kWanEdFLd9+rGOjHcHDAyOjKnjLt5LUzgASs=",
23
- "metakernel/__init__.py": "DFYPlrhXwYjEqCl/8n0SmWGZV8NFml5DPhMjKfv98GY="
24
  }
25
  },
26
  "provenance": {
27
  "kernel-builder": {
28
  "version": "0.17.0-dev0",
29
- "sha": "19aaa6421e674e9fecc352bbae6eab81d19a6bf4",
30
  "dirty": false
31
  },
32
  "kernel": {
33
- "sha": "b62a1cef64b6e3b2cabf774c82ea7ce61dcac6c0",
34
  "dirty": false
35
  }
36
  }
 
1
  {
2
  "name": "metakernel",
3
+ "id": "_metakernel_cuda_0b9de42",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "python-depends": [],
7
+ "kernel-depends": [],
8
  "backend": {
9
  "type": "cuda",
10
  "archs": [
 
17
  "digest": {
18
  "algorithm": "sha256",
19
  "files": {
20
+ "__init__.py": "lDQ+fJ0JnI3sQGR3UMnDovBbG4ciYBsaFjmgYdKDTKI=",
21
+ "_metakernel_cuda_0b9de42.abi3.so": "sUOxA7K/jfqRKcYtfvezMVV0nuoG9/Dz7gCajl53NsU=",
22
+ "_ops.py": "x88Bkm1nxRli0nIu2u4ztaJopS1IAZWdN9LZPfspJdo=",
23
+ "_pinned.py": "UyzMniAjfVMIrIbbrSq92KKo5dX5l/GwV3pMVA+eNtM="
 
24
  }
25
  },
26
  "provenance": {
27
  "kernel-builder": {
28
  "version": "0.17.0-dev0",
29
+ "sha": "2c40e10d3da1392245ee81bd05b55fb3103b2b34",
30
  "dirty": false
31
  },
32
  "kernel": {
33
+ "sha": "0b9de42fee8a1e52593f2f1f58460c0acc93d7e8",
34
  "dirty": false
35
  }
36
  }
build/torch212-cxx11-cu126-x86_64-linux/metakernel/__init__.py DELETED
@@ -1,26 +0,0 @@
1
- import ctypes
2
- import importlib.util
3
- import sys
4
- from pathlib import Path
5
- from types import ModuleType
6
-
7
-
8
- def _import_from_path(file_path: Path) -> ModuleType:
9
- # We cannot use the module name as-is, after adding it to `sys.modules`,
10
- # it would also be used for other imports. So, we make a module name that
11
- # depends on the path for it to be unique using the hex-encoded hash of
12
- # the path.
13
- path_hash = "{:x}".format(ctypes.c_size_t(hash(file_path.absolute())).value)
14
- module_name = path_hash
15
- spec = importlib.util.spec_from_file_location(module_name, file_path)
16
- if spec is None:
17
- raise ImportError(f"Cannot load spec for {module_name} from {file_path}")
18
- module = importlib.util.module_from_spec(spec)
19
- if module is None:
20
- raise ImportError(f"Cannot load module {module_name} from spec")
21
- sys.modules[module_name] = module
22
- spec.loader.exec_module(module) # type: ignore
23
- return module
24
-
25
-
26
- globals().update(vars(_import_from_path(Path(__file__).parent.parent / "__init__.py")))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
build/torch212-cxx11-cu130-x86_64-linux/__init__.py CHANGED
@@ -1,20 +1,4 @@
1
- """Measuring instrument for the kernel-design loop: timing, adversarial
2
- correctness comparison, and measured device facts, as loadable ops.
3
-
4
- from kernels import get_kernel
5
- mk = get_kernel("phanerozoic/metakernel", version=1,
6
- trust_remote_code=True)
7
-
8
- dossier = mk.probe_device() # measured facts
9
- report = mk.bench(lambda: op(x), iters=200) # throttle-rejected
10
- verdict = mk.compare(ref, cand, gen) # ULP evidence
11
- table = mk.sweep(factory, {"BLOCK": [64, 128]}) # config search
12
-
13
- Every op returns plain dicts and tensors. Every dossier entry is
14
- measured by a dedicated probe kernel, carries its SM clock, and labels
15
- throttled samples; no hardware counters, so entries carry a confidence
16
- field.
17
- """
18
  import math
19
  import os
20
  import time
@@ -24,9 +8,7 @@ import torch
24
  try:
25
  from ._ops import ops
26
  except ImportError: # pragma: no cover - spawned child in a source tree
27
- # Subprocess-isolation children re-import this package; in a source
28
- # checkout there is no generated _ops module, so rebuild the op table
29
- # from the (cached) JIT extension that load_local advertised.
30
  _root = os.environ.get("METAKERNEL_DEV_ROOT")
31
  if not _root:
32
  raise
@@ -48,24 +30,25 @@ try:
48
  except ImportError: # pragma: no cover - fresh tree before first pin
49
  _PINNED = {}
50
 
51
- __all__ = ["probe_device", "bench", "compare", "order_shuffles",
52
- "fuzz_shapes", "sweep", "alloc_stamps", "read_stamps",
53
- "stamped_demo", "measure_clock", "ulp_diff", "dossier_drift",
54
- "pinned_dossiers", "ops"]
55
-
56
- # name -> (kind, M*N of the C tile, nominal K, work multiplier, nominal
57
- # K-lanes per unrolled iteration). The accumulator read-back yields
58
- # executed K-lanes per op (column sums of B equal K), so flops =
59
- # 2 x M*N x gain x multiplier with nothing declared trusted; multiplier
60
- # 2.0 is the sparse dense-equivalent convention. Lanes/iter: wmma K x 4
61
- # chained ops; PTX kinds x 8 over two chains.
62
  _MMA_KINDS = {
63
- "fp16": (0, 16 * 16, 16, 1.0, 64.0),
64
- "bf16": (1, 16 * 16, 16, 1.0, 64.0),
65
- "tf32": (2, 16 * 16, 8, 1.0, 32.0),
66
- "int8": (3, 16 * 16, 16, 1.0, 64.0),
67
- "fp8_e4m3": (4, 16 * 8, 32, 1.0, 256.0),
68
- "fp16_sparse_2_4": (5, 16 * 8, 32, 2.0, 128.0), # dense-equivalent
 
69
  }
70
 
71
 
@@ -74,7 +57,7 @@ def _sync():
74
 
75
 
76
  def _events(fn, reps):
77
- """Time reps calls of fn with one event pair each; returns ms list."""
78
  starts = [torch.cuda.Event(enable_timing=True) for _ in range(reps)]
79
  stops = [torch.cuda.Event(enable_timing=True) for _ in range(reps)]
80
  for s, e in zip(starts, stops):
@@ -86,8 +69,7 @@ def _events(fn, reps):
86
 
87
 
88
  def measure_clock(device="cuda", ticks=4_000_000, blocks=None):
89
- """Effective SM clock in GHz under an FMA spin of `ticks` clock64 ticks.
90
- Wall time over elapsed ticks is the frequency the ticks ran at."""
91
  props = torch.cuda.get_device_properties(device)
92
  if blocks is None:
93
  blocks = props.multi_processor_count
@@ -120,8 +102,7 @@ def _entry(value, units, method, clock_ghz, confidence, note=""):
120
 
121
 
122
  def _sattolo_ring(n, device, generator=None):
123
- """A random single-cycle permutation as an int32 next-pointer table, so
124
- a pointer chase visits every slot before repeating."""
125
  perm = torch.randperm(n, generator=generator)
126
  ring = torch.empty(n, dtype=torch.int32)
127
  ring[perm] = torch.roll(perm, -1).to(torch.int32)
@@ -129,8 +110,7 @@ def _sattolo_ring(n, device, generator=None):
129
 
130
 
131
  def _smi_settings(index):
132
- """ECC mode, driver, and power limit via nvidia-smi; None fields
133
- when the tool is unavailable."""
134
  import subprocess
135
  try:
136
  out = subprocess.run(
@@ -150,10 +130,19 @@ def _smi_settings(index):
150
  return {"ecc": None, "driver": None, "power_limit_w": None}
151
 
152
 
 
 
 
 
 
 
 
 
 
 
 
153
  def mma_tile_check(device="cuda", trials=8):
154
- """Random small-integer m16n16k16 tiles through the wmma fragment
155
- path, compared exactly to the reference GEMM; pins M, N, K, and
156
- operand mapping (integer operands make fp32 accumulation exact)."""
157
  worst = 0.0
158
  for t in range(trials):
159
  g = torch.Generator(device=device).manual_seed(1000 + t)
@@ -171,10 +160,83 @@ def mma_tile_check(device="cuda", trials=8):
171
  "trials": trials}
172
 
173
 
174
- def probe_device(device="cuda", quick=False):
175
- """Empirical device dossier. Each entry is measured by a dedicated
176
- probe kernel and carries units, method, the SM clock it ran under, and
177
- a confidence field."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
  props = torch.cuda.get_device_properties(device)
179
  sm = props.multi_processor_count
180
  l2 = props.L2_cache_size
@@ -187,8 +249,7 @@ def probe_device(device="cuda", quick=False):
187
  "vram_bytes": props.total_memory,
188
  "torch": torch.__version__,
189
  "cuda_runtime": torch.version.cuda,
190
- # instrument settings: readings depend on them, so the
191
- # dossier records them (None when nvidia-smi is unavailable)
192
  **_smi_settings(torch.device(device).index or 0),
193
  }
194
  }
@@ -213,9 +274,8 @@ def probe_device(device="cuda", quick=False):
213
  }
214
  clk = max(traj)
215
 
216
- # ---- device-memory bandwidth: triad per access width, plus read-only,
217
- # write-only, and random-gather
218
- n4 = 16 << 20 # 16M float4 = 256 MB per buffer
219
  if props.total_memory < 4 << 30:
220
  n4 = 4 << 20
221
  a = torch.rand(n4 * 4, device=device)
@@ -239,7 +299,6 @@ def probe_device(device="cuda", quick=False):
239
  dtype=torch.int32).to(device)
240
  ops.mk_gather(a, idx, c, 256, 0)
241
  ms = _median(_events(lambda: ops.mk_gather(a, idx, c, 256, 0), reps))
242
- # traffic accounting: idx reads + 16B random loads + streamed stores
243
  hbm["random_gather_16B"] = round(
244
  (idx.numel() * 4 + 2 * n4 * 16) / (ms * 1e-3) / 1e9, 1)
245
  d["dram_bw"] = _entry(hbm, "GB/s", "streaming triad c=a+s*b per access "
@@ -250,9 +309,9 @@ def probe_device(device="cuda", quick=False):
250
  "so its nominal number is the achievable rate for "
251
  "random access, not a line-efficiency claim")
252
 
253
- # ---- L2 bandwidth (working set 3 buffers inside L2)
254
  ws = max(l2 // 4, 1 << 20)
255
- nl2 = ws // 16 # float4 elems per buffer
256
  al = a[:nl2 * 4].contiguous()
257
  bl = b[:nl2 * 4].contiguous()
258
  cl = c[:nl2 * 4].contiguous()
@@ -267,62 +326,61 @@ def probe_device(device="cuda", quick=False):
267
  "medium", "first pass streams from HBM; amortized "
268
  f"over {passes} passes")
269
 
270
- # ---- latencies: pointer chase per level. The 2 MB L2 ring exceeds
271
- # L1, fits L2 everywhere, and stays TLB-resident after a warm walk;
272
- # the DRAM chain touches 2x L2 in lines, so it evicts itself.
273
  lat = {}
274
  idle_clk = measure_clock(device, ticks=2_000_000, blocks=1)
275
- ring_s = _sattolo_ring(8192, device)
276
- ops.mk_chase_shared(ring_s, 1 << 14, out64)
277
- _sync()
278
- hops = 1 << 16
279
- ops.mk_chase_shared(ring_s, hops, out64)
280
- _sync()
281
- lat["smem_cycles"] = round(int(out64[0].item()) / hops, 1)
282
 
283
- l1_ring_n = (16 << 10) // 4 # 16 KB: L1-resident after a warm walk
284
- ring_l1 = _sattolo_ring(l1_ring_n, device)
285
- ops.mk_chase_global(ring_l1, l1_ring_n, out64)
286
- _sync()
287
- hops_l1 = 1 << 15
288
- ops.mk_chase_global(ring_l1, hops_l1, out64)
289
- _sync()
290
- lat["l1_cycles"] = round(int(out64[0].item()) / hops_l1, 1)
291
-
292
- l2_ring_n = (2 << 20) // 4
293
- ring_l2 = _sattolo_ring(l2_ring_n, device)
294
- ops.mk_chase_global(ring_l2, l2_ring_n, out64) # full-cycle warm walk
295
- _sync()
296
- hops_l2 = 1 << 14
297
- ops.mk_chase_global(ring_l2, hops_l2, out64)
298
- _sync()
299
- lat["l2_cycles"] = round(int(out64[0].item()) / hops_l2, 1)
300
 
 
 
 
 
 
 
 
 
 
 
 
 
301
  hbm_bytes = min(max(4 * l2, 64 << 20), props.total_memory // 8)
302
  ring_h = _sattolo_ring(int(hbm_bytes) // 4, device)
303
  hops_h = max((2 * l2) // 128, 1 << 17)
304
- ops.mk_chase_global(ring_h, 1 << 14, out64) # fault pages / warm TLB
305
- _sync()
306
- ops.mk_chase_global(ring_h, hops_h, out64)
307
- _sync()
308
- lat["dram_cycles"] = round(int(out64[0].item()) / hops_h, 1)
309
  lat["ns_per_cycle"] = round(1.0 / idle_clk, 3)
310
  d["latency"] = _entry(lat, "cycles (single-lane dependent loads)",
311
- "Sattolo-cycle pointer chase; 16 KB ring for L1, "
312
- "2 MB TLB-resident ring for L2, self-evicting "
313
- "chain (2x L2 in touched lines) for HBM",
314
- idle_clk, "high",
315
  "smem includes index arithmetic; HBM includes "
316
  "residual TLB effects at large footprints")
317
  del ring_h, ring_l2, ring_l1, ring_s
318
 
319
- # ---- fp32 FMA and tensor-core throughput
320
  blocks = sm * 4
321
  iters = 20_000 if quick else 60_000
322
  ops.mk_fma_f32(blocks, 256, iters, sink)
323
  ms = _median(_events(lambda: ops.mk_fma_f32(blocks, 256, iters, sink),
324
  reps))
325
- flops = blocks * 256 * iters * 16 * 2 * 2 # 2 chains x 2 flops x unroll
326
  d["fma_f32"] = _entry(round(flops / (ms * 1e-3) / 1e12, 2), "TFLOP/s",
327
  "register-resident FMA chains", clk, "high")
328
  sink64 = torch.zeros(2, dtype=torch.float64, device=device)
@@ -334,43 +392,88 @@ def probe_device(device="cuda", quick=False):
334
  d["fma_f64"] = _entry(round(flops64 / (ms * 1e-3) / 1e12, 3), "TFLOP/s",
335
  "register-resident double FMA chains (the DFMA "
336
  "pipe)", clk, "high")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
337
  mma = {}
338
  mma_klanes = {}
339
- for name, (kind, mn, k_nom, mult, nom_lanes) in _MMA_KINDS.items():
 
340
  it = 30_000 if quick else 100_000
341
  r = ops.mk_mma(kind, blocks, 8, 512, sink)
342
  if r != 0:
343
  mma[name] = "unsupported"
344
  continue
345
  _sync()
346
- # verification pass: with unit operands the accumulator gain IS
347
- # the executed K-lane count (exact at these magnitudes)
348
- cal_iters = 512
349
- sink[1] = 0.0
350
- ops.mk_mma(kind, 1, 1, cal_iters, sink)
351
- _sync()
352
- gain_per_iter = float(sink[1].item()) / cal_iters
353
  mma_klanes[name] = {
354
- "executed_k_per_iter": round(gain_per_iter, 2),
355
- "vs_nominal": round(gain_per_iter / nom_lanes, 3),
 
356
  }
 
 
357
  ms = _median(_events(
358
  lambda k=kind, i=it: ops.mk_mma(k, blocks, 8, i, sink), reps))
359
- flops = blocks * 8 * it * 2.0 * mn * gain_per_iter * mult
360
- mma[name] = round(flops / (ms * 1e-3) / 1e12, 1)
 
 
 
 
361
  d["mma"] = _entry(mma, "TFLOP/s (TOP/s for int8)",
362
  "register-fragment mma_sync loops (wmma; fp8 and "
363
- "2:4-sparse fp16 via inline PTX m16n8k32; sparse "
364
- "counted dense-equivalent, the vendor convention); "
365
- "flops derive from the mean accumulator read-back "
366
  "with every fragment element observed, never the "
367
- "declared loop shape",
 
368
  clk, "medium",
369
  "dense-tile rate without memory traffic; real "
370
  "kernels see this only when staging hides behind it")
371
  d["mma"]["verified_k_lanes"] = mma_klanes
 
 
372
 
373
- # ---- shared-memory bandwidth
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
374
  it_sm = 30_000 if quick else 100_000
375
  ops.mk_smem_bw(blocks, 256, it_sm, sink)
376
  ms = _median(_events(lambda: ops.mk_smem_bw(blocks, 256, it_sm, sink),
@@ -380,8 +483,23 @@ def probe_device(device="cuda", quick=False):
380
  "block-resident read+write streaming (8 B per "
381
  "thread-iteration), aggregated across SMs", clk,
382
  "medium",
383
- "bank-conflict-light access pattern; a conflicted "
384
- "pattern can be far lower")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
385
 
386
  # ---- PCIe / host transfer bandwidth
387
  nbytes = 256 << 20
@@ -412,9 +530,7 @@ def probe_device(device="cuda", quick=False):
412
  "d2d counts one direction of traffic")
413
  del host_pin, host_pag, dev_t, dev_u
414
 
415
- # ---- atomic contention curve: throughput vs number of distinct
416
- # addresses the whole grid hammers (slots = 1 is full contention;
417
- # slots = threads is contention-free), plus the shared-memory point
418
  n_thr = blocks * 256
419
  buf = torch.zeros(n_thr, dtype=torch.float32, device=device)
420
  at = {"curve_slots": {}}
@@ -424,7 +540,7 @@ def probe_device(device="cuda", quick=False):
424
  buf.zero_()
425
  ops.mk_atomics(1, blocks, 256, it_at, buf, slots)
426
  ms = _median(_events(
427
- lambda s=slots: ops.mk_atomics(1, blocks, 256, it_at, buf, s),
428
  reps))
429
  at["curve_slots"][str(slots)] = round(
430
  n_thr * it_at / (ms * 1e-3) / 1e9, 2)
@@ -437,7 +553,7 @@ def probe_device(device="cuda", quick=False):
437
  "of distinct global addresses from 1 (full "
438
  "contention) to one per thread", clk, "high")
439
 
440
- # ---- launch overhead
441
  anchor = sink
442
  n_launch = 500 if quick else 2000
443
  _sync()
@@ -452,10 +568,19 @@ def probe_device(device="cuda", quick=False):
452
  ops.mk_empty(anchor)
453
  _sync()
454
  rt = (time.perf_counter() - t0) / n_rt * 1e6
 
 
455
  d["launch_overhead"] = _entry(
456
- {"queued_us": round(queued, 2), "round_trip_us": round(rt, 2)},
457
- "us", "empty-kernel trains, queued vs launch+sync", clk, "high",
458
- "host-dependent: includes torch op dispatch")
 
 
 
 
 
 
 
459
 
460
  # ---- grid barrier
461
  bar_iters = 500 if quick else 2000
@@ -471,9 +596,7 @@ def probe_device(device="cuda", quick=False):
471
  "unavailable or grid too large", clk,
472
  "high")
473
 
474
- # ---- occupancy table (API) plus timed knees: the same random gather
475
- # at every (threads, dyn_smem) point; when the forced blocks/SM stop
476
- # covering memory latency, achieved bandwidth falls off the cliff
477
  occ = {}
478
  for threads in (128, 256, 512, 1024):
479
  row = {}
@@ -494,8 +617,8 @@ def probe_device(device="cuda", quick=False):
494
  ops.mk_fma_dep(knee_blocks, threads, smem_kb * 1024,
495
  knee_iters, sink)
496
  ms = _median(_events(
497
- lambda t=threads, s=smem_kb: ops.mk_fma_dep(
498
- knee_blocks, t, s * 1024, knee_iters, sink), reps))
499
  fl = knee_blocks * threads * knee_iters * 16 * 2
500
  row[f"{smem_kb}KB"] = round(fl / (ms * 1e-3) / 1e12, 2)
501
  knees[f"t{threads}"] = row
@@ -504,27 +627,160 @@ def probe_device(device="cuda", quick=False):
504
  "at forced (threads, dyn smem) occupancies over fixed total work; "
505
  "the cliff marks where occupancy stops covering latency",
506
  clk, "high", "timing-derived counterpart to the API table above")
 
 
 
507
  return d
508
 
509
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
510
  def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
511
  dtype="fp16", clock_guard=0.97, clock_stride=8, graph=False,
512
- device="cuda"):
513
- """Median-of-N cudaEvent timing with throttle rejection.
514
-
515
- The SM clock is probed every `clock_stride` samples; samples in a
516
- window below `clock_guard` x the best observed clock are rejected
517
- and counted. Declared `bytes`/`flops` yield achieved GB/s / TFLOP/s;
518
- a `dossier` adds roofline placement. `graph=True` times CUDA-graph
519
- replays, stripping Python and torch dispatch (fn must be graph-safe:
520
- no sync, no allocation growth, static shapes; a failed capture falls
521
- back to eager with the reason reported). `clock_stride=1` probes
522
- around every sample, ~0.3 ms each."""
523
  for _ in range(warmup):
524
  fn()
525
  _sync()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
526
  graph_state = "off"
527
  graph_reason = ""
 
 
 
 
 
 
528
  if graph:
529
  pre_rng = torch.cuda.get_rng_state(device)
530
  try:
@@ -539,10 +795,7 @@ def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
539
  except Exception as e: # noqa: BLE001 - fall back, say why
540
  graph_state = "fallback_eager"
541
  graph_reason = f"{type(e).__name__}: {str(e)[:120]}"
542
- # drain the invalidated stream so eager timing can proceed.
543
- # On some torch versions a failed capture also wedges the
544
- # device RNG with no in-process recovery; timing uses no RNG,
545
- # but the caller's later randn may raise.
546
  rng_ok = True
547
  for _attempt in range(5):
548
  try:
@@ -572,25 +825,51 @@ def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
572
  samples.append((_events(fn, n), len(clocks) - 1))
573
  clocks.append(measure_clock(device, ticks=probe_ticks))
574
  best_clk = max(clocks)
575
- kept, rejected = [], 0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
576
  flat = []
577
- for group, ci in samples:
578
- window_ok = (clocks[ci] >= clock_guard * best_clk and
579
- clocks[min(ci + 1, len(clocks) - 1)] >=
580
- clock_guard * best_clk)
581
- for ms in group:
582
- flat.append(ms)
583
- if window_ok:
 
 
 
 
 
 
 
 
 
 
 
 
584
  kept.append(ms)
585
  else:
586
  rejected += 1
587
- if not kept:
 
588
  kept = flat
589
  med = _median(kept)
590
  ks = sorted(kept)
591
  n_k = len(ks)
592
- # order-statistic 95% CI on the median (normal approximation to the
593
- # binomial ranks); degenerate at tiny n, where it spans the sample
594
  half = 1.959964 * (n_k ** 0.5) / 2.0
595
  lo_i = max(0, int(n_k / 2 - half) - 1)
596
  hi_i = min(n_k - 1, int(n_k / 2 + half))
@@ -605,12 +884,25 @@ def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
605
  "mean_ms": round(sum(kept) / n_k, 4),
606
  "samples": len(flat),
607
  "rejected_throttled": rejected,
 
608
  "graph_captured": graph_state == "captured",
609
  "clock_ghz": {"best": round(best_clk, 3),
610
  "worst": round(min(clocks), 3)},
611
  }
 
 
 
 
 
 
 
612
  if graph_reason:
613
  rep["graph_fallback_reason"] = graph_reason
 
 
 
 
 
614
  if bytes is not None:
615
  rep["achieved_gbs"] = round(bytes / (med * 1e-3) / 1e9, 1)
616
  if flops is not None:
@@ -620,6 +912,8 @@ def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
620
  if dossier is not None and bytes and flops:
621
  bw = max(dossier["dram_bw"]["value"].values()) * 1e9
622
  mma_v = dossier["mma"]["value"].get(dtype)
 
 
623
  peak = (mma_v if isinstance(mma_v, (int, float))
624
  else dossier["fma_f32"]["value"]) * 1e12
625
  ai = flops / bytes
@@ -630,13 +924,12 @@ def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
630
  "pct_of_roof": round(100 * (flops / (med * 1e-3)) / roof, 1),
631
  "peak_dtype": dtype,
632
  }
 
633
  return rep
634
 
635
 
636
  def _to_ordered_int(t):
637
- """Map float bit patterns to integers whose difference is the ULP
638
- distance: positive floats keep their bits, negative floats mirror
639
- below zero, so the mapping is monotone in the float value."""
640
  if t.dtype == torch.float32:
641
  u = t.view(torch.int32).to(torch.int64) & 0xFFFFFFFF
642
  half = 1 << 31
@@ -652,11 +945,12 @@ def _to_ordered_int(t):
652
 
653
 
654
  def ulp_diff(a, b):
655
- """Elementwise ULP distance between same-dtype tensors. For floats,
656
- NaNs in the same position count as 0 and a one-sided NaN counts as
657
- 2**62; for integer and bool tensors the distance is the absolute
658
- numeric difference (an exact op must be exactly equal)."""
659
- assert a.dtype == b.dtype and a.shape == b.shape
 
660
  if not a.is_floating_point():
661
  return (a.to(torch.int64) - b.to(torch.int64)).abs()
662
  ia, ib = _to_ordered_int(a), _to_ordered_int(b)
@@ -673,9 +967,7 @@ class order_shuffles:
673
 
674
  @staticmethod
675
  def last_dim():
676
- """Permute the last dim of every tensor arg identically: the
677
- accumulation order of a last-dim reduction changes, the value
678
- does not."""
679
  def shuffle(args, seed):
680
  g = torch.Generator(device=args[0].device).manual_seed(seed)
681
  p = torch.randperm(args[0].shape[-1], device=args[0].device,
@@ -685,8 +977,7 @@ class order_shuffles:
685
 
686
  @staticmethod
687
  def matmul_k():
688
- """Permute K of (A [.., M, K], B [.., K, N]) consistently: the
689
- product is unchanged, the accumulation order is not."""
690
  def shuffle(args, seed):
691
  a_m, b_m = args[0], args[1]
692
  g = torch.Generator(device=a_m.device).manual_seed(seed)
@@ -700,37 +991,70 @@ def _as_outputs(x):
700
  return list(x) if isinstance(x, (tuple, list)) else [x]
701
 
702
 
703
- def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
704
- ref_variants=None, ulp_tol=None, atol=None, rtol=None,
705
- band_margin=4.0, zero_atol=None, worst_k=8, seed=0):
706
- """Adversarial correctness comparison with ULP evidence.
707
-
708
- `gen(seed) -> args`; `ref(*args)` and `cand(*args)` return a tensor
709
- or tuple (worst output governs). With `order_shuffle(args, seed)`
710
- reordering contractual-noise dimensions (see `order_shuffles`), each
711
- element is judged against its own measured POINTWISE reduction-order
712
- band: pass is max(ulp / (own_band + 2)) <= band_margin; without a
713
- band, max ULP <= ulp_tol (default 2); `atol`/`rtol` are an
714
- alternative acceptance route. `zero_atol`: elements with
715
- |ref| < zero_atol are judged by absolute difference (ULP explodes
716
- across the denormal range). The report carries the top-`worst_k`
717
- offenders (index, ref, cand, ULP). Order shuffles cover the
718
- reference's own topology; a candidate with a different legitimate
719
- topology (split-K) needs `ref_variants`, callables `f(args) -> out`
720
- whose outputs widen the pointwise band."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
721
  worst = {"max_ulp": -1}
722
  total_hist = {}
723
  per_trial = []
724
  max_abs_seen = 0.0
725
  max_rel_seen = 0.0
 
726
  for t in range(trials):
727
  args = gen(seed + t)
 
 
 
 
728
  r_outs = _as_outputs(ref(*args))
729
  c_outs = _as_outputs(cand(*args))
730
- assert len(r_outs) == len(c_outs), "output arity mismatch"
731
- # pointwise band: each element is judged against its own measured
732
- # order sensitivity (cancellation makes near-zero outputs
733
- # legitimately order-sensitive while neighbors are stable)
 
 
 
 
 
 
 
 
 
 
 
734
  bands = None
735
  band = 0
736
  band_score = 0.0
@@ -754,8 +1078,11 @@ def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
754
  max_ulp = 0
755
  mean_ulp = 0.0
756
  first_div = -1
 
757
  n_el = 0
758
  n_above_band = 0
 
 
759
  nan_r = nan_c = inf_r = inf_c = 0
760
  worst_elements = []
761
  for oi, (r, c) in enumerate(zip(r_outs, c_outs)):
@@ -773,8 +1100,11 @@ def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
773
  mean_ulp += float(d.double().sum().item())
774
  n_el += d.numel()
775
  nz = (d > 0)
 
 
776
  if first_div < 0 and nz.any():
777
  first_div = int(nz.flatten().nonzero()[0].item())
 
778
  if nz.any():
779
  buckets = torch.clamp(
780
  torch.log2(d[nz].double()).floor(), 0, 62
@@ -791,6 +1121,7 @@ def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
791
  if val > 0:
792
  worst_elements.append({
793
  "output": oi, "index": int(ix),
 
794
  "ref": float(rf[ix].item()),
795
  "cand": float(cf[ix].item()),
796
  "ulp": int(val)})
@@ -810,23 +1141,27 @@ def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
810
  inf_r += int(torch.isinf(r).sum().item())
811
  inf_c += int(torch.isinf(c).sum().item())
812
  worst_elements.sort(key=lambda w: -w["ulp"])
 
813
  trial_rep = {
814
  "max_ulp": max_ulp,
815
  "mean_ulp": round(mean_ulp / max(n_el, 1), 3),
816
  "first_divergent_index": first_div,
 
817
  "nan_ref": nan_r, "nan_cand": nan_c,
818
  "inf_ref": inf_r, "inf_cand": inf_c,
819
  "order_band_ulp": band,
820
  "band_score": round(band_score, 2),
821
  "frac_above_band": round(n_above_band / max(n_el, 1), 6),
 
 
822
  "worst_elements": worst_elements[:worst_k],
823
  }
824
  per_trial.append(trial_rep)
825
  if max_ulp > worst["max_ulp"]:
826
  worst = trial_rep
827
- band_max = max(t["order_band_ulp"] for t in per_trial)
828
  max_seen = max(t["max_ulp"] for t in per_trial)
829
- if order_shuffle is not None or ref_variants:
 
830
  score = max(t["band_score"] for t in per_trial)
831
  ulp_ok = score <= band_margin
832
  criterion = (f"pointwise max(ulp / (order_band + 2)) = "
@@ -841,8 +1176,34 @@ def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
841
  max_rel_seen <= (rtol or 0) or max_abs_seen <= (atol or 0)
842
  criterion += (f"; OR max_abs {max_abs_seen:.3e} <= atol {atol} / "
843
  f"max_rel {max_rel_seen:.3e} <= rtol {rtol}")
844
- return {
845
- "pass": bool(ulp_ok or tol_ok),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
846
  "criterion": criterion,
847
  "worst_trial": worst,
848
  "max_abs": max_abs_seen,
@@ -850,6 +1211,9 @@ def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
850
  "trials": per_trial,
851
  "ulp_hist_log2": dict(sorted(total_hist.items())),
852
  }
 
 
 
853
 
854
 
855
  _FUZZ_VARIANTS = ("contig", "transposed", "strided_rows", "offset_slice",
@@ -865,6 +1229,30 @@ def _context_alive(device):
865
  return False
866
 
867
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
868
  def _fuzz_build(make, shape, dtype):
869
  try:
870
  out = make(tuple(shape), dtype)
@@ -881,7 +1269,7 @@ def _fuzz_variant(x, v):
881
  if v == "strided_rows":
882
  return x[::2] if x.dim() >= 1 and x.shape[0] >= 2 else x
883
  if v == "offset_slice":
884
- # a storage-offset-1 view: misaligned for vectorized loads
885
  return x.flatten()[1:] if x.numel() > 1 else x
886
  if v == "overflow_expand":
887
  if x.numel() == 0:
@@ -895,8 +1283,7 @@ def _fuzz_variant(x, v):
895
 
896
  def _fuzz_group(op, ref, make, shape, dtype, variants, ulp_tol, device,
897
  stop_on_context_loss=True):
898
- """All variant cases for one (shape, dtype) group; returns (rows,
899
- context_lost)."""
900
  rows = []
901
  bases = _fuzz_build(make, shape, dtype)
902
  lost = False
@@ -933,8 +1320,7 @@ def _fuzz_group(op, ref, make, shape, dtype, variants, ulp_tol, device,
933
  got = op(*xs)
934
  want = ref(*xs)
935
  if v == "overflow_expand":
936
- # bounded comparison: the head plus the high-index tail,
937
- # which is where int32 flat indexing wraps
938
  w = 1 << 20
939
  gf, wf = got.flatten(), want.flatten()
940
  pairs = ((gf[:w], wf[:w]), (gf[-w:], wf[-w:]))
@@ -959,20 +1345,30 @@ def _fuzz_group(op, ref, make, shape, dtype, variants, ulp_tol, device,
959
  return rows, lost
960
 
961
 
962
- # isolate child: a plain subprocess that bootstraps its own metakernel
963
- # (get_kernel, else the dev-root JIT fallback) and unpickles op/ref/make
964
- # by module reference, so it works under get_kernel's synthetic names
965
- _REPO_ID = "phanerozoic/metakernel"
966
- _REPO_VERSION = 1
967
-
968
- _ISOLATE_BOOTSTRAP = r"""
969
- import json
970
- import os
971
- import pickle
972
- import sys
973
-
 
 
 
 
 
 
 
 
 
 
974
  meta = json.load(open(sys.argv[1], "r"))
975
- sys.path[:0] = meta["sys_path"] # BEFORE unpickling module-ref functions
976
  try:
977
  import kernels
978
  _mk = kernels.get_kernel(meta["repo"], version=meta["version"],
@@ -985,85 +1381,165 @@ except Exception:
985
  import load_local
986
  _mk = load_local.load()
987
  import torch
988
- op, ref, make = pickle.load(open(sys.argv[2], "rb"))
989
- rows, _lost = _mk._fuzz_group(
990
- op, ref, make, tuple(meta["shape"]), getattr(torch, meta["dtype"]),
991
- tuple(meta["variants"]), meta["ulp_tol"], meta["device"],
992
- stop_on_context_loss=False)
993
- pickle.dump(rows, open(sys.argv[3], "wb"))
 
 
 
 
 
 
 
 
 
 
 
 
994
  """
995
 
996
 
997
- def fuzz_shapes(op, ref, make, shapes=None, variants=_FUZZ_VARIANTS,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
998
  dtypes=(torch.float32,), ulp_tol=64, isolate=False,
999
- isolate_timeout_s=180, device="cuda"):
1000
- """Run op vs ref across edge shapes, dtypes, and layout mutations;
1001
- report ok / mismatch / exception per case.
1002
-
1003
- `make(shape, dtype)` (or `make(shape)`) builds a tensor or tuple;
1004
- layout variants derive non-contiguous and misaligned views whose
1005
- content equals a contiguous copy. `overflow_expand` stride-0-expands
1006
- past 2**31 logical elements to catch int32 indexing (`skipped_vram`
1007
- when it would not fit). Every case carries a `repro` recipe.
1008
- In-process, the CUDA context is health-checked after exceptions and
1009
- remaining cases marked `context_lost` if killed; `isolate=True`
1010
- spawns each (shape, dtype) group in a subprocess, so a
1011
- context-killing candidate costs one group (`crashed_isolated`);
1012
- op/ref/make must be picklable."""
1013
  if shapes is None:
1014
  shapes = [(), (7,), (0, 8), (1, 1), (1, 17), (3, 63), (17, 129),
1015
  (128, 128), (1023, 5), (2, 4096), (2, 3, 5)]
 
 
1016
 
1017
  rows = []
1018
  lost = False
1019
  if isolate:
1020
  import json
1021
  import pickle
1022
- import subprocess
1023
  import sys as _sys
1024
  import tempfile
1025
  tmpdir = tempfile.mkdtemp(prefix="mk_fuzz_")
1026
  fns = os.path.join(tmpdir, "fns.pkl")
 
 
1027
  with open(fns, "wb") as f:
1028
- pickle.dump((op, ref, make), f)
1029
- for dtype in dtypes:
1030
- for shape in shapes:
1031
- meta = os.path.join(tmpdir, "meta.json")
1032
- out = os.path.join(tmpdir, "rows.pkl")
1033
- if os.path.exists(out):
1034
- os.remove(out)
1035
- with open(meta, "w", encoding="utf-8") as f:
1036
- json.dump({
1037
- "sys_path": [p for p in _sys.path
1038
- if isinstance(p, str)],
1039
- "repo": _REPO_ID, "version": _REPO_VERSION,
 
 
 
 
1040
  "shape": list(shape),
1041
  "dtype": str(dtype).replace("torch.", ""),
1042
  "variants": list(variants),
1043
  "ulp_tol": ulp_tol, "device": device,
1044
- }, f)
1045
- group = None
1046
- stderr_tail = ""
1047
- try:
1048
- r = subprocess.run(
1049
- [_sys.executable, "-c", _ISOLATE_BOOTSTRAP, meta,
1050
- fns, out], timeout=isolate_timeout_s,
1051
- capture_output=True)
1052
- if r.returncode == 0 and os.path.exists(out):
1053
  with open(out, "rb") as f:
1054
- group = pickle.load(f)
1055
- exitcode = r.returncode
1056
- stderr_tail = r.stderr.decode(
1057
- errors="replace")[-400:]
1058
- except subprocess.TimeoutExpired:
1059
- exitcode = "timeout"
1060
- if group is None:
1061
- group = [{"shape": tuple(shape), "variant": "*",
1062
- "dtype": str(dtype).replace("torch.", ""),
1063
- "status": "crashed_isolated",
1064
- "exitcode": exitcode,
1065
- "stderr_tail": stderr_tail}]
1066
- rows.extend(group)
1067
  else:
1068
  pending = [(dt, sh) for dt in dtypes for sh in shapes]
1069
  for gi, (dtype, shape) in enumerate(pending):
@@ -1080,21 +1556,27 @@ def fuzz_shapes(op, ref, make, shapes=None, variants=_FUZZ_VARIANTS,
1080
  n_bad = sum(1 for r in rows
1081
  if r["status"] not in ("ok", "skipped_vram",
1082
  "skipped_empty"))
1083
- return {"cases": rows, "failed": n_bad, "total": len(rows),
1084
- "context_lost": lost}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1085
 
1086
 
1087
  def sweep(factory, grid, iters=30, warmup=5, bytes=None, flops=None,
1088
  check=None, budget_s=None, race=False):
1089
- """Launch-configuration search: `factory(**point) -> callable`; one
1090
- bench row per grid point, sorted fastest first. A point whose factory
1091
- or run raises is reported, not skipped silently. `check(fn) -> bool`
1092
- is an optional correctness gate recorded per row (a fast wrong config
1093
- must not win a sweep). `budget_s` caps total sweep time; points not
1094
- reached are reported as `not_run` rather than silently dropped.
1095
- `race=True` runs successive halving: a quick pass over every point at
1096
- iters/4, then the full budget only on the fastest quartile; pruned
1097
- points keep their quick measurement and status `pruned_race`."""
1098
  keys = list(grid.keys())
1099
 
1100
  def expand(i, acc):
@@ -1159,13 +1641,71 @@ def sweep(factory, grid, iters=30, warmup=5, bytes=None, flops=None,
1159
  continue
1160
  rows.append(run_point(p, iters))
1161
  rows.sort(key=lambda r: (not r.get("check", True), r["median_ms"]))
 
 
 
 
 
 
 
 
 
 
 
1162
  return rows
1163
 
1164
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1165
  def alloc_stamps(n, blocks=None, device="cuda"):
1166
- """Device buffer for the MK_STAMP convention (metakernel_cuda/
1167
- stamps.cuh): the candidate kernel writes clock64() into slot i. With
1168
- `blocks`, allocates the [blocks, n] layout for MK_STAMP_BLOCK."""
1169
  if blocks is not None:
1170
  return torch.zeros(blocks, n, dtype=torch.int64, device=device)
1171
  return torch.zeros(n, dtype=torch.int64, device=device)
@@ -1175,8 +1715,7 @@ _DRIFT_SKIP = ("device", "clock", "launch_overhead", "transfer_bw")
1175
 
1176
 
1177
  def pinned_dossiers():
1178
- """Names of the dossiers pinned in this release (one per certified
1179
- architecture)."""
1180
  return sorted(_PINNED.keys())
1181
 
1182
 
@@ -1195,11 +1734,7 @@ def _numeric_leaves(d, prefix=""):
1195
 
1196
 
1197
  def dossier_drift(current, pinned=None, rel_tol=0.35, skip=_DRIFT_SKIP):
1198
- """Compare a fresh dossier against the release-pinned one for the
1199
- same architecture: per-leaf relative drift, worst drift, entries
1200
- over `rel_tol`. Host-dependent entries and raw clocks are skipped by
1201
- default (`skip=()` compares everything); `pinned=None` auto-selects
1202
- by sm_arch and raises if no pin exists."""
1203
  if pinned is None:
1204
  arch = current["device"]["sm_arch"]
1205
  matches = [k for k, v in _PINNED.items()
@@ -1222,16 +1757,19 @@ def dossier_drift(current, pinned=None, rel_tol=0.35, skip=_DRIFT_SKIP):
1222
  "rel": round(rel, 4)})
1223
  worst = max((e["rel"] for e in entries), default=0.0)
1224
  over = [e for e in entries if e["rel"] > rel_tol]
1225
- return {"entries": entries, "worst_rel": worst, "over_tol": over,
1226
- "rel_tol": rel_tol, "compared": len(entries),
1227
- "pass": len(over) == 0}
 
 
 
 
 
1228
 
1229
 
1230
  def stamped_demo(n=1 << 22, fma_iters=4000, blocks=None, threads=256,
1231
  device="cuda"):
1232
- """Run the shipped MK_STAMP_BLOCK example kernel (global load, FMA
1233
- compute, global store) and decode its stamps; a template for
1234
- instrumenting a candidate."""
1235
  props = torch.cuda.get_device_properties(device)
1236
  if blocks is None:
1237
  blocks = props.multi_processor_count
@@ -1244,11 +1782,7 @@ def stamped_demo(n=1 << 22, fma_iters=4000, blocks=None, threads=256,
1244
 
1245
 
1246
  def read_stamps(stamps, labels=None, clock_ghz=None):
1247
- """Decode MK_STAMP phases: consecutive deltas in ticks and in
1248
- microseconds under the measured (or provided) SM clock. A 1-D buffer
1249
- (MK_STAMP: block 0 only) yields one delta per phase; a 2-D [B, n]
1250
- buffer (MK_STAMP_BLOCK) yields min / median / max across blocks per
1251
- phase, with blocks whose stamps stayed zero excluded."""
1252
  if clock_ghz is None:
1253
  clock_ghz = measure_clock(stamps.device)
1254
 
@@ -1284,3 +1818,104 @@ def read_stamps(stamps, labels=None, clock_ghz=None):
1284
  return {"phases": phases, "clock_ghz": round(clock_ghz, 3),
1285
  "blocks_reporting": int(live.sum().item()),
1286
  "blocks_total": int(live.numel())}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Measuring instrument for the kernel-design loop: measured device facts, throttle-aware timing, adversarial correctness comparison, all as loadable ops returning plain data."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  import math
3
  import os
4
  import time
 
8
  try:
9
  from ._ops import ops
10
  except ImportError: # pragma: no cover - spawned child in a source tree
11
+ # children re-import the package; a source checkout rebuilds the op table from the cached JIT extension
 
 
12
  _root = os.environ.get("METAKERNEL_DEV_ROOT")
13
  if not _root:
14
  raise
 
30
  except ImportError: # pragma: no cover - fresh tree before first pin
31
  _PINNED = {}
32
 
33
+ __all__ = ["probe_device", "probe_all_devices", "bench", "compare", "judge",
34
+ "order_shuffles", "fuzz_shapes", "sweep", "alloc_stamps",
35
+ "read_stamps", "stamped_demo", "measure_clock", "ulp_diff",
36
+ "dossier_drift", "pinned_dossiers", "mma_tile_check", "schema",
37
+ "probe_capturable", "ops"]
38
+
39
+ _REPO_ID = "phanerozoic/metakernel"
40
+ _REPO_VERSION = 1
41
+ _PROBE_SCHEMA = 2
42
+
43
+ # kind id, C-tile M*N, nominal K, nominal K-lanes per unrolled loop iteration
44
  _MMA_KINDS = {
45
+ "fp16": (0, 16 * 16, 16, 64.0),
46
+ "bf16": (1, 16 * 16, 16, 64.0),
47
+ "tf32": (2, 16 * 16, 8, 32.0),
48
+ "int8": (3, 16 * 16, 16, 64.0),
49
+ "fp8_e4m3": (4, 16 * 8, 32, 256.0),
50
+ "fp16_sparse_2_4": (5, 16 * 8, 32, 128.0),
51
+ "fp8_e5m2": (6, 16 * 8, 32, 256.0),
52
  }
53
 
54
 
 
57
 
58
 
59
  def _events(fn, reps):
60
+ """Time reps calls of fn with one cudaEvent pair each; returns a ms list."""
61
  starts = [torch.cuda.Event(enable_timing=True) for _ in range(reps)]
62
  stops = [torch.cuda.Event(enable_timing=True) for _ in range(reps)]
63
  for s, e in zip(starts, stops):
 
69
 
70
 
71
  def measure_clock(device="cuda", ticks=4_000_000, blocks=None):
72
+ """Effective SM clock in GHz from clock64 ticks over wall time under an FMA spin."""
 
73
  props = torch.cuda.get_device_properties(device)
74
  if blocks is None:
75
  blocks = props.multi_processor_count
 
102
 
103
 
104
  def _sattolo_ring(n, device, generator=None):
105
+ """Random single-cycle permutation as an int32 next-pointer table."""
 
106
  perm = torch.randperm(n, generator=generator)
107
  ring = torch.empty(n, dtype=torch.int32)
108
  ring[perm] = torch.roll(perm, -1).to(torch.int32)
 
110
 
111
 
112
  def _smi_settings(index):
113
+ """ECC mode, driver, power limit via nvidia-smi; None fields when unavailable."""
 
114
  import subprocess
115
  try:
116
  out = subprocess.run(
 
130
  return {"ecc": None, "driver": None, "power_limit_w": None}
131
 
132
 
133
+ def _unravel(flat, shape):
134
+ """Multi-dimensional index for a flat index into shape."""
135
+ if not shape:
136
+ return []
137
+ out = []
138
+ for s in reversed(list(shape)):
139
+ out.append(int(flat % s))
140
+ flat //= s
141
+ return list(reversed(out))
142
+
143
+
144
  def mma_tile_check(device="cuda", trials=8):
145
+ """Random small-integer m16n16k16 tiles through the wmma path, compared exactly to the reference GEMM."""
 
 
146
  worst = 0.0
147
  for t in range(trials):
148
  g = torch.Generator(device=device).manual_seed(1000 + t)
 
160
  "trials": trials}
161
 
162
 
163
+ def _tenancy_canary(device, rel_tol=0.10, attempts=3):
164
+ """Fixed-cost triad timed in paired rounds; busy only when every attempt disagrees."""
165
+ n4 = 4 << 20
166
+ a = torch.rand(n4 * 4, device=device)
167
+ b = torch.rand(n4 * 4, device=device)
168
+ c = torch.empty(n4 * 4, device=device)
169
+ ops.mk_triad(a, b, c, 4, 1.5)
170
+ rels = []
171
+ for _ in range(attempts):
172
+ r1 = _median(_events(lambda: ops.mk_triad(a, b, c, 4, 1.5), 3))
173
+ r2 = _median(_events(lambda: ops.mk_triad(a, b, c, 4, 1.5), 3))
174
+ rel = abs(r1 - r2) / min(r1, r2)
175
+ rels.append(round(rel, 4))
176
+ if rel <= rel_tol:
177
+ return False, {"rel_diffs": rels, "rel_tol": rel_tol}
178
+ return True, {"rel_diffs": rels, "rel_tol": rel_tol}
179
+
180
+
181
+ def _cache_dir():
182
+ base = os.environ.get("LOCALAPPDATA") or os.path.expanduser("~/.cache")
183
+ d = os.path.join(base, "metakernel")
184
+ os.makedirs(d, exist_ok=True)
185
+ return d
186
+
187
+
188
+ def _cache_path(device, quick):
189
+ import re
190
+ props = torch.cuda.get_device_properties(device)
191
+ smi = _smi_settings(torch.device(device).index or 0)
192
+ slug = re.sub(r"[^a-z0-9]+", "", props.name.lower())[:24]
193
+ key = "sm%d%d_%s_drv%s_cu%s_q%d_s%d" % (
194
+ props.major, props.minor, slug, smi["driver"] or "na",
195
+ torch.version.cuda, int(quick), _PROBE_SCHEMA)
196
+ return os.path.join(_cache_dir(), key + ".json")
197
+
198
+
199
+ def _mma_calibrate(kind_id, sink):
200
+ """Executed K-lanes per loop iteration from a sub-saturation accumulator read-back, with a linearity check."""
201
+ gains = []
202
+ for cal in (4, 32):
203
+ sink[1] = 0.0
204
+ ops.mk_mma(kind_id, 1, 1, cal, sink)
205
+ _sync()
206
+ gains.append(float(sink[1].item()) / cal)
207
+ linear = gains[0] > 0 and abs(gains[1] - gains[0]) / gains[0] < 0.02
208
+ return gains[0], linear
209
+
210
+
211
+ def _fp8_accum_stall(kind_id, sink, k_exec):
212
+ """Growth curve of the fp8 accumulator; a stall reveals the effective accumulate mantissa width."""
213
+ vals = []
214
+ for it in (32, 64, 128, 256, 512):
215
+ sink[1] = 0.0
216
+ ops.mk_mma(kind_id, 1, 1, it, sink)
217
+ _sync()
218
+ vals.append(float(sink[1].item()) / 2.0)
219
+ if vals[-1] == vals[-2] and vals[-1] < vals[0] * 16 * 0.99:
220
+ bits = int(round(math.log2(vals[-1] / (2.0 * k_exec))))
221
+ return {"stall_element_value": vals[-1],
222
+ "effective_accum_mantissa_bits": bits}
223
+ return {"stall_element_value": None,
224
+ "effective_accum_mantissa_bits": None}
225
+
226
+
227
+ def probe_device(device="cuda", quick=False, cached=True,
228
+ tenancy_check=True):
229
+ """Empirical device dossier; every entry measured by a dedicated probe kernel and stamped with its SM clock."""
230
+ cpath = _cache_path(device, quick)
231
+ if cached and os.path.exists(cpath):
232
+ import json
233
+ return json.load(open(cpath, encoding="utf-8"))
234
+ if tenancy_check:
235
+ busy, ev = _tenancy_canary(device)
236
+ if busy:
237
+ raise RuntimeError("device_busy: canary rounds disagree %s; "
238
+ "idle the device or pass "
239
+ "tenancy_check=False" % ev)
240
  props = torch.cuda.get_device_properties(device)
241
  sm = props.multi_processor_count
242
  l2 = props.L2_cache_size
 
249
  "vram_bytes": props.total_memory,
250
  "torch": torch.__version__,
251
  "cuda_runtime": torch.version.cuda,
252
+ # instrument settings recorded because readings depend on them
 
253
  **_smi_settings(torch.device(device).index or 0),
254
  }
255
  }
 
274
  }
275
  clk = max(traj)
276
 
277
+ # ---- device-memory bandwidth per access width plus read/write/gather
278
+ n4 = 16 << 20
 
279
  if props.total_memory < 4 << 30:
280
  n4 = 4 << 20
281
  a = torch.rand(n4 * 4, device=device)
 
299
  dtype=torch.int32).to(device)
300
  ops.mk_gather(a, idx, c, 256, 0)
301
  ms = _median(_events(lambda: ops.mk_gather(a, idx, c, 256, 0), reps))
 
302
  hbm["random_gather_16B"] = round(
303
  (idx.numel() * 4 + 2 * n4 * 16) / (ms * 1e-3) / 1e9, 1)
304
  d["dram_bw"] = _entry(hbm, "GB/s", "streaming triad c=a+s*b per access "
 
309
  "so its nominal number is the achievable rate for "
310
  "random access, not a line-efficiency claim")
311
 
312
+ # ---- L2 bandwidth over an L2-resident working set
313
  ws = max(l2 // 4, 1 << 20)
314
+ nl2 = ws // 16
315
  al = a[:nl2 * 4].contiguous()
316
  bl = b[:nl2 * 4].contiguous()
317
  cl = c[:nl2 * 4].contiguous()
 
326
  "medium", "first pass streams from HBM; amortized "
327
  f"over {passes} passes")
328
 
329
+ # ---- latencies: min of 3 chases per level so a co-tenant burst cannot land in the dossier
 
 
330
  lat = {}
331
  idle_clk = measure_clock(device, ticks=2_000_000, blocks=1)
 
 
 
 
 
 
 
332
 
333
+ def chase(op, ring, warm_hops, hops, extra=None):
334
+ best = None
335
+ for _ in range(3):
336
+ if extra is None:
337
+ op(ring, warm_hops, out64)
338
+ _sync()
339
+ op(ring, hops, out64)
340
+ else:
341
+ op(ring, extra, warm_hops, out64)
342
+ _sync()
343
+ op(ring, extra, hops, out64)
344
+ _sync()
345
+ t = int(out64[0].item()) / hops
346
+ best = t if best is None else min(best, t)
347
+ return best
 
 
348
 
349
+ ring_s = _sattolo_ring(8192, device)
350
+ lat["smem_cycles"] = round(
351
+ chase(lambda r, h, o: ops.mk_chase_shared(r, h, o), ring_s,
352
+ 1 << 14, 1 << 16), 1)
353
+ ring_l1 = _sattolo_ring((16 << 10) // 4, device)
354
+ lat["l1_cycles"] = round(
355
+ chase(lambda r, h, o: ops.mk_chase_global(r, h, o), ring_l1,
356
+ ring_l1.numel(), 1 << 15), 1)
357
+ ring_l2 = _sattolo_ring((2 << 20) // 4, device)
358
+ lat["l2_cycles"] = round(
359
+ chase(lambda r, h, o: ops.mk_chase_global(r, h, o), ring_l2,
360
+ ring_l2.numel(), 1 << 14), 1)
361
  hbm_bytes = min(max(4 * l2, 64 << 20), props.total_memory // 8)
362
  ring_h = _sattolo_ring(int(hbm_bytes) // 4, device)
363
  hops_h = max((2 * l2) // 128, 1 << 17)
364
+ lat["dram_cycles"] = round(
365
+ chase(lambda r, h, o: ops.mk_chase_global(r, h, o), ring_h,
366
+ 1 << 14, hops_h), 1)
 
 
367
  lat["ns_per_cycle"] = round(1.0 / idle_clk, 3)
368
  d["latency"] = _entry(lat, "cycles (single-lane dependent loads)",
369
+ "Sattolo-cycle pointer chase, min of 3 runs; 16 "
370
+ "KB ring for L1, 2 MB TLB-resident ring for L2, "
371
+ "self-evicting chain (2x L2 in touched lines) "
372
+ "for HBM", idle_clk, "high",
373
  "smem includes index arithmetic; HBM includes "
374
  "residual TLB effects at large footprints")
375
  del ring_h, ring_l2, ring_l1, ring_s
376
 
377
+ # ---- fp32/fp64 FMA and tensor-core throughput
378
  blocks = sm * 4
379
  iters = 20_000 if quick else 60_000
380
  ops.mk_fma_f32(blocks, 256, iters, sink)
381
  ms = _median(_events(lambda: ops.mk_fma_f32(blocks, 256, iters, sink),
382
  reps))
383
+ flops = blocks * 256 * iters * 16 * 2 * 2
384
  d["fma_f32"] = _entry(round(flops / (ms * 1e-3) / 1e12, 2), "TFLOP/s",
385
  "register-resident FMA chains", clk, "high")
386
  sink64 = torch.zeros(2, dtype=torch.float64, device=device)
 
392
  d["fma_f64"] = _entry(round(flops64 / (ms * 1e-3) / 1e12, 3), "TFLOP/s",
393
  "register-resident double FMA chains (the DFMA "
394
  "pipe)", clk, "high")
395
+
396
+ # ---- ILP ladder: 1/2/4 chains at low occupancy separates latency-bound from pipe-bound
397
+ ilp = {}
398
+ it_ilp = 20_000 if quick else 60_000
399
+ for ch in (1, 2, 4):
400
+ ops.mk_fma_ilp(ch, sm, 64, it_ilp, sink)
401
+ ms = _median(_events(
402
+ lambda c2=ch: ops.mk_fma_ilp(c2, sm, 64, it_ilp, sink), reps))
403
+ ilp[f"ilp{ch}"] = round(
404
+ sm * 64 * it_ilp * 16 * 2 * ch / (ms * 1e-3) / 1e12, 2)
405
+ ilp["ratio_4_1"] = round(ilp["ilp4"] / ilp["ilp1"], 2)
406
+ d["fma_ilp"] = _entry(ilp, "TFLOP/s",
407
+ "1/2/4 independent dependent FMA chains per "
408
+ "thread at one block of 64 threads per SM; a "
409
+ "ratio near 4 means latency-bound at that "
410
+ "occupancy", clk, "high")
411
+
412
  mma = {}
413
  mma_klanes = {}
414
+ fp8_accum = {}
415
+ for name, (kind, mn, k_nom, nom_lanes) in _MMA_KINDS.items():
416
  it = 30_000 if quick else 100_000
417
  r = ops.mk_mma(kind, blocks, 8, 512, sink)
418
  if r != 0:
419
  mma[name] = "unsupported"
420
  continue
421
  _sync()
422
+ gain, linear = _mma_calibrate(kind, sink)
 
 
 
 
 
 
423
  mma_klanes[name] = {
424
+ "executed_k_per_iter": round(gain, 2),
425
+ "vs_nominal": round(gain / nom_lanes, 3),
426
+ "calibration_linear": linear,
427
  }
428
+ if name.startswith("fp8"):
429
+ fp8_accum[name] = _fp8_accum_stall(kind, sink, k_nom)
430
  ms = _median(_events(
431
  lambda k=kind, i=it: ops.mk_mma(k, blocks, 8, i, sink), reps))
432
+ tflops = blocks * 8 * it * 2.0 * mn * gain / (ms * 1e-3) / 1e12
433
+ if name == "fp16_sparse_2_4":
434
+ mma[name] = {"executed": round(tflops, 1),
435
+ "dense_equivalent": round(2 * tflops, 1)}
436
+ else:
437
+ mma[name] = round(tflops, 1)
438
  d["mma"] = _entry(mma, "TFLOP/s (TOP/s for int8)",
439
  "register-fragment mma_sync loops (wmma; fp8 and "
440
+ "2:4-sparse fp16 via inline PTX m16n8k32); flops "
441
+ "derive from a sub-saturation accumulator read-back "
 
442
  "with every fragment element observed, never the "
443
+ "declared loop shape; sparse reports executed and "
444
+ "dense-equivalent flops",
445
  clk, "medium",
446
  "dense-tile rate without memory traffic; real "
447
  "kernels see this only when staging hides behind it")
448
  d["mma"]["verified_k_lanes"] = mma_klanes
449
+ if fp8_accum:
450
+ d["mma"]["fp8_accum"] = fp8_accum
451
 
452
+ # ---- fed MMA: smem-staged fp16 tiles under cp.async double buffering
453
+ if props.major >= 8:
454
+ gbuf = torch.ones(16 << 20, dtype=torch.float16, device=device)
455
+ sink[1] = 0.0
456
+ r = ops.mk_mma_fed(gbuf, 1, 1, 64, sink)
457
+ if r == 0:
458
+ _sync()
459
+ fed_gain = float(sink[1].item()) / 64
460
+ it_fed = 6_000 if quick else 20_000
461
+ ops.mk_mma_fed(gbuf, sm * 2, 8, it_fed, sink)
462
+ ms = _median(_events(
463
+ lambda: ops.mk_mma_fed(gbuf, sm * 2, 8, it_fed, sink),
464
+ reps))
465
+ fed = sm * 2 * 8 * it_fed * 2.0 * 256 * fed_gain / \
466
+ (ms * 1e-3) / 1e12
467
+ d["mma_fed"] = _entry(
468
+ round(fed, 1), "TFLOP/s",
469
+ "m16n16k16 fp16 tiles fed from an L2-resident buffer "
470
+ "through cp.async double-buffered shared memory; "
471
+ "accumulator-verified", clk, "high",
472
+ "the achieved counterpart to the register peak: staging "
473
+ "exposed, one dependent accumulator chain per warp")
474
+ del gbuf
475
+
476
+ # ---- shared-memory bandwidth and the bank-conflict stride sweep
477
  it_sm = 30_000 if quick else 100_000
478
  ops.mk_smem_bw(blocks, 256, it_sm, sink)
479
  ms = _median(_events(lambda: ops.mk_smem_bw(blocks, 256, it_sm, sink),
 
483
  "block-resident read+write streaming (8 B per "
484
  "thread-iteration), aggregated across SMs", clk,
485
  "medium",
486
+ "bank-conflict-light access pattern; the stride "
487
+ "sweep below prices the conflicted case")
488
+ strides = (1, 8, 32) if quick else (1, 2, 4, 8, 16, 32)
489
+ it_st = 8_000 if quick else 20_000
490
+ st_ms = {}
491
+ for s2 in strides:
492
+ ops.mk_smem_stride(s2, blocks, 256, it_st, sink)
493
+ st_ms[s2] = _median(_events(
494
+ lambda s3=s2: ops.mk_smem_stride(s3, blocks, 256, it_st, sink),
495
+ reps))
496
+ d["smem_conflict_penalty"] = _entry(
497
+ {f"stride_{s2}": round(st_ms[s2] / st_ms[1], 2) for s2 in strides},
498
+ "x (time ratio vs stride 1)",
499
+ "dependent read-FMA-write per thread at warp-lane bank distance "
500
+ "`stride`; stride 32 puts every lane in one bank", clk, "high",
501
+ "the direct conflict measurement that disambiguates a "
502
+ "bank-conflict signature from an ILP stall")
503
 
504
  # ---- PCIe / host transfer bandwidth
505
  nbytes = 256 << 20
 
530
  "d2d counts one direction of traffic")
531
  del host_pin, host_pag, dev_t, dev_u
532
 
533
+ # ---- atomic contention curve
 
 
534
  n_thr = blocks * 256
535
  buf = torch.zeros(n_thr, dtype=torch.float32, device=device)
536
  at = {"curve_slots": {}}
 
540
  buf.zero_()
541
  ops.mk_atomics(1, blocks, 256, it_at, buf, slots)
542
  ms = _median(_events(
543
+ lambda s4=slots: ops.mk_atomics(1, blocks, 256, it_at, buf, s4),
544
  reps))
545
  at["curve_slots"][str(slots)] = round(
546
  n_thr * it_at / (ms * 1e-3) / 1e9, 2)
 
553
  "of distinct global addresses from 1 (full "
554
  "contention) to one per thread", clk, "high")
555
 
556
+ # ---- launch overhead: torch-dispatch path and the raw C++ path
557
  anchor = sink
558
  n_launch = 500 if quick else 2000
559
  _sync()
 
568
  ops.mk_empty(anchor)
569
  _sync()
570
  rt = (time.perf_counter() - t0) / n_rt * 1e6
571
+ raw_q = float(ops.mk_launch_raw(n_launch, 0, anchor))
572
+ raw_rt = float(ops.mk_launch_raw(n_rt, 1, anchor))
573
  d["launch_overhead"] = _entry(
574
+ {"torch_queued_us": round(queued, 2),
575
+ "torch_round_trip_us": round(rt, 2),
576
+ "raw_queued_us": round(raw_q / n_launch * 1e6, 2)
577
+ if raw_q >= 0 else None,
578
+ "raw_round_trip_us": round(raw_rt / n_rt * 1e6, 2)
579
+ if raw_rt >= 0 else None},
580
+ "us", "empty-kernel trains; torch path per-launch through op "
581
+ "dispatch, raw path looped inside one binding call", clk,
582
+ "medium", "host-state-dependent; the torch-minus-raw gap is the "
583
+ "dispatch cost a torch-launched candidate pays")
584
 
585
  # ---- grid barrier
586
  bar_iters = 500 if quick else 2000
 
596
  "unavailable or grid too large", clk,
597
  "high")
598
 
599
+ # ---- occupancy API table plus timed knees
 
 
600
  occ = {}
601
  for threads in (128, 256, 512, 1024):
602
  row = {}
 
617
  ops.mk_fma_dep(knee_blocks, threads, smem_kb * 1024,
618
  knee_iters, sink)
619
  ms = _median(_events(
620
+ lambda t=threads, s5=smem_kb: ops.mk_fma_dep(
621
+ knee_blocks, t, s5 * 1024, knee_iters, sink), reps))
622
  fl = knee_blocks * threads * knee_iters * 16 * 2
623
  row[f"{smem_kb}KB"] = round(fl / (ms * 1e-3) / 1e12, 2)
624
  knees[f"t{threads}"] = row
 
627
  "at forced (threads, dyn smem) occupancies over fixed total work; "
628
  "the cliff marks where occupancy stops covering latency",
629
  clk, "high", "timing-derived counterpart to the API table above")
630
+ if cached:
631
+ import json
632
+ json.dump(d, open(cpath, "w", encoding="utf-8"))
633
  return d
634
 
635
 
636
+ def probe_all_devices(quick=True, cached=True, tenancy_check=True):
637
+ """One dossier per visible device plus a pairwise P2P bandwidth matrix."""
638
+ n = torch.cuda.device_count()
639
+ devices = {}
640
+ for i in range(n):
641
+ devices[i] = probe_device(f"cuda:{i}", quick=quick, cached=cached,
642
+ tenancy_check=tenancy_check)
643
+ p2p = {}
644
+ if n > 1:
645
+ nbytes = 64 << 20
646
+ for i in range(n):
647
+ for j in range(n):
648
+ if i == j:
649
+ continue
650
+ src = torch.rand(nbytes // 4, device=f"cuda:{i}")
651
+ dst = torch.empty(nbytes // 4, device=f"cuda:{j}")
652
+ dst.copy_(src)
653
+ torch.cuda.synchronize(i)
654
+ torch.cuda.synchronize(j)
655
+ t0 = time.perf_counter()
656
+ for _ in range(5):
657
+ dst.copy_(src)
658
+ torch.cuda.synchronize(i)
659
+ torch.cuda.synchronize(j)
660
+ ms = (time.perf_counter() - t0) / 5 * 1e3
661
+ p2p[f"{i}->{j}"] = {
662
+ "gbs": round(nbytes / (ms * 1e-3) / 1e9, 1),
663
+ "peer_access": bool(
664
+ torch.cuda.can_device_access_peer(i, j)),
665
+ }
666
+ del src, dst
667
+ return {"devices": devices, "p2p": p2p}
668
+
669
+
670
+ _CAPTURE_PROBE = r"""
671
+ import pickle, sys, torch
672
+ fn = pickle.load(open(sys.argv[1], "rb"))
673
+ for _ in range(3):
674
+ fn()
675
+ torch.cuda.synchronize()
676
+ try:
677
+ g = torch.cuda.CUDAGraph()
678
+ with torch.cuda.graph(g):
679
+ fn()
680
+ print("capturable", flush=True)
681
+ except Exception as e:
682
+ print("uncapturable: %s: %s" % (type(e).__name__, str(e)[:120]),
683
+ flush=True)
684
+ """
685
+
686
+
687
+ def probe_capturable(fn, timeout_s=180):
688
+ """Test CUDA-graph capturability of fn in a child process, so a capture-wedged RNG never reaches the caller."""
689
+ import pickle
690
+ import subprocess
691
+ import sys as _sys
692
+ import tempfile
693
+ try:
694
+ blob = pickle.dumps(fn)
695
+ except Exception: # noqa: BLE001
696
+ return {"capturable": None,
697
+ "reason": "fn is not picklable; probe skipped (define fn "
698
+ "at module top level to enable it)"}
699
+ path = os.path.join(tempfile.mkdtemp(prefix="mk_cap_"), "fn.pkl")
700
+ with open(path, "wb") as f:
701
+ f.write(blob)
702
+ try:
703
+ r = subprocess.run([_sys.executable, "-c", _CAPTURE_PROBE, path],
704
+ capture_output=True, text=True,
705
+ timeout=timeout_s)
706
+ except Exception as e: # noqa: BLE001
707
+ return {"capturable": None,
708
+ "reason": f"probe child failed: {type(e).__name__}"}
709
+ out = (r.stdout or "").strip()
710
+ if out == "capturable":
711
+ return {"capturable": True, "reason": ""}
712
+ if out.startswith("uncapturable"):
713
+ return {"capturable": False, "reason": out}
714
+ return {"capturable": None,
715
+ "reason": "probe child exit %s: %s" % (
716
+ r.returncode, (r.stderr or "")[-200:])}
717
+
718
+
719
+ def _bench_verdict(rep):
720
+ parts = ["median %.4g ms" % rep["median_ms"]]
721
+ if "achieved_tflops" in rep:
722
+ parts.append("%.4g TFLOP/s" % rep["achieved_tflops"])
723
+ elif "achieved_gbs" in rep:
724
+ parts.append("%.4g GB/s" % rep["achieved_gbs"])
725
+ if "roofline" in rep:
726
+ parts.append("%.0f%% of %s roof" % (rep["roofline"]["pct_of_roof"],
727
+ rep["roofline"]["bound"]))
728
+ if rep["rejected_throttled"]:
729
+ parts.append("%d/%d rejected (%s)" % (
730
+ rep["rejected_throttled"], rep["samples"],
731
+ rep["rejection_basis"]))
732
+ if rep.get("no_clean_window"):
733
+ parts.append("no clean window: the whole run was throttled")
734
+ if rep.get("alloc_growth_bytes_per_call"):
735
+ parts.append("fn allocates %d B/call" %
736
+ rep["alloc_growth_bytes_per_call"])
737
+ if rep.get("host_overhead_suspected"):
738
+ parts.append("fn appears to sync or do host work inside")
739
+ return "; ".join(parts)
740
+
741
+
742
  def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
743
  dtype="fp16", clock_guard=0.97, clock_stride=8, graph=False,
744
+ graph_probe_isolated=False, device="cuda"):
745
+ """Median-of-N cudaEvent timing with throttle rejection, roofline placement, and footgun detection."""
746
+ for name, v in (("iters", iters), ("warmup", warmup)):
747
+ if not isinstance(v, int) or v < (1 if name == "iters" else 0):
748
+ raise ValueError(f"{name} must be a non-negative int; "
749
+ f"got {v!r}")
750
+ for name, v in (("bytes", bytes), ("flops", flops)):
751
+ if v is not None and (not isinstance(v, (int, float)) or v <= 0):
752
+ raise ValueError(f"{name} must be a positive number counting "
753
+ f"{name} per call of fn; got {v!r}")
 
754
  for _ in range(warmup):
755
  fn()
756
  _sync()
757
+ # footgun probes: per-call allocation growth and hidden host work
758
+ alloc0 = torch.cuda.memory_allocated(device)
759
+ fn()
760
+ fn()
761
+ _sync()
762
+ alloc_growth = max(0, (torch.cuda.memory_allocated(device) - alloc0)
763
+ // 2)
764
+ # a burst of enqueues that leaves the stream idle means fn drains it; only trustworthy above the launch-overhead floor
765
+ for _ in range(16):
766
+ fn()
767
+ stream_idle = bool(torch.cuda.current_stream(device).query())
768
+ _sync()
769
+ t0 = time.perf_counter()
770
+ pre = _events(fn, 4)
771
+ wall_ms = (time.perf_counter() - t0) * 1e3
772
+ ev_ms = sum(pre)
773
+ host_suspect = (stream_idle and ev_ms / 4 > 0.05) or \
774
+ wall_ms > ev_ms * 1.5 + 2.0
775
+
776
  graph_state = "off"
777
  graph_reason = ""
778
+ if graph and graph_probe_isolated:
779
+ cap = probe_capturable(fn)
780
+ if cap["capturable"] is False:
781
+ graph = False
782
+ graph_state = "fallback_eager"
783
+ graph_reason = "isolated probe: " + cap["reason"]
784
  if graph:
785
  pre_rng = torch.cuda.get_rng_state(device)
786
  try:
 
795
  except Exception as e: # noqa: BLE001 - fall back, say why
796
  graph_state = "fallback_eager"
797
  graph_reason = f"{type(e).__name__}: {str(e)[:120]}"
798
+ # drain the invalidated stream; a failed capture can wedge the device RNG on some torch versions
 
 
 
799
  rng_ok = True
800
  for _attempt in range(5):
801
  try:
 
825
  samples.append((_events(fn, n), len(clocks) - 1))
826
  clocks.append(measure_clock(device, ticks=probe_ticks))
827
  best_clk = max(clocks)
828
+
829
+ # rejection basis: memory-bound work is judged on its own rate, not the SM clock
830
+ ridge_memory = False
831
+ if dossier is not None and bytes and flops:
832
+ bw0 = max(dossier["dram_bw"]["value"].values()) * 1e9
833
+ mma_v0 = dossier["mma"]["value"].get(dtype)
834
+ if isinstance(mma_v0, dict):
835
+ mma_v0 = mma_v0.get("dense_equivalent")
836
+ peak0 = (mma_v0 if isinstance(mma_v0, (int, float))
837
+ else dossier["fma_f32"]["value"]) * 1e12
838
+ ridge_memory = (flops / bytes) * bw0 < peak0
839
+ basis = "bandwidth_consistency" if (
840
+ bytes is not None and (flops is None or ridge_memory)) \
841
+ else "sm_clock"
842
+
843
  flat = []
844
+ kept = []
845
+ rejected = 0
846
+ if basis == "sm_clock":
847
+ for group, ci in samples:
848
+ window_ok = (clocks[ci] >= clock_guard * best_clk and
849
+ clocks[min(ci + 1, len(clocks) - 1)] >=
850
+ clock_guard * best_clk)
851
+ for ms in group:
852
+ flat.append(ms)
853
+ if window_ok:
854
+ kept.append(ms)
855
+ else:
856
+ rejected += 1
857
+ else:
858
+ for group, _ci in samples:
859
+ flat.extend(group)
860
+ floor = sorted(flat)[max(0, int(0.05 * len(flat)) - 1)]
861
+ for ms in flat:
862
+ if ms <= floor * 1.15:
863
  kept.append(ms)
864
  else:
865
  rejected += 1
866
+ all_rejected = not kept
867
+ if all_rejected:
868
  kept = flat
869
  med = _median(kept)
870
  ks = sorted(kept)
871
  n_k = len(ks)
872
+ # order-statistic 95% CI on the median; degenerate at tiny n
 
873
  half = 1.959964 * (n_k ** 0.5) / 2.0
874
  lo_i = max(0, int(n_k / 2 - half) - 1)
875
  hi_i = min(n_k - 1, int(n_k / 2 + half))
 
884
  "mean_ms": round(sum(kept) / n_k, 4),
885
  "samples": len(flat),
886
  "rejected_throttled": rejected,
887
+ "rejection_basis": basis,
888
  "graph_captured": graph_state == "captured",
889
  "clock_ghz": {"best": round(best_clk, 3),
890
  "worst": round(min(clocks), 3)},
891
  }
892
+ no_clean = all_rejected
893
+ if basis == "sm_clock" and dossier is not None:
894
+ ref_clk = dossier["clock"]["max_ghz"]
895
+ if best_clk < clock_guard * ref_clk:
896
+ no_clean = True
897
+ if no_clean:
898
+ rep["no_clean_window"] = True
899
  if graph_reason:
900
  rep["graph_fallback_reason"] = graph_reason
901
+ if alloc_growth:
902
+ rep["alloc_growth_bytes_per_call"] = int(alloc_growth)
903
+ if host_suspect:
904
+ rep["host_overhead_suspected"] = True
905
+ rep["host_wall_over_event"] = round(wall_ms / max(ev_ms, 1e-9), 2)
906
  if bytes is not None:
907
  rep["achieved_gbs"] = round(bytes / (med * 1e-3) / 1e9, 1)
908
  if flops is not None:
 
912
  if dossier is not None and bytes and flops:
913
  bw = max(dossier["dram_bw"]["value"].values()) * 1e9
914
  mma_v = dossier["mma"]["value"].get(dtype)
915
+ if isinstance(mma_v, dict):
916
+ mma_v = mma_v.get("dense_equivalent")
917
  peak = (mma_v if isinstance(mma_v, (int, float))
918
  else dossier["fma_f32"]["value"]) * 1e12
919
  ai = flops / bytes
 
924
  "pct_of_roof": round(100 * (flops / (med * 1e-3)) / roof, 1),
925
  "peak_dtype": dtype,
926
  }
927
+ rep["verdict"] = _bench_verdict(rep)
928
  return rep
929
 
930
 
931
  def _to_ordered_int(t):
932
+ """Monotone integer mapping of float bit patterns; differences are ULP distances."""
 
 
933
  if t.dtype == torch.float32:
934
  u = t.view(torch.int32).to(torch.int64) & 0xFFFFFFFF
935
  half = 1 << 31
 
945
 
946
 
947
  def ulp_diff(a, b):
948
+ """Elementwise ULP distance; both-NaN counts 0, one-sided NaN 2**62, integers by absolute difference."""
949
+ if a.dtype != b.dtype or a.shape != b.shape:
950
+ raise ValueError("ulp_diff needs matching dtype and shape; got "
951
+ f"{a.dtype}{tuple(a.shape)} vs "
952
+ f"{b.dtype}{tuple(b.shape)}; cast or reshape the "
953
+ "candidate to the reference layout")
954
  if not a.is_floating_point():
955
  return (a.to(torch.int64) - b.to(torch.int64)).abs()
956
  ia, ib = _to_ordered_int(a), _to_ordered_int(b)
 
967
 
968
  @staticmethod
969
  def last_dim():
970
+ """Identical permutation of every arg's last dim: reduction order changes, value does not."""
 
 
971
  def shuffle(args, seed):
972
  g = torch.Generator(device=args[0].device).manual_seed(seed)
973
  p = torch.randperm(args[0].shape[-1], device=args[0].device,
 
977
 
978
  @staticmethod
979
  def matmul_k():
980
+ """Consistent K permutation of (A [.., M, K], B [.., K, N]): product unchanged, accumulation order not."""
 
981
  def shuffle(args, seed):
982
  a_m, b_m = args[0], args[1]
983
  g = torch.Generator(device=a_m.device).manual_seed(seed)
 
991
  return list(x) if isinstance(x, (tuple, list)) else [x]
992
 
993
 
994
+ def _compare_hints(trial):
995
+ hints = []
996
+ if trial.get("bias_one_sided_frac", 0) >= 0.9 and \
997
+ trial.get("frac_above_band", 0) > 0.5:
998
+ hints.append("systematic one-sided bias: check scale factors or "
999
+ "the accumulator dtype")
1000
+ if trial["nan_cand"] > trial["nan_ref"]:
1001
+ hints.append("candidate introduces NaN: check uninitialized "
1002
+ "memory or masking")
1003
+ nd = trial.get("first_divergent_nd")
1004
+ if nd and nd[-1] > 0 and any(nd[-1] % t == 0 for t in (16, 32, 64,
1005
+ 128)):
1006
+ hints.append("divergence starts on a tile-aligned index: check "
1007
+ "boundary indexing")
1008
+ return hints
1009
+
1010
+
1011
+ def compare(ref, cand, gen=None, trials=8, order_shuffle=None,
1012
+ order_trials=8, ref_variants=None, ulp_tol=None, atol=None,
1013
+ rtol=None, band_margin=4.0, zero_atol=None, worst_k=8, seed=0,
1014
+ inputs=None, frac_above_tol=0.5):
1015
+ """Adversarial correctness comparison with pointwise order bands, ULP evidence, and a one-sided-bias gate; the banded route cannot reject errors under band_margin*(band+2) ULP at order-stable elements."""
1016
+ if (gen is None) == (inputs is None):
1017
+ raise ValueError("pass exactly one of gen (a seed -> args "
1018
+ "callable) or inputs (a tuple of concrete "
1019
+ "tensors)")
1020
+ if inputs is not None:
1021
+ if isinstance(inputs, torch.Tensor):
1022
+ inputs = (inputs,)
1023
+ if not all(isinstance(x, torch.Tensor) for x in inputs):
1024
+ raise ValueError("inputs must be tensors; wrap scalars with "
1025
+ "torch.tensor first")
1026
+ fixed = tuple(inputs)
1027
+ gen = lambda seed: fixed # noqa: E731
1028
+ trials = 1
1029
  worst = {"max_ulp": -1}
1030
  total_hist = {}
1031
  per_trial = []
1032
  max_abs_seen = 0.0
1033
  max_rel_seen = 0.0
1034
+ out_shapes = []
1035
  for t in range(trials):
1036
  args = gen(seed + t)
1037
+ if not isinstance(args, (tuple, list)):
1038
+ raise ValueError("gen(seed) must return a tuple of args; got "
1039
+ f"{type(args).__name__}; return (x,) for a "
1040
+ "single-argument op")
1041
  r_outs = _as_outputs(ref(*args))
1042
  c_outs = _as_outputs(cand(*args))
1043
+ if len(r_outs) != len(c_outs):
1044
+ raise ValueError(f"reference returned {len(r_outs)} outputs, "
1045
+ f"candidate {len(c_outs)}; make the arities "
1046
+ "match")
1047
+ for oi, (r, c) in enumerate(zip(r_outs, c_outs)):
1048
+ if r.dtype != c.dtype:
1049
+ raise ValueError(f"output {oi} dtype mismatch: ref "
1050
+ f"{r.dtype}, cand {c.dtype}; cast the "
1051
+ "candidate to the reference dtype")
1052
+ if r.shape != c.shape:
1053
+ raise ValueError(f"output {oi} shape mismatch: ref "
1054
+ f"{tuple(r.shape)}, cand "
1055
+ f"{tuple(c.shape)}")
1056
+ out_shapes = [tuple(r.shape) for r in r_outs]
1057
+ # pointwise band: each element judged against its own measured order sensitivity
1058
  bands = None
1059
  band = 0
1060
  band_score = 0.0
 
1078
  max_ulp = 0
1079
  mean_ulp = 0.0
1080
  first_div = -1
1081
+ first_div_nd = None
1082
  n_el = 0
1083
  n_above_band = 0
1084
+ n_diverging = 0
1085
+ n_cand_high = 0
1086
  nan_r = nan_c = inf_r = inf_c = 0
1087
  worst_elements = []
1088
  for oi, (r, c) in enumerate(zip(r_outs, c_outs)):
 
1100
  mean_ulp += float(d.double().sum().item())
1101
  n_el += d.numel()
1102
  nz = (d > 0)
1103
+ n_diverging += int(nz.sum().item())
1104
+ n_cand_high += int(((c > r) & nz).sum().item())
1105
  if first_div < 0 and nz.any():
1106
  first_div = int(nz.flatten().nonzero()[0].item())
1107
+ first_div_nd = _unravel(first_div, r.shape)
1108
  if nz.any():
1109
  buckets = torch.clamp(
1110
  torch.log2(d[nz].double()).floor(), 0, 62
 
1121
  if val > 0:
1122
  worst_elements.append({
1123
  "output": oi, "index": int(ix),
1124
+ "index_nd": _unravel(int(ix), r.shape),
1125
  "ref": float(rf[ix].item()),
1126
  "cand": float(cf[ix].item()),
1127
  "ulp": int(val)})
 
1141
  inf_r += int(torch.isinf(r).sum().item())
1142
  inf_c += int(torch.isinf(c).sum().item())
1143
  worst_elements.sort(key=lambda w: -w["ulp"])
1144
+ frac_pos = n_cand_high / max(n_diverging, 1)
1145
  trial_rep = {
1146
  "max_ulp": max_ulp,
1147
  "mean_ulp": round(mean_ulp / max(n_el, 1), 3),
1148
  "first_divergent_index": first_div,
1149
+ "first_divergent_nd": first_div_nd,
1150
  "nan_ref": nan_r, "nan_cand": nan_c,
1151
  "inf_ref": inf_r, "inf_cand": inf_c,
1152
  "order_band_ulp": band,
1153
  "band_score": round(band_score, 2),
1154
  "frac_above_band": round(n_above_band / max(n_el, 1), 6),
1155
+ "bias_one_sided_frac": round(max(frac_pos, 1 - frac_pos), 4)
1156
+ if n_diverging else 0.0,
1157
  "worst_elements": worst_elements[:worst_k],
1158
  }
1159
  per_trial.append(trial_rep)
1160
  if max_ulp > worst["max_ulp"]:
1161
  worst = trial_rep
 
1162
  max_seen = max(t["max_ulp"] for t in per_trial)
1163
+ banded = order_shuffle is not None or ref_variants
1164
+ if banded:
1165
  score = max(t["band_score"] for t in per_trial)
1166
  ulp_ok = score <= band_margin
1167
  criterion = (f"pointwise max(ulp / (order_band + 2)) = "
 
1176
  max_rel_seen <= (rtol or 0) or max_abs_seen <= (atol or 0)
1177
  criterion += (f"; OR max_abs {max_abs_seen:.3e} <= atol {atol} / "
1178
  f"max_rel {max_rel_seen:.3e} <= rtol {rtol}")
1179
+ # one-sided bias gate: a uniform small error is above-band nearly everywhere in one direction
1180
+ bias_fail = False
1181
+ if banded and (ulp_ok or tol_ok):
1182
+ w_frac = max(t["frac_above_band"] for t in per_trial)
1183
+ w_side = max(t["bias_one_sided_frac"] for t in per_trial)
1184
+ if w_frac > frac_above_tol and w_side >= 0.9:
1185
+ bias_fail = True
1186
+ criterion += ("; FAILED bias gate: frac_above_band %.3f > %s "
1187
+ "with one-sided fraction %.2f" %
1188
+ (w_frac, frac_above_tol, w_side))
1189
+ passed = bool((ulp_ok or tol_ok) and not bias_fail)
1190
+ hints = [] if passed else _compare_hints(worst)
1191
+ if passed:
1192
+ verdict = "pass: " + (
1193
+ f"max {max_seen} ULP within the order band" if banded
1194
+ else f"max {max_seen} ULP")
1195
+ elif bias_fail:
1196
+ verdict = ("fail: uniform bias, %.0f%% of elements above their "
1197
+ "order band in one direction" %
1198
+ (100 * max(t["frac_above_band"] for t in per_trial)))
1199
+ else:
1200
+ w0 = worst["worst_elements"][0] if worst["worst_elements"] else None
1201
+ verdict = "fail: max %d ULP" % max_seen + (
1202
+ " at index %s (ref %.6g, cand %.6g)" %
1203
+ (w0["index_nd"], w0["ref"], w0["cand"]) if w0 else "")
1204
+ rep = {
1205
+ "pass": passed,
1206
+ "verdict": verdict,
1207
  "criterion": criterion,
1208
  "worst_trial": worst,
1209
  "max_abs": max_abs_seen,
 
1211
  "trials": per_trial,
1212
  "ulp_hist_log2": dict(sorted(total_hist.items())),
1213
  }
1214
+ if hints:
1215
+ rep["hints"] = hints
1216
+ return rep
1217
 
1218
 
1219
  _FUZZ_VARIANTS = ("contig", "transposed", "strided_rows", "offset_slice",
 
1229
  return False
1230
 
1231
 
1232
+ class _RandnMaker:
1233
+ """Picklable default maker: `arity` same-shape randn tensors (zeros for empty shapes)."""
1234
+
1235
+ def __init__(self, arity=1):
1236
+ self.arity = arity
1237
+
1238
+ def __call__(self, shape, dtype=torch.float32):
1239
+ n = math.prod(shape)
1240
+ if n:
1241
+ out = tuple(torch.randn(tuple(shape), device="cuda",
1242
+ dtype=dtype)
1243
+ for _ in range(self.arity))
1244
+ else:
1245
+ out = tuple(torch.zeros(tuple(shape), device="cuda",
1246
+ dtype=dtype)
1247
+ for _ in range(self.arity))
1248
+ return out if self.arity > 1 else out[0]
1249
+
1250
+
1251
+ def _default_make(arity=1):
1252
+ """Default maker instance; a class so isolate=True can transport it."""
1253
+ return _RandnMaker(arity)
1254
+
1255
+
1256
  def _fuzz_build(make, shape, dtype):
1257
  try:
1258
  out = make(tuple(shape), dtype)
 
1269
  if v == "strided_rows":
1270
  return x[::2] if x.dim() >= 1 and x.shape[0] >= 2 else x
1271
  if v == "offset_slice":
1272
+ # storage-offset-1 view: misaligned for vectorized loads
1273
  return x.flatten()[1:] if x.numel() > 1 else x
1274
  if v == "overflow_expand":
1275
  if x.numel() == 0:
 
1283
 
1284
  def _fuzz_group(op, ref, make, shape, dtype, variants, ulp_tol, device,
1285
  stop_on_context_loss=True):
1286
+ """All variant cases for one (shape, dtype) group; returns (rows, context_lost)."""
 
1287
  rows = []
1288
  bases = _fuzz_build(make, shape, dtype)
1289
  lost = False
 
1320
  got = op(*xs)
1321
  want = ref(*xs)
1322
  if v == "overflow_expand":
1323
+ # bounded comparison: head plus the high-index tail where int32 indexing wraps
 
1324
  w = 1 << 20
1325
  gf, wf = got.flatten(), want.flatten()
1326
  pairs = ((gf[:w], wf[:w]), (gf[-w:], wf[-w:]))
 
1345
  return rows, lost
1346
 
1347
 
1348
+ def _fn_spec(fn, name):
1349
+ """Picklable transport spec for a callable, with a source fallback for local defs."""
1350
+ import pickle
1351
+ try:
1352
+ return ("pickle", pickle.dumps(fn), None)
1353
+ except Exception: # noqa: BLE001
1354
+ import inspect
1355
+ import textwrap
1356
+ try:
1357
+ src = textwrap.dedent(inspect.getsource(fn))
1358
+ except (OSError, TypeError):
1359
+ src = ""
1360
+ if not src.startswith("def "):
1361
+ raise ValueError(
1362
+ f"{name} is not picklable and not a plain def; define "
1363
+ "op/ref/make as top-level functions (not lambdas or "
1364
+ "closures) to use isolate=True") from None
1365
+ return ("source", src, fn.__name__)
1366
+
1367
+
1368
+ _POOL_BOOTSTRAP = r"""
1369
+ import json, os, pickle, sys
1370
  meta = json.load(open(sys.argv[1], "r"))
1371
+ sys.path[:0] = meta["sys_path"]
1372
  try:
1373
  import kernels
1374
  _mk = kernels.get_kernel(meta["repo"], version=meta["version"],
 
1381
  import load_local
1382
  _mk = load_local.load()
1383
  import torch
1384
+ _ns = {"torch": torch}
1385
+ def _resolve(spec):
1386
+ kind, payload, name = spec
1387
+ if kind == "pickle":
1388
+ return pickle.loads(payload)
1389
+ exec(payload, _ns)
1390
+ return _ns[name]
1391
+ _op, _ref, _make = (_resolve(s) for s in
1392
+ pickle.load(open(meta["fns"], "rb")))
1393
+ print("ready", flush=True)
1394
+ for line in sys.stdin:
1395
+ job = json.loads(line)
1396
+ rows, _l = _mk._fuzz_group(
1397
+ _op, _ref, _make, tuple(job["shape"]),
1398
+ getattr(torch, job["dtype"]), tuple(job["variants"]),
1399
+ job["ulp_tol"], job["device"], stop_on_context_loss=False)
1400
+ pickle.dump(rows, open(job["out"], "wb"))
1401
+ print("done", flush=True)
1402
  """
1403
 
1404
 
1405
+ class _FuzzPool:
1406
+ """One persistent isolation worker fed groups over stdin, respawned only after a crash."""
1407
+
1408
+ def __init__(self, meta_path, stderr_path, timeout_s):
1409
+ self.meta_path = meta_path
1410
+ self.stderr_path = stderr_path
1411
+ self.timeout_s = timeout_s
1412
+ self.proc = None
1413
+ self.q = None
1414
+
1415
+ def _spawn(self):
1416
+ import queue
1417
+ import subprocess
1418
+ import sys as _sys
1419
+ import threading
1420
+ self.errf = open(self.stderr_path, "ab")
1421
+ self.proc = subprocess.Popen(
1422
+ [_sys.executable, "-c", _POOL_BOOTSTRAP, self.meta_path],
1423
+ stdin=subprocess.PIPE, stdout=subprocess.PIPE,
1424
+ stderr=self.errf, text=True)
1425
+ self.q = queue.Queue()
1426
+
1427
+ def reader(p, q):
1428
+ for line in p.stdout:
1429
+ q.put(line.strip())
1430
+ threading.Thread(target=reader, args=(self.proc, self.q),
1431
+ daemon=True).start()
1432
+ try:
1433
+ first = self.q.get(timeout=self.timeout_s)
1434
+ except Exception: # noqa: BLE001
1435
+ first = None
1436
+ return first == "ready"
1437
+
1438
+ def run(self, job_line):
1439
+ """Returns True when the worker reports done, False on crash or timeout."""
1440
+ import queue
1441
+ if self.proc is None or self.proc.poll() is not None:
1442
+ if not self._spawn():
1443
+ return False
1444
+ try:
1445
+ self.proc.stdin.write(job_line + "\n")
1446
+ self.proc.stdin.flush()
1447
+ except OSError:
1448
+ return False
1449
+ try:
1450
+ return self.q.get(timeout=self.timeout_s) == "done"
1451
+ except queue.Empty:
1452
+ if self.proc.poll() is None:
1453
+ self.proc.kill()
1454
+ return False
1455
+
1456
+ def stderr_tail(self):
1457
+ try:
1458
+ self.errf.flush()
1459
+ with open(self.stderr_path, "rb") as f:
1460
+ return f.read()[-400:].decode(errors="replace")
1461
+ except OSError:
1462
+ return ""
1463
+
1464
+ def close(self):
1465
+ if self.proc is not None and self.proc.poll() is None:
1466
+ self.proc.kill()
1467
+ try:
1468
+ self.errf.close()
1469
+ except Exception: # noqa: BLE001
1470
+ pass
1471
+
1472
+
1473
+ def _fuzz_hints(rows):
1474
+ hints = []
1475
+ bad = [c for c in rows if c["status"] == "mismatch"]
1476
+ if bad and all(c["variant"] in ("transposed", "strided_rows",
1477
+ "offset_slice") for c in bad):
1478
+ hints.append("mismatches only on non-contiguous variants: the op "
1479
+ "assumes contiguity; call .contiguous() or honor "
1480
+ "strides")
1481
+ if any(c["variant"] == "overflow_expand" and
1482
+ c["status"] == "mismatch" for c in rows):
1483
+ hints.append("overflow_expand mismatch: int32 indexing wraps past "
1484
+ "2^31 elements; use 64-bit index arithmetic")
1485
+ return hints
1486
+
1487
+
1488
+ def fuzz_shapes(op, ref, make=None, shapes=None, variants=_FUZZ_VARIANTS,
1489
  dtypes=(torch.float32,), ulp_tol=64, isolate=False,
1490
+ isolate_timeout_s=180, device="cuda", arity=1):
1491
+ """Edge shapes, dtypes, and layout mutations with per-case repro recipes; isolate=True survives context-killing candidates via a pooled worker."""
 
 
 
 
 
 
 
 
 
 
 
 
1492
  if shapes is None:
1493
  shapes = [(), (7,), (0, 8), (1, 1), (1, 17), (3, 63), (17, 129),
1494
  (128, 128), (1023, 5), (2, 4096), (2, 3, 5)]
1495
+ if make is None:
1496
+ make = _default_make(arity)
1497
 
1498
  rows = []
1499
  lost = False
1500
  if isolate:
1501
  import json
1502
  import pickle
 
1503
  import sys as _sys
1504
  import tempfile
1505
  tmpdir = tempfile.mkdtemp(prefix="mk_fuzz_")
1506
  fns = os.path.join(tmpdir, "fns.pkl")
1507
+ specs = [_fn_spec(f, n) for f, n in ((op, "op"), (ref, "ref"),
1508
+ (make, "make"))]
1509
  with open(fns, "wb") as f:
1510
+ pickle.dump(specs, f)
1511
+ meta_path = os.path.join(tmpdir, "meta.json")
1512
+ with open(meta_path, "w", encoding="utf-8") as f:
1513
+ json.dump({"sys_path": [p for p in _sys.path
1514
+ if isinstance(p, str)],
1515
+ "repo": _REPO_ID, "version": _REPO_VERSION,
1516
+ "fns": fns}, f)
1517
+ pool = _FuzzPool(meta_path, os.path.join(tmpdir, "stderr.log"),
1518
+ isolate_timeout_s)
1519
+ try:
1520
+ for dtype in dtypes:
1521
+ for shape in shapes:
1522
+ out = os.path.join(tmpdir, "rows.pkl")
1523
+ if os.path.exists(out):
1524
+ os.remove(out)
1525
+ job = json.dumps({
1526
  "shape": list(shape),
1527
  "dtype": str(dtype).replace("torch.", ""),
1528
  "variants": list(variants),
1529
  "ulp_tol": ulp_tol, "device": device,
1530
+ "out": out})
1531
+ ok = pool.run(job)
1532
+ if ok and os.path.exists(out):
 
 
 
 
 
 
1533
  with open(out, "rb") as f:
1534
+ rows.extend(pickle.load(f))
1535
+ else:
1536
+ rows.append({
1537
+ "shape": tuple(shape), "variant": "*",
1538
+ "dtype": str(dtype).replace("torch.", ""),
1539
+ "status": "crashed_isolated",
1540
+ "stderr_tail": pool.stderr_tail()})
1541
+ finally:
1542
+ pool.close()
 
 
 
 
1543
  else:
1544
  pending = [(dt, sh) for dt in dtypes for sh in shapes]
1545
  for gi, (dtype, shape) in enumerate(pending):
 
1556
  n_bad = sum(1 for r in rows
1557
  if r["status"] not in ("ok", "skipped_vram",
1558
  "skipped_empty"))
1559
+ rep = {"cases": rows, "failed": n_bad, "total": len(rows),
1560
+ "context_lost": lost}
1561
+ hints = _fuzz_hints(rows)
1562
+ if hints:
1563
+ rep["hints"] = hints
1564
+ if n_bad == 0:
1565
+ rep["verdict"] = "pass: %d/%d cases ok" % (len(rows) - n_bad,
1566
+ len(rows))
1567
+ else:
1568
+ worst_case = next(c for c in rows
1569
+ if c["status"] not in ("ok", "skipped_vram",
1570
+ "skipped_empty"))
1571
+ rep["verdict"] = "fail: %d/%d cases (%s at %s %s)" % (
1572
+ n_bad, len(rows), worst_case["status"], worst_case["shape"],
1573
+ worst_case["variant"])
1574
+ return rep
1575
 
1576
 
1577
  def sweep(factory, grid, iters=30, warmup=5, bytes=None, flops=None,
1578
  check=None, budget_s=None, race=False):
1579
+ """Launch-configuration search, fastest first, with a correctness gate per row and no silently dropped points."""
 
 
 
 
 
 
 
 
1580
  keys = list(grid.keys())
1581
 
1582
  def expand(i, acc):
 
1641
  continue
1642
  rows.append(run_point(p, iters))
1643
  rows.sort(key=lambda r: (not r.get("check", True), r["median_ms"]))
1644
+ if rows:
1645
+ n_pruned = sum(1 for r in rows if r.get("status") == "pruned_race")
1646
+ n_skip = sum(1 for r in rows if r.get("status") == "not_run")
1647
+ best = rows[0]
1648
+ rows_verdict = "fastest %s at %.4g ms" % (
1649
+ {k: best[k] for k in keys}, best["median_ms"])
1650
+ if n_pruned:
1651
+ rows_verdict += "; %d pruned" % n_pruned
1652
+ if n_skip:
1653
+ rows_verdict += "; %d not run" % n_skip
1654
+ best["verdict"] = rows_verdict
1655
  return rows
1656
 
1657
 
1658
+ def judge(cand, ref, inputs=None, gen=None, bytes=None, flops=None,
1659
+ dtype=None, order_shuffle=None, make=None, quick_fuzz=True,
1660
+ device="cuda"):
1661
+ """One-call verdict: compare, a quick fuzz, and a roofline bench against the cached dossier."""
1662
+ if (gen is None) == (inputs is None):
1663
+ raise ValueError("pass exactly one of inputs (concrete tensors) "
1664
+ "or gen (seed -> args)")
1665
+ if inputs is not None and isinstance(inputs, torch.Tensor):
1666
+ inputs = (inputs,)
1667
+ args0 = tuple(inputs) if inputs is not None else gen(0)
1668
+ arity = len(args0)
1669
+ if dtype is None:
1670
+ dt0 = args0[0].dtype
1671
+ dtype = {torch.float16: "fp16", torch.bfloat16: "bf16"}.get(
1672
+ dt0, "f32")
1673
+ dossier = probe_device(device, quick=True, cached=True,
1674
+ tenancy_check=False)
1675
+ cmp_rep = compare(ref, cand, gen=gen, inputs=inputs, trials=4,
1676
+ order_shuffle=order_shuffle)
1677
+ fuzz_rep = None
1678
+ if quick_fuzz:
1679
+ fuzz_rep = fuzz_shapes(
1680
+ cand, ref, make=make, arity=arity,
1681
+ shapes=[(1, 17), (3, 63), (128, 128)],
1682
+ variants=("contig", "transposed", "offset_slice"),
1683
+ dtypes=(args0[0].dtype,))
1684
+ bench_rep = None
1685
+ if inputs is not None:
1686
+ bench_rep = bench(lambda: cand(*inputs), iters=32, warmup=6,
1687
+ bytes=bytes, flops=flops, dossier=dossier,
1688
+ dtype=dtype, device=device)
1689
+ passed = cmp_rep["pass"] and (fuzz_rep is None or
1690
+ fuzz_rep["failed"] == 0)
1691
+ parts = [cmp_rep["verdict"]]
1692
+ if fuzz_rep is not None:
1693
+ parts.append(fuzz_rep["verdict"])
1694
+ if bench_rep is not None:
1695
+ parts.append(bench_rep["verdict"])
1696
+ rep = {"pass": passed, "verdict" : " | ".join(parts),
1697
+ "compare": cmp_rep, "fuzz": fuzz_rep, "bench": bench_rep,
1698
+ "device": dossier["device"]["name"]}
1699
+ hints = list(cmp_rep.get("hints", []))
1700
+ if fuzz_rep is not None:
1701
+ hints += fuzz_rep.get("hints", [])
1702
+ if hints:
1703
+ rep["hints"] = hints
1704
+ return rep
1705
+
1706
+
1707
  def alloc_stamps(n, blocks=None, device="cuda"):
1708
+ """Device buffer for MK_STAMP (1-D) or MK_STAMP_BLOCK ([blocks, n]) stamps."""
 
 
1709
  if blocks is not None:
1710
  return torch.zeros(blocks, n, dtype=torch.int64, device=device)
1711
  return torch.zeros(n, dtype=torch.int64, device=device)
 
1715
 
1716
 
1717
  def pinned_dossiers():
1718
+ """Names of the release-pinned dossiers, one per certified architecture."""
 
1719
  return sorted(_PINNED.keys())
1720
 
1721
 
 
1734
 
1735
 
1736
  def dossier_drift(current, pinned=None, rel_tol=0.35, skip=_DRIFT_SKIP):
1737
+ """Per-leaf relative drift of a fresh dossier against the release pin for the same architecture."""
 
 
 
 
1738
  if pinned is None:
1739
  arch = current["device"]["sm_arch"]
1740
  matches = [k for k, v in _PINNED.items()
 
1757
  "rel": round(rel, 4)})
1758
  worst = max((e["rel"] for e in entries), default=0.0)
1759
  over = [e for e in entries if e["rel"] > rel_tol]
1760
+ rep = {"entries": entries, "worst_rel": worst, "over_tol": over,
1761
+ "rel_tol": rel_tol, "compared": len(entries),
1762
+ "pass": len(over) == 0}
1763
+ rep["verdict"] = ("pass: worst drift %.3f over %d entries" %
1764
+ (worst, len(entries))) if rep["pass"] else \
1765
+ ("fail: %d entries over %.2f, worst %s at %.3f" %
1766
+ (len(over), rel_tol, over[0]["path"], worst))
1767
+ return rep
1768
 
1769
 
1770
  def stamped_demo(n=1 << 22, fma_iters=4000, blocks=None, threads=256,
1771
  device="cuda"):
1772
+ """Run the shipped MK_STAMP_BLOCK example (load, compute, drained store) and decode its stamps."""
 
 
1773
  props = torch.cuda.get_device_properties(device)
1774
  if blocks is None:
1775
  blocks = props.multi_processor_count
 
1782
 
1783
 
1784
  def read_stamps(stamps, labels=None, clock_ghz=None):
1785
+ """Decode MK_STAMP deltas to ticks and microseconds; 2-D buffers yield min/median/max across blocks."""
 
 
 
 
1786
  if clock_ghz is None:
1787
  clock_ghz = measure_clock(stamps.device)
1788
 
 
1818
  return {"phases": phases, "clock_ghz": round(clock_ghz, 3),
1819
  "blocks_reporting": int(live.sum().item()),
1820
  "blocks_total": int(live.numel())}
1821
+
1822
+
1823
+ _SCHEMAS = {
1824
+ "probe_device": {
1825
+ "device": "name, sm_arch, sms, l2_bytes, vram_bytes, torch, "
1826
+ "cuda_runtime, ecc, driver, power_limit_w",
1827
+ "clock": "max/min/final GHz under sustained FMA load plus sag_pct",
1828
+ "dram_bw": "GB/s: triad_4B/8B/16B, read_only, write_only, "
1829
+ "random_gather_16B",
1830
+ "l2_bw": "GB/s over an L2-resident working set",
1831
+ "latency": "cycles: smem, l1, l2, dram (min of 3 chases) plus "
1832
+ "ns_per_cycle",
1833
+ "fma_f32 / fma_f64": "TFLOP/s of the register-resident pipes",
1834
+ "fma_ilp": "TFLOP/s at 1/2/4 chains plus ratio_4_1; near 4 means "
1835
+ "latency-bound",
1836
+ "mma": "TFLOP/s per dtype; sparse carries executed and "
1837
+ "dense_equivalent; verified_k_lanes carries "
1838
+ "executed_k_per_iter, vs_nominal, calibration_linear; "
1839
+ "fp8_accum carries the accumulate-precision stall",
1840
+ "mma_fed": "TFLOP/s of smem-staged fp16 tiles (staging exposed)",
1841
+ "smem_bw": "GB/s aggregate, conflict-light pattern",
1842
+ "smem_conflict_penalty": "time ratio vs stride 1 per lane stride",
1843
+ "transfer_bw": "GB/s: h2d/d2h pinned and pageable, d2d",
1844
+ "atomics_f32": "Gop/s curve over distinct addresses plus "
1845
+ "shared_same_addr",
1846
+ "launch_overhead": "us: torch_queued, torch_round_trip, "
1847
+ "raw_queued, raw_round_trip",
1848
+ "grid_barrier": "us per grid.sync or 'unsupported'",
1849
+ "occupancy_blocks_per_sm": "API table by (threads, dyn smem)",
1850
+ "occupancy_knees_fma_tflops": "timed dependent-chain table",
1851
+ },
1852
+ "bench": {
1853
+ "median_ms": "median over accepted samples",
1854
+ "sustained_ms": "median over all samples",
1855
+ "median_ci95_ms / p05_ms / p95_ms / min_ms / mean_ms":
1856
+ "distribution of accepted samples",
1857
+ "samples / rejected_throttled / rejection_basis":
1858
+ "counts and whether rejection used sm_clock or "
1859
+ "bandwidth_consistency",
1860
+ "no_clean_window": "present when every window ran throttled; the "
1861
+ "median is then a throttled-regime number",
1862
+ "clock_ghz": "best and worst SM clock probes around the run",
1863
+ "achieved_gbs / achieved_tflops / arithmetic_intensity":
1864
+ "rates from declared traffic",
1865
+ "roofline": "bound, ridge_flops_per_byte, pct_of_roof, peak_dtype",
1866
+ "graph_captured / graph_fallback_reason": "CUDA-graph state",
1867
+ "alloc_growth_bytes_per_call": "present when fn leaks allocations",
1868
+ "host_overhead_suspected": "present when fn syncs or does host "
1869
+ "work inside; ops under ~50 us are "
1870
+ "below the detection floor",
1871
+ "verdict": "one-sentence summary",
1872
+ },
1873
+ "compare": {
1874
+ "pass": "final verdict incl. the one-sided bias gate",
1875
+ "verdict / criterion / hints": "summary, acceptance rule, and "
1876
+ "failure-pattern suggestions",
1877
+ "worst_trial": "max_ulp, mean_ulp, first_divergent_index(+_nd), "
1878
+ "NaN/Inf census, order_band_ulp, band_score, "
1879
+ "frac_above_band, bias_one_sided_frac, "
1880
+ "worst_elements (index, index_nd, ref, cand, ulp)",
1881
+ "max_abs / max_rel / ulp_hist_log2": "aggregate evidence",
1882
+ "floor": "the banded route cannot reject errors under "
1883
+ "band_margin*(band+2) ULP at order-stable elements",
1884
+ },
1885
+ "fuzz_shapes": {
1886
+ "cases": "per-case shape, variant, dtype, status, repro recipe",
1887
+ "status values": "ok | mismatch | exception | context_killed | "
1888
+ "context_lost | crashed_isolated | skipped_vram "
1889
+ "| skipped_empty",
1890
+ "failed / total / context_lost / verdict / hints": "aggregates",
1891
+ },
1892
+ "sweep": {
1893
+ "rows": "grid point plus median_ms, rejected_throttled, check?, "
1894
+ "achieved rates, status in pruned_race | not_run",
1895
+ "verdict": "on the fastest row",
1896
+ },
1897
+ "dossier_drift": {
1898
+ "entries": "path, current, pinned, rel per intersecting leaf",
1899
+ "worst_rel / over_tol / compared / pass / verdict": "aggregates",
1900
+ },
1901
+ "judge": {
1902
+ "pass / verdict / hints": "composite over compare, fuzz, bench",
1903
+ "compare / fuzz / bench": "the full sub-reports",
1904
+ },
1905
+ "read_stamps": {
1906
+ "phases": "ticks and us per phase; 2-D input adds min/median/max "
1907
+ "across blocks",
1908
+ "note": "stamps record issue; DRAIN variants include the store "
1909
+ "drain to the coherence point",
1910
+ },
1911
+ }
1912
+
1913
+
1914
+ def schema(name=None):
1915
+ """Field layout of every report, so an agent can look up what a number means."""
1916
+ if name is None:
1917
+ return _SCHEMAS
1918
+ if name not in _SCHEMAS:
1919
+ raise KeyError(f"no schema for {name!r}; have "
1920
+ f"{sorted(_SCHEMAS)}")
1921
+ return _SCHEMAS[name]
build/{torch212-cxx11-cu132-x86_64-linux/_metakernel_cuda_b62a1ce.abi3.so → torch212-cxx11-cu130-x86_64-linux/_metakernel_cuda_0b9de42.abi3.so} RENAMED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:a0c457291c297cb5d91dffc19192ab490dbfd9cbb78cc0381b7dfc343f7624a1
3
- size 1192744
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:43c0a08aedf84b5774061ee641a2244f265695210389af8a04402849511d99c2
3
+ size 1426264
build/torch212-cxx11-cu130-x86_64-linux/_ops.py CHANGED
@@ -1,9 +1,9 @@
1
  import torch
2
- from . import _metakernel_cuda_b62a1ce
3
- ops = torch.ops._metakernel_cuda_b62a1ce
4
 
5
  def add_op_namespace_prefix(op_name: str):
6
  """
7
  Prefix op by namespace.
8
  """
9
- return f"_metakernel_cuda_b62a1ce::{op_name}"
 
1
  import torch
2
+ from . import _metakernel_cuda_0b9de42
3
+ ops = torch.ops._metakernel_cuda_0b9de42
4
 
5
  def add_op_namespace_prefix(op_name: str):
6
  """
7
  Prefix op by namespace.
8
  """
9
+ return f"_metakernel_cuda_0b9de42::{op_name}"
build/torch212-cxx11-cu130-x86_64-linux/_pinned.py CHANGED
@@ -1,8 +1,4 @@
1
- """Release-pinned device dossiers, one per certified architecture.
2
-
3
- Generated by dev/pin_dossier.py from full probe_device() runs on idle,
4
- single-tenant cards; consumed by metakernel.dossier_drift.
5
- """
6
 
7
  PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
8
  'confidence': 'high',
@@ -295,7 +291,7 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
295
  'd2h_pinned': 10.8,
296
  'h2d_pageable': 10.3,
297
  'h2d_pinned': 11.8}}},
298
- 'sm89_nvidiartx6000adagenerati': {'atomics_f32': {'clock_ghz': 2.706,
299
  'confidence': 'high',
300
  'method': 'atomicAdd '
301
  'hammer; curve '
@@ -308,21 +304,21 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
308
  'contention) to '
309
  'one per thread',
310
  'units': 'Gop/s',
311
- 'value': {'curve_slots': {'1': 0.72,
312
- '145408': 483.54,
313
- '4096': 209.84,
314
- '512': 55.6,
315
- '64': 13.82,
316
- '8': 5.77},
317
- 'shared_same_addr': 12.88}},
318
  'clock': {'confidence': 'high',
319
- 'final_ghz': 2.699,
320
- 'max_ghz': 2.706,
321
  'method': '24 back-to-back FMA '
322
  'spin windows of 30M '
323
  'ticks; wall/ticks '
324
  'per window',
325
- 'min_ghz': 2.501,
326
  'sag_pct': 0.3,
327
  'units': 'GHz'},
328
  'device': {'cuda_runtime': '12.6',
@@ -336,7 +332,7 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
336
  'sms': 142,
337
  'torch': '2.10.0+cu126',
338
  'vram_bytes': 48305274880},
339
- 'dram_bw': {'clock_ghz': 2.706,
340
  'confidence': 'high',
341
  'method': 'streaming triad '
342
  'c=a+s*b per access '
@@ -356,26 +352,41 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
356
  'line-efficiency '
357
  'claim',
358
  'units': 'GB/s',
359
- 'value': {'random_gather_16B': 218.5,
360
- 'read_only': 771.0,
361
- 'triad_16B': 663.1,
362
- 'triad_4B': 638.3,
363
- 'triad_8B': 705.3,
364
  'write_only': 766.5}},
365
- 'fma_f32': {'clock_ghz': 2.706,
366
  'confidence': 'high',
367
  'method': 'register-resident '
368
  'FMA chains',
369
  'units': 'TFLOP/s',
370
- 'value': 90.85},
371
- 'fma_f64': {'clock_ghz': 2.706,
372
  'confidence': 'high',
373
  'method': 'register-resident '
374
  'double FMA chains '
375
  '(the DFMA pipe)',
376
  'units': 'TFLOP/s',
377
- 'value': 1.282},
378
- 'grid_barrier': {'clock_ghz': 2.706,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
379
  'confidence': 'high',
380
  'method': 'cooperative '
381
  'kernel, 142 '
@@ -383,8 +394,8 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
383
  'threads',
384
  'units': 'us per '
385
  'grid.sync',
386
- 'value': 0.54},
387
- 'l2_bw': {'clock_ghz': 2.706,
388
  'confidence': 'medium',
389
  'method': 'in-kernel repeated '
390
  'triad passes over 72 '
@@ -394,14 +405,15 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
394
  'from HBM; amortized '
395
  'over 64 passes',
396
  'units': 'GB/s',
397
- 'value': 9325.3},
398
- 'latency': {'clock_ghz': 2.687,
399
  'confidence': 'high',
400
  'method': 'Sattolo-cycle '
401
- 'pointer chase; 16 '
402
- 'KB ring for L1, 2 '
403
- 'MB TLB-resident '
404
- 'ring for L2, '
 
405
  'self-evicting '
406
  'chain (2x L2 in '
407
  'touched lines) for '
@@ -413,40 +425,58 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
413
  'footprints',
414
  'units': 'cycles (single-lane '
415
  'dependent loads)',
416
- 'value': {'dram_cycles': 649.4,
417
  'l1_cycles': 45.8,
418
- 'l2_cycles': 291.5,
419
  'ns_per_cycle': 0.372,
420
  'smem_cycles': 30.0}},
421
- 'launch_overhead': {'clock_ghz': 2.706,
422
- 'confidence': 'high',
423
  'method': 'empty-kernel '
424
- 'trains, '
425
- 'queued vs '
426
- 'launch+sync',
427
- 'note': 'host-dependent: '
428
- 'includes '
429
- 'torch op '
430
- 'dispatch',
 
 
 
 
 
 
 
 
 
 
 
 
431
  'units': 'us',
432
- 'value': {'queued_us': 5.55,
433
- 'round_trip_us': 14.1}},
434
- 'mma': {'clock_ghz': 2.706,
 
 
435
  'confidence': 'medium',
 
 
 
 
436
  'method': 'register-fragment '
437
  'mma_sync loops (wmma; '
438
  'fp8 and 2:4-sparse '
439
  'fp16 via inline PTX '
440
- 'm16n8k32; sparse '
441
- 'counted '
442
- 'dense-equivalent, the '
443
- 'vendor convention); '
444
- 'flops derive from the '
445
- 'mean accumulator '
446
- 'read-back with every '
447
- 'fragment element '
448
- 'observed, never the '
449
- 'declared loop shape',
450
  'note': 'dense-tile rate without '
451
  'memory traffic; real '
452
  'kernels see this only '
@@ -454,25 +484,54 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
454
  'behind it',
455
  'units': 'TFLOP/s (TOP/s for '
456
  'int8)',
457
- 'value': {'bf16': 365.5,
458
- 'fp16': 375.9,
459
- 'fp16_sparse_2_4': 713.4,
460
- 'fp8_e4m3': 193.7,
461
- 'int8': 770.4,
462
- 'tf32': 177.1},
463
- 'verified_k_lanes': {'bf16': {'executed_k_per_iter': 64.0,
 
 
 
464
  'vs_nominal': 1.0},
465
- 'fp16': {'executed_k_per_iter': 64.0,
 
466
  'vs_nominal': 1.0},
467
- 'fp16_sparse_2_4': {'executed_k_per_iter': 128.0,
 
468
  'vs_nominal': 1.0},
469
- 'fp8_e4m3': {'executed_k_per_iter': 64.0,
470
- 'vs_nominal': 0.25},
471
- 'int8': {'executed_k_per_iter': 64.0,
 
 
 
 
 
472
  'vs_nominal': 1.0},
473
- 'tf32': {'executed_k_per_iter': 32.0,
 
474
  'vs_nominal': 1.0}}},
475
- 'occupancy_blocks_per_sm': {'clock_ghz': 2.706,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
476
  'confidence': 'high',
477
  'method': 'occupancy '
478
  'API '
@@ -501,7 +560,7 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
501
  '16KB': 3,
502
  '32KB': 3,
503
  '48KB': 2}}},
504
- 'occupancy_knees_fma_tflops': {'clock_ghz': 2.706,
505
  'confidence': 'high',
506
  'method': 'a '
507
  'single '
@@ -521,17 +580,6 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
521
  'fixed '
522
  'total '
523
  'work; '
524
- 'throughput '
525
- 'is '
526
- 'proportional '
527
- 'to '
528
- 'resident '
529
- 'threads '
530
- 'until '
531
- 'the '
532
- 'pipe '
533
- 'saturates, '
534
- 'so '
535
  'the '
536
  'cliff '
537
  'marks '
@@ -549,15 +597,15 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
549
  'above',
550
  'units': 'TFLOP/s',
551
  'value': {'t1024': {'0KB': 75.5,
552
- '32KB': 73.66,
553
- '48KB': 74.95},
554
- 't128': {'0KB': 79.86,
555
  '32KB': 60.31,
556
- '48KB': 44.57},
557
- 't256': {'0KB': 81.69,
558
- '32KB': 79.48,
559
- '48KB': 71.47}}},
560
- 'smem_bw': {'clock_ghz': 2.706,
561
  'confidence': 'medium',
562
  'method': 'block-resident '
563
  'read+write '
@@ -566,12 +614,56 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
566
  'aggregated across '
567
  'SMs',
568
  'note': 'bank-conflict-light '
569
- 'access pattern; a '
570
- 'conflicted pattern '
571
- 'can be far lower',
 
572
  'units': 'GB/s',
573
- 'value': 21250.0},
574
- 'transfer_bw': {'clock_ghz': 2.706,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
575
  'confidence': 'high',
576
  'method': '256 MB torch '
577
  'copies, '
@@ -583,8 +675,8 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
583
  'direction of '
584
  'traffic',
585
  'units': 'GB/s',
586
- 'value': {'d2d': 330.7,
587
- 'd2h_pageable': 15.6,
588
- 'd2h_pinned': 26.2,
589
- 'h2d_pageable': 16.6,
590
  'h2d_pinned': 24.0}}}}
 
1
+ '''Release-pinned device dossiers, one per certified architecture; generated by dev/pin_dossier.py from idle single-tenant runs, consumed by dossier_drift.'''
 
 
 
 
2
 
3
  PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
4
  'confidence': 'high',
 
291
  'd2h_pinned': 10.8,
292
  'h2d_pageable': 10.3,
293
  'h2d_pinned': 11.8}}},
294
+ 'sm89_nvidiartx6000adagenerati': {'atomics_f32': {'clock_ghz': 2.705,
295
  'confidence': 'high',
296
  'method': 'atomicAdd '
297
  'hammer; curve '
 
304
  'contention) to '
305
  'one per thread',
306
  'units': 'Gop/s',
307
+ 'value': {'curve_slots': {'1': 0.71,
308
+ '145408': 413.99,
309
+ '4096': 184.9,
310
+ '512': 49.84,
311
+ '64': 13.25,
312
+ '8': 5.74},
313
+ 'shared_same_addr': 12.23}},
314
  'clock': {'confidence': 'high',
315
+ 'final_ghz': 2.698,
316
+ 'max_ghz': 2.705,
317
  'method': '24 back-to-back FMA '
318
  'spin windows of 30M '
319
  'ticks; wall/ticks '
320
  'per window',
321
+ 'min_ghz': 2.5,
322
  'sag_pct': 0.3,
323
  'units': 'GHz'},
324
  'device': {'cuda_runtime': '12.6',
 
332
  'sms': 142,
333
  'torch': '2.10.0+cu126',
334
  'vram_bytes': 48305274880},
335
+ 'dram_bw': {'clock_ghz': 2.705,
336
  'confidence': 'high',
337
  'method': 'streaming triad '
338
  'c=a+s*b per access '
 
352
  'line-efficiency '
353
  'claim',
354
  'units': 'GB/s',
355
+ 'value': {'random_gather_16B': 218.1,
356
+ 'read_only': 766.5,
357
+ 'triad_16B': 682.1,
358
+ 'triad_4B': 654.8,
359
+ 'triad_8B': 670.9,
360
  'write_only': 766.5}},
361
+ 'fma_f32': {'clock_ghz': 2.705,
362
  'confidence': 'high',
363
  'method': 'register-resident '
364
  'FMA chains',
365
  'units': 'TFLOP/s',
366
+ 'value': 86.58},
367
+ 'fma_f64': {'clock_ghz': 2.705,
368
  'confidence': 'high',
369
  'method': 'register-resident '
370
  'double FMA chains '
371
  '(the DFMA pipe)',
372
  'units': 'TFLOP/s',
373
+ 'value': 1.257},
374
+ 'fma_ilp': {'clock_ghz': 2.705,
375
+ 'confidence': 'high',
376
+ 'method': '1/2/4 independent '
377
+ 'dependent FMA '
378
+ 'chains per thread '
379
+ 'at one block of 64 '
380
+ 'threads per SM; a '
381
+ 'ratio near 4 means '
382
+ 'latency-bound at '
383
+ 'that occupancy',
384
+ 'units': 'TFLOP/s',
385
+ 'value': {'ilp1': 11.75,
386
+ 'ilp2': 23.37,
387
+ 'ilp4': 43.03,
388
+ 'ratio_4_1': 3.66}},
389
+ 'grid_barrier': {'clock_ghz': 2.705,
390
  'confidence': 'high',
391
  'method': 'cooperative '
392
  'kernel, 142 '
 
394
  'threads',
395
  'units': 'us per '
396
  'grid.sync',
397
+ 'value': 0.55},
398
+ 'l2_bw': {'clock_ghz': 2.705,
399
  'confidence': 'medium',
400
  'method': 'in-kernel repeated '
401
  'triad passes over 72 '
 
405
  'from HBM; amortized '
406
  'over 64 passes',
407
  'units': 'GB/s',
408
+ 'value': 9590.6},
409
+ 'latency': {'clock_ghz': 2.691,
410
  'confidence': 'high',
411
  'method': 'Sattolo-cycle '
412
+ 'pointer chase, min '
413
+ 'of 3 runs; 16 KB '
414
+ 'ring for L1, 2 MB '
415
+ 'TLB-resident ring '
416
+ 'for L2, '
417
  'self-evicting '
418
  'chain (2x L2 in '
419
  'touched lines) for '
 
425
  'footprints',
426
  'units': 'cycles (single-lane '
427
  'dependent loads)',
428
+ 'value': {'dram_cycles': 656.9,
429
  'l1_cycles': 45.8,
430
+ 'l2_cycles': 292.0,
431
  'ns_per_cycle': 0.372,
432
  'smem_cycles': 30.0}},
433
+ 'launch_overhead': {'clock_ghz': 2.705,
434
+ 'confidence': 'medium',
435
  'method': 'empty-kernel '
436
+ 'trains; '
437
+ 'torch path '
438
+ 'per-launch '
439
+ 'through op '
440
+ 'dispatch, '
441
+ 'raw path '
442
+ 'looped '
443
+ 'inside one '
444
+ 'binding '
445
+ 'call',
446
+ 'note': 'host-state-dependent; '
447
+ 'the '
448
+ 'torch-minus-raw '
449
+ 'gap is the '
450
+ 'dispatch '
451
+ 'cost a '
452
+ 'torch-launched '
453
+ 'candidate '
454
+ 'pays',
455
  'units': 'us',
456
+ 'value': {'raw_queued_us': 6.75,
457
+ 'raw_round_trip_us': 13.87,
458
+ 'torch_queued_us': 7.09,
459
+ 'torch_round_trip_us': 18.53}},
460
+ 'mma': {'clock_ghz': 2.705,
461
  'confidence': 'medium',
462
+ 'fp8_accum': {'fp8_e4m3': {'effective_accum_mantissa_bits': 8,
463
+ 'stall_element_value': 16384.0},
464
+ 'fp8_e5m2': {'effective_accum_mantissa_bits': 8,
465
+ 'stall_element_value': 16384.0}},
466
  'method': 'register-fragment '
467
  'mma_sync loops (wmma; '
468
  'fp8 and 2:4-sparse '
469
  'fp16 via inline PTX '
470
+ 'm16n8k32); flops '
471
+ 'derive from a '
472
+ 'sub-saturation '
473
+ 'accumulator read-back '
474
+ 'with every fragment '
475
+ 'element observed, '
476
+ 'never the declared '
477
+ 'loop shape; sparse '
478
+ 'reports executed and '
479
+ 'dense-equivalent flops',
480
  'note': 'dense-tile rate without '
481
  'memory traffic; real '
482
  'kernels see this only '
 
484
  'behind it',
485
  'units': 'TFLOP/s (TOP/s for '
486
  'int8)',
487
+ 'value': {'bf16': 369.6,
488
+ 'fp16': 365.3,
489
+ 'fp16_sparse_2_4': {'dense_equivalent': 711.1,
490
+ 'executed': 355.6},
491
+ 'fp8_e4m3': 772.3,
492
+ 'fp8_e5m2': 771.2,
493
+ 'int8': 750.0,
494
+ 'tf32': 178.3},
495
+ 'verified_k_lanes': {'bf16': {'calibration_linear': True,
496
+ 'executed_k_per_iter': 64.0,
497
  'vs_nominal': 1.0},
498
+ 'fp16': {'calibration_linear': True,
499
+ 'executed_k_per_iter': 64.0,
500
  'vs_nominal': 1.0},
501
+ 'fp16_sparse_2_4': {'calibration_linear': True,
502
+ 'executed_k_per_iter': 128.0,
503
  'vs_nominal': 1.0},
504
+ 'fp8_e4m3': {'calibration_linear': True,
505
+ 'executed_k_per_iter': 256.0,
506
+ 'vs_nominal': 1.0},
507
+ 'fp8_e5m2': {'calibration_linear': True,
508
+ 'executed_k_per_iter': 256.0,
509
+ 'vs_nominal': 1.0},
510
+ 'int8': {'calibration_linear': True,
511
+ 'executed_k_per_iter': 64.0,
512
  'vs_nominal': 1.0},
513
+ 'tf32': {'calibration_linear': True,
514
+ 'executed_k_per_iter': 32.0,
515
  'vs_nominal': 1.0}}},
516
+ 'mma_fed': {'clock_ghz': 2.705,
517
+ 'confidence': 'high',
518
+ 'method': 'm16n16k16 fp16 '
519
+ 'tiles fed from an '
520
+ 'L2-resident buffer '
521
+ 'through cp.async '
522
+ 'double-buffered '
523
+ 'shared memory; '
524
+ 'accumulator-verified',
525
+ 'note': 'the achieved '
526
+ 'counterpart to the '
527
+ 'register peak: '
528
+ 'staging exposed, one '
529
+ 'dependent '
530
+ 'accumulator chain '
531
+ 'per warp',
532
+ 'units': 'TFLOP/s',
533
+ 'value': 85.2},
534
+ 'occupancy_blocks_per_sm': {'clock_ghz': 2.705,
535
  'confidence': 'high',
536
  'method': 'occupancy '
537
  'API '
 
560
  '16KB': 3,
561
  '32KB': 3,
562
  '48KB': 2}}},
563
+ 'occupancy_knees_fma_tflops': {'clock_ghz': 2.705,
564
  'confidence': 'high',
565
  'method': 'a '
566
  'single '
 
580
  'fixed '
581
  'total '
582
  'work; '
 
 
 
 
 
 
 
 
 
 
 
583
  'the '
584
  'cliff '
585
  'marks '
 
597
  'above',
598
  'units': 'TFLOP/s',
599
  'value': {'t1024': {'0KB': 75.5,
600
+ '32KB': 73.71,
601
+ '48KB': 72.69},
602
+ 't128': {'0KB': 79.72,
603
  '32KB': 60.31,
604
+ '48KB': 45.81},
605
+ 't256': {'0KB': 78.79,
606
+ '32KB': 78.85,
607
+ '48KB': 72.97}}},
608
+ 'smem_bw': {'clock_ghz': 2.705,
609
  'confidence': 'medium',
610
  'method': 'block-resident '
611
  'read+write '
 
614
  'aggregated across '
615
  'SMs',
616
  'note': 'bank-conflict-light '
617
+ 'access pattern; the '
618
+ 'stride sweep below '
619
+ 'prices the '
620
+ 'conflicted case',
621
  'units': 'GB/s',
622
+ 'value': 22244.0},
623
+ 'smem_conflict_penalty': {'clock_ghz': 2.705,
624
+ 'confidence': 'high',
625
+ 'method': 'dependent '
626
+ 'read-FMA-write '
627
+ 'per '
628
+ 'thread '
629
+ 'at '
630
+ 'warp-lane '
631
+ 'bank '
632
+ 'distance '
633
+ '`stride`; '
634
+ 'stride '
635
+ '32 '
636
+ 'puts '
637
+ 'every '
638
+ 'lane '
639
+ 'in '
640
+ 'one '
641
+ 'bank',
642
+ 'note': 'the '
643
+ 'direct '
644
+ 'conflict '
645
+ 'measurement '
646
+ 'that '
647
+ 'disambiguates '
648
+ 'a '
649
+ 'bank-conflict '
650
+ 'signature '
651
+ 'from '
652
+ 'an ILP '
653
+ 'stall',
654
+ 'units': 'x '
655
+ '(time '
656
+ 'ratio '
657
+ 'vs '
658
+ 'stride '
659
+ '1)',
660
+ 'value': {'stride_1': 1.0,
661
+ 'stride_16': 7.13,
662
+ 'stride_2': 1.07,
663
+ 'stride_32': 14.08,
664
+ 'stride_4': 2.04,
665
+ 'stride_8': 3.91}},
666
+ 'transfer_bw': {'clock_ghz': 2.705,
667
  'confidence': 'high',
668
  'method': '256 MB torch '
669
  'copies, '
 
675
  'direction of '
676
  'traffic',
677
  'units': 'GB/s',
678
+ 'value': {'d2d': 324.0,
679
+ 'd2h_pageable': 7.8,
680
+ 'd2h_pinned': 26.3,
681
+ 'h2d_pageable': 7.6,
682
  'h2d_pinned': 24.0}}}}
build/torch212-cxx11-cu130-x86_64-linux/metadata.json CHANGED
@@ -1,9 +1,10 @@
1
  {
2
  "name": "metakernel",
3
- "id": "_metakernel_cuda_b62a1ce",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "python-depends": [],
 
7
  "backend": {
8
  "type": "cuda",
9
  "archs": [
@@ -18,21 +19,20 @@
18
  "digest": {
19
  "algorithm": "sha256",
20
  "files": {
21
- "__init__.py": "e/P3LIoxZqzuhgyc26YJRkdLj4OilextyRiU8YbAvIA=",
22
- "_metakernel_cuda_b62a1ce.abi3.so": "1Y6NPGyDQe/N1E5YvWt+4IjBUPeIfmXnS4GZr+1KUto=",
23
- "_ops.py": "QokZXDYGh+rLEKO/wNDRcMkJPtPWK568RiTlCHopsHM=",
24
- "_pinned.py": "4i1FvV3kWanEdFLd9+rGOjHcHDAyOjKnjLt5LUzgASs=",
25
- "metakernel/__init__.py": "DFYPlrhXwYjEqCl/8n0SmWGZV8NFml5DPhMjKfv98GY="
26
  }
27
  },
28
  "provenance": {
29
  "kernel-builder": {
30
  "version": "0.17.0-dev0",
31
- "sha": "19aaa6421e674e9fecc352bbae6eab81d19a6bf4",
32
  "dirty": false
33
  },
34
  "kernel": {
35
- "sha": "b62a1cef64b6e3b2cabf774c82ea7ce61dcac6c0",
36
  "dirty": false
37
  }
38
  }
 
1
  {
2
  "name": "metakernel",
3
+ "id": "_metakernel_cuda_0b9de42",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "python-depends": [],
7
+ "kernel-depends": [],
8
  "backend": {
9
  "type": "cuda",
10
  "archs": [
 
19
  "digest": {
20
  "algorithm": "sha256",
21
  "files": {
22
+ "__init__.py": "lDQ+fJ0JnI3sQGR3UMnDovBbG4ciYBsaFjmgYdKDTKI=",
23
+ "_metakernel_cuda_0b9de42.abi3.so": "Q8Cgiu34S1d0Bh7mQaIkTyZWlSEDia+KBEAoSVEdmcI=",
24
+ "_ops.py": "x88Bkm1nxRli0nIu2u4ztaJopS1IAZWdN9LZPfspJdo=",
25
+ "_pinned.py": "UyzMniAjfVMIrIbbrSq92KKo5dX5l/GwV3pMVA+eNtM="
 
26
  }
27
  },
28
  "provenance": {
29
  "kernel-builder": {
30
  "version": "0.17.0-dev0",
31
+ "sha": "2c40e10d3da1392245ee81bd05b55fb3103b2b34",
32
  "dirty": false
33
  },
34
  "kernel": {
35
+ "sha": "0b9de42fee8a1e52593f2f1f58460c0acc93d7e8",
36
  "dirty": false
37
  }
38
  }
build/torch212-cxx11-cu130-x86_64-linux/metakernel/__init__.py DELETED
@@ -1,26 +0,0 @@
1
- import ctypes
2
- import importlib.util
3
- import sys
4
- from pathlib import Path
5
- from types import ModuleType
6
-
7
-
8
- def _import_from_path(file_path: Path) -> ModuleType:
9
- # We cannot use the module name as-is, after adding it to `sys.modules`,
10
- # it would also be used for other imports. So, we make a module name that
11
- # depends on the path for it to be unique using the hex-encoded hash of
12
- # the path.
13
- path_hash = "{:x}".format(ctypes.c_size_t(hash(file_path.absolute())).value)
14
- module_name = path_hash
15
- spec = importlib.util.spec_from_file_location(module_name, file_path)
16
- if spec is None:
17
- raise ImportError(f"Cannot load spec for {module_name} from {file_path}")
18
- module = importlib.util.module_from_spec(spec)
19
- if module is None:
20
- raise ImportError(f"Cannot load module {module_name} from spec")
21
- sys.modules[module_name] = module
22
- spec.loader.exec_module(module) # type: ignore
23
- return module
24
-
25
-
26
- globals().update(vars(_import_from_path(Path(__file__).parent.parent / "__init__.py")))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
build/torch212-cxx11-cu132-x86_64-linux/__init__.py CHANGED
@@ -1,20 +1,4 @@
1
- """Measuring instrument for the kernel-design loop: timing, adversarial
2
- correctness comparison, and measured device facts, as loadable ops.
3
-
4
- from kernels import get_kernel
5
- mk = get_kernel("phanerozoic/metakernel", version=1,
6
- trust_remote_code=True)
7
-
8
- dossier = mk.probe_device() # measured facts
9
- report = mk.bench(lambda: op(x), iters=200) # throttle-rejected
10
- verdict = mk.compare(ref, cand, gen) # ULP evidence
11
- table = mk.sweep(factory, {"BLOCK": [64, 128]}) # config search
12
-
13
- Every op returns plain dicts and tensors. Every dossier entry is
14
- measured by a dedicated probe kernel, carries its SM clock, and labels
15
- throttled samples; no hardware counters, so entries carry a confidence
16
- field.
17
- """
18
  import math
19
  import os
20
  import time
@@ -24,9 +8,7 @@ import torch
24
  try:
25
  from ._ops import ops
26
  except ImportError: # pragma: no cover - spawned child in a source tree
27
- # Subprocess-isolation children re-import this package; in a source
28
- # checkout there is no generated _ops module, so rebuild the op table
29
- # from the (cached) JIT extension that load_local advertised.
30
  _root = os.environ.get("METAKERNEL_DEV_ROOT")
31
  if not _root:
32
  raise
@@ -48,24 +30,25 @@ try:
48
  except ImportError: # pragma: no cover - fresh tree before first pin
49
  _PINNED = {}
50
 
51
- __all__ = ["probe_device", "bench", "compare", "order_shuffles",
52
- "fuzz_shapes", "sweep", "alloc_stamps", "read_stamps",
53
- "stamped_demo", "measure_clock", "ulp_diff", "dossier_drift",
54
- "pinned_dossiers", "ops"]
55
-
56
- # name -> (kind, M*N of the C tile, nominal K, work multiplier, nominal
57
- # K-lanes per unrolled iteration). The accumulator read-back yields
58
- # executed K-lanes per op (column sums of B equal K), so flops =
59
- # 2 x M*N x gain x multiplier with nothing declared trusted; multiplier
60
- # 2.0 is the sparse dense-equivalent convention. Lanes/iter: wmma K x 4
61
- # chained ops; PTX kinds x 8 over two chains.
62
  _MMA_KINDS = {
63
- "fp16": (0, 16 * 16, 16, 1.0, 64.0),
64
- "bf16": (1, 16 * 16, 16, 1.0, 64.0),
65
- "tf32": (2, 16 * 16, 8, 1.0, 32.0),
66
- "int8": (3, 16 * 16, 16, 1.0, 64.0),
67
- "fp8_e4m3": (4, 16 * 8, 32, 1.0, 256.0),
68
- "fp16_sparse_2_4": (5, 16 * 8, 32, 2.0, 128.0), # dense-equivalent
 
69
  }
70
 
71
 
@@ -74,7 +57,7 @@ def _sync():
74
 
75
 
76
  def _events(fn, reps):
77
- """Time reps calls of fn with one event pair each; returns ms list."""
78
  starts = [torch.cuda.Event(enable_timing=True) for _ in range(reps)]
79
  stops = [torch.cuda.Event(enable_timing=True) for _ in range(reps)]
80
  for s, e in zip(starts, stops):
@@ -86,8 +69,7 @@ def _events(fn, reps):
86
 
87
 
88
  def measure_clock(device="cuda", ticks=4_000_000, blocks=None):
89
- """Effective SM clock in GHz under an FMA spin of `ticks` clock64 ticks.
90
- Wall time over elapsed ticks is the frequency the ticks ran at."""
91
  props = torch.cuda.get_device_properties(device)
92
  if blocks is None:
93
  blocks = props.multi_processor_count
@@ -120,8 +102,7 @@ def _entry(value, units, method, clock_ghz, confidence, note=""):
120
 
121
 
122
  def _sattolo_ring(n, device, generator=None):
123
- """A random single-cycle permutation as an int32 next-pointer table, so
124
- a pointer chase visits every slot before repeating."""
125
  perm = torch.randperm(n, generator=generator)
126
  ring = torch.empty(n, dtype=torch.int32)
127
  ring[perm] = torch.roll(perm, -1).to(torch.int32)
@@ -129,8 +110,7 @@ def _sattolo_ring(n, device, generator=None):
129
 
130
 
131
  def _smi_settings(index):
132
- """ECC mode, driver, and power limit via nvidia-smi; None fields
133
- when the tool is unavailable."""
134
  import subprocess
135
  try:
136
  out = subprocess.run(
@@ -150,10 +130,19 @@ def _smi_settings(index):
150
  return {"ecc": None, "driver": None, "power_limit_w": None}
151
 
152
 
 
 
 
 
 
 
 
 
 
 
 
153
  def mma_tile_check(device="cuda", trials=8):
154
- """Random small-integer m16n16k16 tiles through the wmma fragment
155
- path, compared exactly to the reference GEMM; pins M, N, K, and
156
- operand mapping (integer operands make fp32 accumulation exact)."""
157
  worst = 0.0
158
  for t in range(trials):
159
  g = torch.Generator(device=device).manual_seed(1000 + t)
@@ -171,10 +160,83 @@ def mma_tile_check(device="cuda", trials=8):
171
  "trials": trials}
172
 
173
 
174
- def probe_device(device="cuda", quick=False):
175
- """Empirical device dossier. Each entry is measured by a dedicated
176
- probe kernel and carries units, method, the SM clock it ran under, and
177
- a confidence field."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
  props = torch.cuda.get_device_properties(device)
179
  sm = props.multi_processor_count
180
  l2 = props.L2_cache_size
@@ -187,8 +249,7 @@ def probe_device(device="cuda", quick=False):
187
  "vram_bytes": props.total_memory,
188
  "torch": torch.__version__,
189
  "cuda_runtime": torch.version.cuda,
190
- # instrument settings: readings depend on them, so the
191
- # dossier records them (None when nvidia-smi is unavailable)
192
  **_smi_settings(torch.device(device).index or 0),
193
  }
194
  }
@@ -213,9 +274,8 @@ def probe_device(device="cuda", quick=False):
213
  }
214
  clk = max(traj)
215
 
216
- # ---- device-memory bandwidth: triad per access width, plus read-only,
217
- # write-only, and random-gather
218
- n4 = 16 << 20 # 16M float4 = 256 MB per buffer
219
  if props.total_memory < 4 << 30:
220
  n4 = 4 << 20
221
  a = torch.rand(n4 * 4, device=device)
@@ -239,7 +299,6 @@ def probe_device(device="cuda", quick=False):
239
  dtype=torch.int32).to(device)
240
  ops.mk_gather(a, idx, c, 256, 0)
241
  ms = _median(_events(lambda: ops.mk_gather(a, idx, c, 256, 0), reps))
242
- # traffic accounting: idx reads + 16B random loads + streamed stores
243
  hbm["random_gather_16B"] = round(
244
  (idx.numel() * 4 + 2 * n4 * 16) / (ms * 1e-3) / 1e9, 1)
245
  d["dram_bw"] = _entry(hbm, "GB/s", "streaming triad c=a+s*b per access "
@@ -250,9 +309,9 @@ def probe_device(device="cuda", quick=False):
250
  "so its nominal number is the achievable rate for "
251
  "random access, not a line-efficiency claim")
252
 
253
- # ---- L2 bandwidth (working set 3 buffers inside L2)
254
  ws = max(l2 // 4, 1 << 20)
255
- nl2 = ws // 16 # float4 elems per buffer
256
  al = a[:nl2 * 4].contiguous()
257
  bl = b[:nl2 * 4].contiguous()
258
  cl = c[:nl2 * 4].contiguous()
@@ -267,62 +326,61 @@ def probe_device(device="cuda", quick=False):
267
  "medium", "first pass streams from HBM; amortized "
268
  f"over {passes} passes")
269
 
270
- # ---- latencies: pointer chase per level. The 2 MB L2 ring exceeds
271
- # L1, fits L2 everywhere, and stays TLB-resident after a warm walk;
272
- # the DRAM chain touches 2x L2 in lines, so it evicts itself.
273
  lat = {}
274
  idle_clk = measure_clock(device, ticks=2_000_000, blocks=1)
275
- ring_s = _sattolo_ring(8192, device)
276
- ops.mk_chase_shared(ring_s, 1 << 14, out64)
277
- _sync()
278
- hops = 1 << 16
279
- ops.mk_chase_shared(ring_s, hops, out64)
280
- _sync()
281
- lat["smem_cycles"] = round(int(out64[0].item()) / hops, 1)
282
 
283
- l1_ring_n = (16 << 10) // 4 # 16 KB: L1-resident after a warm walk
284
- ring_l1 = _sattolo_ring(l1_ring_n, device)
285
- ops.mk_chase_global(ring_l1, l1_ring_n, out64)
286
- _sync()
287
- hops_l1 = 1 << 15
288
- ops.mk_chase_global(ring_l1, hops_l1, out64)
289
- _sync()
290
- lat["l1_cycles"] = round(int(out64[0].item()) / hops_l1, 1)
291
-
292
- l2_ring_n = (2 << 20) // 4
293
- ring_l2 = _sattolo_ring(l2_ring_n, device)
294
- ops.mk_chase_global(ring_l2, l2_ring_n, out64) # full-cycle warm walk
295
- _sync()
296
- hops_l2 = 1 << 14
297
- ops.mk_chase_global(ring_l2, hops_l2, out64)
298
- _sync()
299
- lat["l2_cycles"] = round(int(out64[0].item()) / hops_l2, 1)
300
 
 
 
 
 
 
 
 
 
 
 
 
 
301
  hbm_bytes = min(max(4 * l2, 64 << 20), props.total_memory // 8)
302
  ring_h = _sattolo_ring(int(hbm_bytes) // 4, device)
303
  hops_h = max((2 * l2) // 128, 1 << 17)
304
- ops.mk_chase_global(ring_h, 1 << 14, out64) # fault pages / warm TLB
305
- _sync()
306
- ops.mk_chase_global(ring_h, hops_h, out64)
307
- _sync()
308
- lat["dram_cycles"] = round(int(out64[0].item()) / hops_h, 1)
309
  lat["ns_per_cycle"] = round(1.0 / idle_clk, 3)
310
  d["latency"] = _entry(lat, "cycles (single-lane dependent loads)",
311
- "Sattolo-cycle pointer chase; 16 KB ring for L1, "
312
- "2 MB TLB-resident ring for L2, self-evicting "
313
- "chain (2x L2 in touched lines) for HBM",
314
- idle_clk, "high",
315
  "smem includes index arithmetic; HBM includes "
316
  "residual TLB effects at large footprints")
317
  del ring_h, ring_l2, ring_l1, ring_s
318
 
319
- # ---- fp32 FMA and tensor-core throughput
320
  blocks = sm * 4
321
  iters = 20_000 if quick else 60_000
322
  ops.mk_fma_f32(blocks, 256, iters, sink)
323
  ms = _median(_events(lambda: ops.mk_fma_f32(blocks, 256, iters, sink),
324
  reps))
325
- flops = blocks * 256 * iters * 16 * 2 * 2 # 2 chains x 2 flops x unroll
326
  d["fma_f32"] = _entry(round(flops / (ms * 1e-3) / 1e12, 2), "TFLOP/s",
327
  "register-resident FMA chains", clk, "high")
328
  sink64 = torch.zeros(2, dtype=torch.float64, device=device)
@@ -334,43 +392,88 @@ def probe_device(device="cuda", quick=False):
334
  d["fma_f64"] = _entry(round(flops64 / (ms * 1e-3) / 1e12, 3), "TFLOP/s",
335
  "register-resident double FMA chains (the DFMA "
336
  "pipe)", clk, "high")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
337
  mma = {}
338
  mma_klanes = {}
339
- for name, (kind, mn, k_nom, mult, nom_lanes) in _MMA_KINDS.items():
 
340
  it = 30_000 if quick else 100_000
341
  r = ops.mk_mma(kind, blocks, 8, 512, sink)
342
  if r != 0:
343
  mma[name] = "unsupported"
344
  continue
345
  _sync()
346
- # verification pass: with unit operands the accumulator gain IS
347
- # the executed K-lane count (exact at these magnitudes)
348
- cal_iters = 512
349
- sink[1] = 0.0
350
- ops.mk_mma(kind, 1, 1, cal_iters, sink)
351
- _sync()
352
- gain_per_iter = float(sink[1].item()) / cal_iters
353
  mma_klanes[name] = {
354
- "executed_k_per_iter": round(gain_per_iter, 2),
355
- "vs_nominal": round(gain_per_iter / nom_lanes, 3),
 
356
  }
 
 
357
  ms = _median(_events(
358
  lambda k=kind, i=it: ops.mk_mma(k, blocks, 8, i, sink), reps))
359
- flops = blocks * 8 * it * 2.0 * mn * gain_per_iter * mult
360
- mma[name] = round(flops / (ms * 1e-3) / 1e12, 1)
 
 
 
 
361
  d["mma"] = _entry(mma, "TFLOP/s (TOP/s for int8)",
362
  "register-fragment mma_sync loops (wmma; fp8 and "
363
- "2:4-sparse fp16 via inline PTX m16n8k32; sparse "
364
- "counted dense-equivalent, the vendor convention); "
365
- "flops derive from the mean accumulator read-back "
366
  "with every fragment element observed, never the "
367
- "declared loop shape",
 
368
  clk, "medium",
369
  "dense-tile rate without memory traffic; real "
370
  "kernels see this only when staging hides behind it")
371
  d["mma"]["verified_k_lanes"] = mma_klanes
 
 
372
 
373
- # ---- shared-memory bandwidth
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
374
  it_sm = 30_000 if quick else 100_000
375
  ops.mk_smem_bw(blocks, 256, it_sm, sink)
376
  ms = _median(_events(lambda: ops.mk_smem_bw(blocks, 256, it_sm, sink),
@@ -380,8 +483,23 @@ def probe_device(device="cuda", quick=False):
380
  "block-resident read+write streaming (8 B per "
381
  "thread-iteration), aggregated across SMs", clk,
382
  "medium",
383
- "bank-conflict-light access pattern; a conflicted "
384
- "pattern can be far lower")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
385
 
386
  # ---- PCIe / host transfer bandwidth
387
  nbytes = 256 << 20
@@ -412,9 +530,7 @@ def probe_device(device="cuda", quick=False):
412
  "d2d counts one direction of traffic")
413
  del host_pin, host_pag, dev_t, dev_u
414
 
415
- # ---- atomic contention curve: throughput vs number of distinct
416
- # addresses the whole grid hammers (slots = 1 is full contention;
417
- # slots = threads is contention-free), plus the shared-memory point
418
  n_thr = blocks * 256
419
  buf = torch.zeros(n_thr, dtype=torch.float32, device=device)
420
  at = {"curve_slots": {}}
@@ -424,7 +540,7 @@ def probe_device(device="cuda", quick=False):
424
  buf.zero_()
425
  ops.mk_atomics(1, blocks, 256, it_at, buf, slots)
426
  ms = _median(_events(
427
- lambda s=slots: ops.mk_atomics(1, blocks, 256, it_at, buf, s),
428
  reps))
429
  at["curve_slots"][str(slots)] = round(
430
  n_thr * it_at / (ms * 1e-3) / 1e9, 2)
@@ -437,7 +553,7 @@ def probe_device(device="cuda", quick=False):
437
  "of distinct global addresses from 1 (full "
438
  "contention) to one per thread", clk, "high")
439
 
440
- # ---- launch overhead
441
  anchor = sink
442
  n_launch = 500 if quick else 2000
443
  _sync()
@@ -452,10 +568,19 @@ def probe_device(device="cuda", quick=False):
452
  ops.mk_empty(anchor)
453
  _sync()
454
  rt = (time.perf_counter() - t0) / n_rt * 1e6
 
 
455
  d["launch_overhead"] = _entry(
456
- {"queued_us": round(queued, 2), "round_trip_us": round(rt, 2)},
457
- "us", "empty-kernel trains, queued vs launch+sync", clk, "high",
458
- "host-dependent: includes torch op dispatch")
 
 
 
 
 
 
 
459
 
460
  # ---- grid barrier
461
  bar_iters = 500 if quick else 2000
@@ -471,9 +596,7 @@ def probe_device(device="cuda", quick=False):
471
  "unavailable or grid too large", clk,
472
  "high")
473
 
474
- # ---- occupancy table (API) plus timed knees: the same random gather
475
- # at every (threads, dyn_smem) point; when the forced blocks/SM stop
476
- # covering memory latency, achieved bandwidth falls off the cliff
477
  occ = {}
478
  for threads in (128, 256, 512, 1024):
479
  row = {}
@@ -494,8 +617,8 @@ def probe_device(device="cuda", quick=False):
494
  ops.mk_fma_dep(knee_blocks, threads, smem_kb * 1024,
495
  knee_iters, sink)
496
  ms = _median(_events(
497
- lambda t=threads, s=smem_kb: ops.mk_fma_dep(
498
- knee_blocks, t, s * 1024, knee_iters, sink), reps))
499
  fl = knee_blocks * threads * knee_iters * 16 * 2
500
  row[f"{smem_kb}KB"] = round(fl / (ms * 1e-3) / 1e12, 2)
501
  knees[f"t{threads}"] = row
@@ -504,27 +627,160 @@ def probe_device(device="cuda", quick=False):
504
  "at forced (threads, dyn smem) occupancies over fixed total work; "
505
  "the cliff marks where occupancy stops covering latency",
506
  clk, "high", "timing-derived counterpart to the API table above")
 
 
 
507
  return d
508
 
509
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
510
  def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
511
  dtype="fp16", clock_guard=0.97, clock_stride=8, graph=False,
512
- device="cuda"):
513
- """Median-of-N cudaEvent timing with throttle rejection.
514
-
515
- The SM clock is probed every `clock_stride` samples; samples in a
516
- window below `clock_guard` x the best observed clock are rejected
517
- and counted. Declared `bytes`/`flops` yield achieved GB/s / TFLOP/s;
518
- a `dossier` adds roofline placement. `graph=True` times CUDA-graph
519
- replays, stripping Python and torch dispatch (fn must be graph-safe:
520
- no sync, no allocation growth, static shapes; a failed capture falls
521
- back to eager with the reason reported). `clock_stride=1` probes
522
- around every sample, ~0.3 ms each."""
523
  for _ in range(warmup):
524
  fn()
525
  _sync()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
526
  graph_state = "off"
527
  graph_reason = ""
 
 
 
 
 
 
528
  if graph:
529
  pre_rng = torch.cuda.get_rng_state(device)
530
  try:
@@ -539,10 +795,7 @@ def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
539
  except Exception as e: # noqa: BLE001 - fall back, say why
540
  graph_state = "fallback_eager"
541
  graph_reason = f"{type(e).__name__}: {str(e)[:120]}"
542
- # drain the invalidated stream so eager timing can proceed.
543
- # On some torch versions a failed capture also wedges the
544
- # device RNG with no in-process recovery; timing uses no RNG,
545
- # but the caller's later randn may raise.
546
  rng_ok = True
547
  for _attempt in range(5):
548
  try:
@@ -572,25 +825,51 @@ def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
572
  samples.append((_events(fn, n), len(clocks) - 1))
573
  clocks.append(measure_clock(device, ticks=probe_ticks))
574
  best_clk = max(clocks)
575
- kept, rejected = [], 0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
576
  flat = []
577
- for group, ci in samples:
578
- window_ok = (clocks[ci] >= clock_guard * best_clk and
579
- clocks[min(ci + 1, len(clocks) - 1)] >=
580
- clock_guard * best_clk)
581
- for ms in group:
582
- flat.append(ms)
583
- if window_ok:
 
 
 
 
 
 
 
 
 
 
 
 
584
  kept.append(ms)
585
  else:
586
  rejected += 1
587
- if not kept:
 
588
  kept = flat
589
  med = _median(kept)
590
  ks = sorted(kept)
591
  n_k = len(ks)
592
- # order-statistic 95% CI on the median (normal approximation to the
593
- # binomial ranks); degenerate at tiny n, where it spans the sample
594
  half = 1.959964 * (n_k ** 0.5) / 2.0
595
  lo_i = max(0, int(n_k / 2 - half) - 1)
596
  hi_i = min(n_k - 1, int(n_k / 2 + half))
@@ -605,12 +884,25 @@ def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
605
  "mean_ms": round(sum(kept) / n_k, 4),
606
  "samples": len(flat),
607
  "rejected_throttled": rejected,
 
608
  "graph_captured": graph_state == "captured",
609
  "clock_ghz": {"best": round(best_clk, 3),
610
  "worst": round(min(clocks), 3)},
611
  }
 
 
 
 
 
 
 
612
  if graph_reason:
613
  rep["graph_fallback_reason"] = graph_reason
 
 
 
 
 
614
  if bytes is not None:
615
  rep["achieved_gbs"] = round(bytes / (med * 1e-3) / 1e9, 1)
616
  if flops is not None:
@@ -620,6 +912,8 @@ def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
620
  if dossier is not None and bytes and flops:
621
  bw = max(dossier["dram_bw"]["value"].values()) * 1e9
622
  mma_v = dossier["mma"]["value"].get(dtype)
 
 
623
  peak = (mma_v if isinstance(mma_v, (int, float))
624
  else dossier["fma_f32"]["value"]) * 1e12
625
  ai = flops / bytes
@@ -630,13 +924,12 @@ def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
630
  "pct_of_roof": round(100 * (flops / (med * 1e-3)) / roof, 1),
631
  "peak_dtype": dtype,
632
  }
 
633
  return rep
634
 
635
 
636
  def _to_ordered_int(t):
637
- """Map float bit patterns to integers whose difference is the ULP
638
- distance: positive floats keep their bits, negative floats mirror
639
- below zero, so the mapping is monotone in the float value."""
640
  if t.dtype == torch.float32:
641
  u = t.view(torch.int32).to(torch.int64) & 0xFFFFFFFF
642
  half = 1 << 31
@@ -652,11 +945,12 @@ def _to_ordered_int(t):
652
 
653
 
654
  def ulp_diff(a, b):
655
- """Elementwise ULP distance between same-dtype tensors. For floats,
656
- NaNs in the same position count as 0 and a one-sided NaN counts as
657
- 2**62; for integer and bool tensors the distance is the absolute
658
- numeric difference (an exact op must be exactly equal)."""
659
- assert a.dtype == b.dtype and a.shape == b.shape
 
660
  if not a.is_floating_point():
661
  return (a.to(torch.int64) - b.to(torch.int64)).abs()
662
  ia, ib = _to_ordered_int(a), _to_ordered_int(b)
@@ -673,9 +967,7 @@ class order_shuffles:
673
 
674
  @staticmethod
675
  def last_dim():
676
- """Permute the last dim of every tensor arg identically: the
677
- accumulation order of a last-dim reduction changes, the value
678
- does not."""
679
  def shuffle(args, seed):
680
  g = torch.Generator(device=args[0].device).manual_seed(seed)
681
  p = torch.randperm(args[0].shape[-1], device=args[0].device,
@@ -685,8 +977,7 @@ class order_shuffles:
685
 
686
  @staticmethod
687
  def matmul_k():
688
- """Permute K of (A [.., M, K], B [.., K, N]) consistently: the
689
- product is unchanged, the accumulation order is not."""
690
  def shuffle(args, seed):
691
  a_m, b_m = args[0], args[1]
692
  g = torch.Generator(device=a_m.device).manual_seed(seed)
@@ -700,37 +991,70 @@ def _as_outputs(x):
700
  return list(x) if isinstance(x, (tuple, list)) else [x]
701
 
702
 
703
- def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
704
- ref_variants=None, ulp_tol=None, atol=None, rtol=None,
705
- band_margin=4.0, zero_atol=None, worst_k=8, seed=0):
706
- """Adversarial correctness comparison with ULP evidence.
707
-
708
- `gen(seed) -> args`; `ref(*args)` and `cand(*args)` return a tensor
709
- or tuple (worst output governs). With `order_shuffle(args, seed)`
710
- reordering contractual-noise dimensions (see `order_shuffles`), each
711
- element is judged against its own measured POINTWISE reduction-order
712
- band: pass is max(ulp / (own_band + 2)) <= band_margin; without a
713
- band, max ULP <= ulp_tol (default 2); `atol`/`rtol` are an
714
- alternative acceptance route. `zero_atol`: elements with
715
- |ref| < zero_atol are judged by absolute difference (ULP explodes
716
- across the denormal range). The report carries the top-`worst_k`
717
- offenders (index, ref, cand, ULP). Order shuffles cover the
718
- reference's own topology; a candidate with a different legitimate
719
- topology (split-K) needs `ref_variants`, callables `f(args) -> out`
720
- whose outputs widen the pointwise band."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
721
  worst = {"max_ulp": -1}
722
  total_hist = {}
723
  per_trial = []
724
  max_abs_seen = 0.0
725
  max_rel_seen = 0.0
 
726
  for t in range(trials):
727
  args = gen(seed + t)
 
 
 
 
728
  r_outs = _as_outputs(ref(*args))
729
  c_outs = _as_outputs(cand(*args))
730
- assert len(r_outs) == len(c_outs), "output arity mismatch"
731
- # pointwise band: each element is judged against its own measured
732
- # order sensitivity (cancellation makes near-zero outputs
733
- # legitimately order-sensitive while neighbors are stable)
 
 
 
 
 
 
 
 
 
 
 
734
  bands = None
735
  band = 0
736
  band_score = 0.0
@@ -754,8 +1078,11 @@ def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
754
  max_ulp = 0
755
  mean_ulp = 0.0
756
  first_div = -1
 
757
  n_el = 0
758
  n_above_band = 0
 
 
759
  nan_r = nan_c = inf_r = inf_c = 0
760
  worst_elements = []
761
  for oi, (r, c) in enumerate(zip(r_outs, c_outs)):
@@ -773,8 +1100,11 @@ def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
773
  mean_ulp += float(d.double().sum().item())
774
  n_el += d.numel()
775
  nz = (d > 0)
 
 
776
  if first_div < 0 and nz.any():
777
  first_div = int(nz.flatten().nonzero()[0].item())
 
778
  if nz.any():
779
  buckets = torch.clamp(
780
  torch.log2(d[nz].double()).floor(), 0, 62
@@ -791,6 +1121,7 @@ def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
791
  if val > 0:
792
  worst_elements.append({
793
  "output": oi, "index": int(ix),
 
794
  "ref": float(rf[ix].item()),
795
  "cand": float(cf[ix].item()),
796
  "ulp": int(val)})
@@ -810,23 +1141,27 @@ def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
810
  inf_r += int(torch.isinf(r).sum().item())
811
  inf_c += int(torch.isinf(c).sum().item())
812
  worst_elements.sort(key=lambda w: -w["ulp"])
 
813
  trial_rep = {
814
  "max_ulp": max_ulp,
815
  "mean_ulp": round(mean_ulp / max(n_el, 1), 3),
816
  "first_divergent_index": first_div,
 
817
  "nan_ref": nan_r, "nan_cand": nan_c,
818
  "inf_ref": inf_r, "inf_cand": inf_c,
819
  "order_band_ulp": band,
820
  "band_score": round(band_score, 2),
821
  "frac_above_band": round(n_above_band / max(n_el, 1), 6),
 
 
822
  "worst_elements": worst_elements[:worst_k],
823
  }
824
  per_trial.append(trial_rep)
825
  if max_ulp > worst["max_ulp"]:
826
  worst = trial_rep
827
- band_max = max(t["order_band_ulp"] for t in per_trial)
828
  max_seen = max(t["max_ulp"] for t in per_trial)
829
- if order_shuffle is not None or ref_variants:
 
830
  score = max(t["band_score"] for t in per_trial)
831
  ulp_ok = score <= band_margin
832
  criterion = (f"pointwise max(ulp / (order_band + 2)) = "
@@ -841,8 +1176,34 @@ def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
841
  max_rel_seen <= (rtol or 0) or max_abs_seen <= (atol or 0)
842
  criterion += (f"; OR max_abs {max_abs_seen:.3e} <= atol {atol} / "
843
  f"max_rel {max_rel_seen:.3e} <= rtol {rtol}")
844
- return {
845
- "pass": bool(ulp_ok or tol_ok),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
846
  "criterion": criterion,
847
  "worst_trial": worst,
848
  "max_abs": max_abs_seen,
@@ -850,6 +1211,9 @@ def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
850
  "trials": per_trial,
851
  "ulp_hist_log2": dict(sorted(total_hist.items())),
852
  }
 
 
 
853
 
854
 
855
  _FUZZ_VARIANTS = ("contig", "transposed", "strided_rows", "offset_slice",
@@ -865,6 +1229,30 @@ def _context_alive(device):
865
  return False
866
 
867
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
868
  def _fuzz_build(make, shape, dtype):
869
  try:
870
  out = make(tuple(shape), dtype)
@@ -881,7 +1269,7 @@ def _fuzz_variant(x, v):
881
  if v == "strided_rows":
882
  return x[::2] if x.dim() >= 1 and x.shape[0] >= 2 else x
883
  if v == "offset_slice":
884
- # a storage-offset-1 view: misaligned for vectorized loads
885
  return x.flatten()[1:] if x.numel() > 1 else x
886
  if v == "overflow_expand":
887
  if x.numel() == 0:
@@ -895,8 +1283,7 @@ def _fuzz_variant(x, v):
895
 
896
  def _fuzz_group(op, ref, make, shape, dtype, variants, ulp_tol, device,
897
  stop_on_context_loss=True):
898
- """All variant cases for one (shape, dtype) group; returns (rows,
899
- context_lost)."""
900
  rows = []
901
  bases = _fuzz_build(make, shape, dtype)
902
  lost = False
@@ -933,8 +1320,7 @@ def _fuzz_group(op, ref, make, shape, dtype, variants, ulp_tol, device,
933
  got = op(*xs)
934
  want = ref(*xs)
935
  if v == "overflow_expand":
936
- # bounded comparison: the head plus the high-index tail,
937
- # which is where int32 flat indexing wraps
938
  w = 1 << 20
939
  gf, wf = got.flatten(), want.flatten()
940
  pairs = ((gf[:w], wf[:w]), (gf[-w:], wf[-w:]))
@@ -959,20 +1345,30 @@ def _fuzz_group(op, ref, make, shape, dtype, variants, ulp_tol, device,
959
  return rows, lost
960
 
961
 
962
- # isolate child: a plain subprocess that bootstraps its own metakernel
963
- # (get_kernel, else the dev-root JIT fallback) and unpickles op/ref/make
964
- # by module reference, so it works under get_kernel's synthetic names
965
- _REPO_ID = "phanerozoic/metakernel"
966
- _REPO_VERSION = 1
967
-
968
- _ISOLATE_BOOTSTRAP = r"""
969
- import json
970
- import os
971
- import pickle
972
- import sys
973
-
 
 
 
 
 
 
 
 
 
 
974
  meta = json.load(open(sys.argv[1], "r"))
975
- sys.path[:0] = meta["sys_path"] # BEFORE unpickling module-ref functions
976
  try:
977
  import kernels
978
  _mk = kernels.get_kernel(meta["repo"], version=meta["version"],
@@ -985,85 +1381,165 @@ except Exception:
985
  import load_local
986
  _mk = load_local.load()
987
  import torch
988
- op, ref, make = pickle.load(open(sys.argv[2], "rb"))
989
- rows, _lost = _mk._fuzz_group(
990
- op, ref, make, tuple(meta["shape"]), getattr(torch, meta["dtype"]),
991
- tuple(meta["variants"]), meta["ulp_tol"], meta["device"],
992
- stop_on_context_loss=False)
993
- pickle.dump(rows, open(sys.argv[3], "wb"))
 
 
 
 
 
 
 
 
 
 
 
 
994
  """
995
 
996
 
997
- def fuzz_shapes(op, ref, make, shapes=None, variants=_FUZZ_VARIANTS,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
998
  dtypes=(torch.float32,), ulp_tol=64, isolate=False,
999
- isolate_timeout_s=180, device="cuda"):
1000
- """Run op vs ref across edge shapes, dtypes, and layout mutations;
1001
- report ok / mismatch / exception per case.
1002
-
1003
- `make(shape, dtype)` (or `make(shape)`) builds a tensor or tuple;
1004
- layout variants derive non-contiguous and misaligned views whose
1005
- content equals a contiguous copy. `overflow_expand` stride-0-expands
1006
- past 2**31 logical elements to catch int32 indexing (`skipped_vram`
1007
- when it would not fit). Every case carries a `repro` recipe.
1008
- In-process, the CUDA context is health-checked after exceptions and
1009
- remaining cases marked `context_lost` if killed; `isolate=True`
1010
- spawns each (shape, dtype) group in a subprocess, so a
1011
- context-killing candidate costs one group (`crashed_isolated`);
1012
- op/ref/make must be picklable."""
1013
  if shapes is None:
1014
  shapes = [(), (7,), (0, 8), (1, 1), (1, 17), (3, 63), (17, 129),
1015
  (128, 128), (1023, 5), (2, 4096), (2, 3, 5)]
 
 
1016
 
1017
  rows = []
1018
  lost = False
1019
  if isolate:
1020
  import json
1021
  import pickle
1022
- import subprocess
1023
  import sys as _sys
1024
  import tempfile
1025
  tmpdir = tempfile.mkdtemp(prefix="mk_fuzz_")
1026
  fns = os.path.join(tmpdir, "fns.pkl")
 
 
1027
  with open(fns, "wb") as f:
1028
- pickle.dump((op, ref, make), f)
1029
- for dtype in dtypes:
1030
- for shape in shapes:
1031
- meta = os.path.join(tmpdir, "meta.json")
1032
- out = os.path.join(tmpdir, "rows.pkl")
1033
- if os.path.exists(out):
1034
- os.remove(out)
1035
- with open(meta, "w", encoding="utf-8") as f:
1036
- json.dump({
1037
- "sys_path": [p for p in _sys.path
1038
- if isinstance(p, str)],
1039
- "repo": _REPO_ID, "version": _REPO_VERSION,
 
 
 
 
1040
  "shape": list(shape),
1041
  "dtype": str(dtype).replace("torch.", ""),
1042
  "variants": list(variants),
1043
  "ulp_tol": ulp_tol, "device": device,
1044
- }, f)
1045
- group = None
1046
- stderr_tail = ""
1047
- try:
1048
- r = subprocess.run(
1049
- [_sys.executable, "-c", _ISOLATE_BOOTSTRAP, meta,
1050
- fns, out], timeout=isolate_timeout_s,
1051
- capture_output=True)
1052
- if r.returncode == 0 and os.path.exists(out):
1053
  with open(out, "rb") as f:
1054
- group = pickle.load(f)
1055
- exitcode = r.returncode
1056
- stderr_tail = r.stderr.decode(
1057
- errors="replace")[-400:]
1058
- except subprocess.TimeoutExpired:
1059
- exitcode = "timeout"
1060
- if group is None:
1061
- group = [{"shape": tuple(shape), "variant": "*",
1062
- "dtype": str(dtype).replace("torch.", ""),
1063
- "status": "crashed_isolated",
1064
- "exitcode": exitcode,
1065
- "stderr_tail": stderr_tail}]
1066
- rows.extend(group)
1067
  else:
1068
  pending = [(dt, sh) for dt in dtypes for sh in shapes]
1069
  for gi, (dtype, shape) in enumerate(pending):
@@ -1080,21 +1556,27 @@ def fuzz_shapes(op, ref, make, shapes=None, variants=_FUZZ_VARIANTS,
1080
  n_bad = sum(1 for r in rows
1081
  if r["status"] not in ("ok", "skipped_vram",
1082
  "skipped_empty"))
1083
- return {"cases": rows, "failed": n_bad, "total": len(rows),
1084
- "context_lost": lost}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1085
 
1086
 
1087
  def sweep(factory, grid, iters=30, warmup=5, bytes=None, flops=None,
1088
  check=None, budget_s=None, race=False):
1089
- """Launch-configuration search: `factory(**point) -> callable`; one
1090
- bench row per grid point, sorted fastest first. A point whose factory
1091
- or run raises is reported, not skipped silently. `check(fn) -> bool`
1092
- is an optional correctness gate recorded per row (a fast wrong config
1093
- must not win a sweep). `budget_s` caps total sweep time; points not
1094
- reached are reported as `not_run` rather than silently dropped.
1095
- `race=True` runs successive halving: a quick pass over every point at
1096
- iters/4, then the full budget only on the fastest quartile; pruned
1097
- points keep their quick measurement and status `pruned_race`."""
1098
  keys = list(grid.keys())
1099
 
1100
  def expand(i, acc):
@@ -1159,13 +1641,71 @@ def sweep(factory, grid, iters=30, warmup=5, bytes=None, flops=None,
1159
  continue
1160
  rows.append(run_point(p, iters))
1161
  rows.sort(key=lambda r: (not r.get("check", True), r["median_ms"]))
 
 
 
 
 
 
 
 
 
 
 
1162
  return rows
1163
 
1164
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1165
  def alloc_stamps(n, blocks=None, device="cuda"):
1166
- """Device buffer for the MK_STAMP convention (metakernel_cuda/
1167
- stamps.cuh): the candidate kernel writes clock64() into slot i. With
1168
- `blocks`, allocates the [blocks, n] layout for MK_STAMP_BLOCK."""
1169
  if blocks is not None:
1170
  return torch.zeros(blocks, n, dtype=torch.int64, device=device)
1171
  return torch.zeros(n, dtype=torch.int64, device=device)
@@ -1175,8 +1715,7 @@ _DRIFT_SKIP = ("device", "clock", "launch_overhead", "transfer_bw")
1175
 
1176
 
1177
  def pinned_dossiers():
1178
- """Names of the dossiers pinned in this release (one per certified
1179
- architecture)."""
1180
  return sorted(_PINNED.keys())
1181
 
1182
 
@@ -1195,11 +1734,7 @@ def _numeric_leaves(d, prefix=""):
1195
 
1196
 
1197
  def dossier_drift(current, pinned=None, rel_tol=0.35, skip=_DRIFT_SKIP):
1198
- """Compare a fresh dossier against the release-pinned one for the
1199
- same architecture: per-leaf relative drift, worst drift, entries
1200
- over `rel_tol`. Host-dependent entries and raw clocks are skipped by
1201
- default (`skip=()` compares everything); `pinned=None` auto-selects
1202
- by sm_arch and raises if no pin exists."""
1203
  if pinned is None:
1204
  arch = current["device"]["sm_arch"]
1205
  matches = [k for k, v in _PINNED.items()
@@ -1222,16 +1757,19 @@ def dossier_drift(current, pinned=None, rel_tol=0.35, skip=_DRIFT_SKIP):
1222
  "rel": round(rel, 4)})
1223
  worst = max((e["rel"] for e in entries), default=0.0)
1224
  over = [e for e in entries if e["rel"] > rel_tol]
1225
- return {"entries": entries, "worst_rel": worst, "over_tol": over,
1226
- "rel_tol": rel_tol, "compared": len(entries),
1227
- "pass": len(over) == 0}
 
 
 
 
 
1228
 
1229
 
1230
  def stamped_demo(n=1 << 22, fma_iters=4000, blocks=None, threads=256,
1231
  device="cuda"):
1232
- """Run the shipped MK_STAMP_BLOCK example kernel (global load, FMA
1233
- compute, global store) and decode its stamps; a template for
1234
- instrumenting a candidate."""
1235
  props = torch.cuda.get_device_properties(device)
1236
  if blocks is None:
1237
  blocks = props.multi_processor_count
@@ -1244,11 +1782,7 @@ def stamped_demo(n=1 << 22, fma_iters=4000, blocks=None, threads=256,
1244
 
1245
 
1246
  def read_stamps(stamps, labels=None, clock_ghz=None):
1247
- """Decode MK_STAMP phases: consecutive deltas in ticks and in
1248
- microseconds under the measured (or provided) SM clock. A 1-D buffer
1249
- (MK_STAMP: block 0 only) yields one delta per phase; a 2-D [B, n]
1250
- buffer (MK_STAMP_BLOCK) yields min / median / max across blocks per
1251
- phase, with blocks whose stamps stayed zero excluded."""
1252
  if clock_ghz is None:
1253
  clock_ghz = measure_clock(stamps.device)
1254
 
@@ -1284,3 +1818,104 @@ def read_stamps(stamps, labels=None, clock_ghz=None):
1284
  return {"phases": phases, "clock_ghz": round(clock_ghz, 3),
1285
  "blocks_reporting": int(live.sum().item()),
1286
  "blocks_total": int(live.numel())}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Measuring instrument for the kernel-design loop: measured device facts, throttle-aware timing, adversarial correctness comparison, all as loadable ops returning plain data."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  import math
3
  import os
4
  import time
 
8
  try:
9
  from ._ops import ops
10
  except ImportError: # pragma: no cover - spawned child in a source tree
11
+ # children re-import the package; a source checkout rebuilds the op table from the cached JIT extension
 
 
12
  _root = os.environ.get("METAKERNEL_DEV_ROOT")
13
  if not _root:
14
  raise
 
30
  except ImportError: # pragma: no cover - fresh tree before first pin
31
  _PINNED = {}
32
 
33
+ __all__ = ["probe_device", "probe_all_devices", "bench", "compare", "judge",
34
+ "order_shuffles", "fuzz_shapes", "sweep", "alloc_stamps",
35
+ "read_stamps", "stamped_demo", "measure_clock", "ulp_diff",
36
+ "dossier_drift", "pinned_dossiers", "mma_tile_check", "schema",
37
+ "probe_capturable", "ops"]
38
+
39
+ _REPO_ID = "phanerozoic/metakernel"
40
+ _REPO_VERSION = 1
41
+ _PROBE_SCHEMA = 2
42
+
43
+ # kind id, C-tile M*N, nominal K, nominal K-lanes per unrolled loop iteration
44
  _MMA_KINDS = {
45
+ "fp16": (0, 16 * 16, 16, 64.0),
46
+ "bf16": (1, 16 * 16, 16, 64.0),
47
+ "tf32": (2, 16 * 16, 8, 32.0),
48
+ "int8": (3, 16 * 16, 16, 64.0),
49
+ "fp8_e4m3": (4, 16 * 8, 32, 256.0),
50
+ "fp16_sparse_2_4": (5, 16 * 8, 32, 128.0),
51
+ "fp8_e5m2": (6, 16 * 8, 32, 256.0),
52
  }
53
 
54
 
 
57
 
58
 
59
  def _events(fn, reps):
60
+ """Time reps calls of fn with one cudaEvent pair each; returns a ms list."""
61
  starts = [torch.cuda.Event(enable_timing=True) for _ in range(reps)]
62
  stops = [torch.cuda.Event(enable_timing=True) for _ in range(reps)]
63
  for s, e in zip(starts, stops):
 
69
 
70
 
71
  def measure_clock(device="cuda", ticks=4_000_000, blocks=None):
72
+ """Effective SM clock in GHz from clock64 ticks over wall time under an FMA spin."""
 
73
  props = torch.cuda.get_device_properties(device)
74
  if blocks is None:
75
  blocks = props.multi_processor_count
 
102
 
103
 
104
  def _sattolo_ring(n, device, generator=None):
105
+ """Random single-cycle permutation as an int32 next-pointer table."""
 
106
  perm = torch.randperm(n, generator=generator)
107
  ring = torch.empty(n, dtype=torch.int32)
108
  ring[perm] = torch.roll(perm, -1).to(torch.int32)
 
110
 
111
 
112
  def _smi_settings(index):
113
+ """ECC mode, driver, power limit via nvidia-smi; None fields when unavailable."""
 
114
  import subprocess
115
  try:
116
  out = subprocess.run(
 
130
  return {"ecc": None, "driver": None, "power_limit_w": None}
131
 
132
 
133
+ def _unravel(flat, shape):
134
+ """Multi-dimensional index for a flat index into shape."""
135
+ if not shape:
136
+ return []
137
+ out = []
138
+ for s in reversed(list(shape)):
139
+ out.append(int(flat % s))
140
+ flat //= s
141
+ return list(reversed(out))
142
+
143
+
144
  def mma_tile_check(device="cuda", trials=8):
145
+ """Random small-integer m16n16k16 tiles through the wmma path, compared exactly to the reference GEMM."""
 
 
146
  worst = 0.0
147
  for t in range(trials):
148
  g = torch.Generator(device=device).manual_seed(1000 + t)
 
160
  "trials": trials}
161
 
162
 
163
+ def _tenancy_canary(device, rel_tol=0.10, attempts=3):
164
+ """Fixed-cost triad timed in paired rounds; busy only when every attempt disagrees."""
165
+ n4 = 4 << 20
166
+ a = torch.rand(n4 * 4, device=device)
167
+ b = torch.rand(n4 * 4, device=device)
168
+ c = torch.empty(n4 * 4, device=device)
169
+ ops.mk_triad(a, b, c, 4, 1.5)
170
+ rels = []
171
+ for _ in range(attempts):
172
+ r1 = _median(_events(lambda: ops.mk_triad(a, b, c, 4, 1.5), 3))
173
+ r2 = _median(_events(lambda: ops.mk_triad(a, b, c, 4, 1.5), 3))
174
+ rel = abs(r1 - r2) / min(r1, r2)
175
+ rels.append(round(rel, 4))
176
+ if rel <= rel_tol:
177
+ return False, {"rel_diffs": rels, "rel_tol": rel_tol}
178
+ return True, {"rel_diffs": rels, "rel_tol": rel_tol}
179
+
180
+
181
+ def _cache_dir():
182
+ base = os.environ.get("LOCALAPPDATA") or os.path.expanduser("~/.cache")
183
+ d = os.path.join(base, "metakernel")
184
+ os.makedirs(d, exist_ok=True)
185
+ return d
186
+
187
+
188
+ def _cache_path(device, quick):
189
+ import re
190
+ props = torch.cuda.get_device_properties(device)
191
+ smi = _smi_settings(torch.device(device).index or 0)
192
+ slug = re.sub(r"[^a-z0-9]+", "", props.name.lower())[:24]
193
+ key = "sm%d%d_%s_drv%s_cu%s_q%d_s%d" % (
194
+ props.major, props.minor, slug, smi["driver"] or "na",
195
+ torch.version.cuda, int(quick), _PROBE_SCHEMA)
196
+ return os.path.join(_cache_dir(), key + ".json")
197
+
198
+
199
+ def _mma_calibrate(kind_id, sink):
200
+ """Executed K-lanes per loop iteration from a sub-saturation accumulator read-back, with a linearity check."""
201
+ gains = []
202
+ for cal in (4, 32):
203
+ sink[1] = 0.0
204
+ ops.mk_mma(kind_id, 1, 1, cal, sink)
205
+ _sync()
206
+ gains.append(float(sink[1].item()) / cal)
207
+ linear = gains[0] > 0 and abs(gains[1] - gains[0]) / gains[0] < 0.02
208
+ return gains[0], linear
209
+
210
+
211
+ def _fp8_accum_stall(kind_id, sink, k_exec):
212
+ """Growth curve of the fp8 accumulator; a stall reveals the effective accumulate mantissa width."""
213
+ vals = []
214
+ for it in (32, 64, 128, 256, 512):
215
+ sink[1] = 0.0
216
+ ops.mk_mma(kind_id, 1, 1, it, sink)
217
+ _sync()
218
+ vals.append(float(sink[1].item()) / 2.0)
219
+ if vals[-1] == vals[-2] and vals[-1] < vals[0] * 16 * 0.99:
220
+ bits = int(round(math.log2(vals[-1] / (2.0 * k_exec))))
221
+ return {"stall_element_value": vals[-1],
222
+ "effective_accum_mantissa_bits": bits}
223
+ return {"stall_element_value": None,
224
+ "effective_accum_mantissa_bits": None}
225
+
226
+
227
+ def probe_device(device="cuda", quick=False, cached=True,
228
+ tenancy_check=True):
229
+ """Empirical device dossier; every entry measured by a dedicated probe kernel and stamped with its SM clock."""
230
+ cpath = _cache_path(device, quick)
231
+ if cached and os.path.exists(cpath):
232
+ import json
233
+ return json.load(open(cpath, encoding="utf-8"))
234
+ if tenancy_check:
235
+ busy, ev = _tenancy_canary(device)
236
+ if busy:
237
+ raise RuntimeError("device_busy: canary rounds disagree %s; "
238
+ "idle the device or pass "
239
+ "tenancy_check=False" % ev)
240
  props = torch.cuda.get_device_properties(device)
241
  sm = props.multi_processor_count
242
  l2 = props.L2_cache_size
 
249
  "vram_bytes": props.total_memory,
250
  "torch": torch.__version__,
251
  "cuda_runtime": torch.version.cuda,
252
+ # instrument settings recorded because readings depend on them
 
253
  **_smi_settings(torch.device(device).index or 0),
254
  }
255
  }
 
274
  }
275
  clk = max(traj)
276
 
277
+ # ---- device-memory bandwidth per access width plus read/write/gather
278
+ n4 = 16 << 20
 
279
  if props.total_memory < 4 << 30:
280
  n4 = 4 << 20
281
  a = torch.rand(n4 * 4, device=device)
 
299
  dtype=torch.int32).to(device)
300
  ops.mk_gather(a, idx, c, 256, 0)
301
  ms = _median(_events(lambda: ops.mk_gather(a, idx, c, 256, 0), reps))
 
302
  hbm["random_gather_16B"] = round(
303
  (idx.numel() * 4 + 2 * n4 * 16) / (ms * 1e-3) / 1e9, 1)
304
  d["dram_bw"] = _entry(hbm, "GB/s", "streaming triad c=a+s*b per access "
 
309
  "so its nominal number is the achievable rate for "
310
  "random access, not a line-efficiency claim")
311
 
312
+ # ---- L2 bandwidth over an L2-resident working set
313
  ws = max(l2 // 4, 1 << 20)
314
+ nl2 = ws // 16
315
  al = a[:nl2 * 4].contiguous()
316
  bl = b[:nl2 * 4].contiguous()
317
  cl = c[:nl2 * 4].contiguous()
 
326
  "medium", "first pass streams from HBM; amortized "
327
  f"over {passes} passes")
328
 
329
+ # ---- latencies: min of 3 chases per level so a co-tenant burst cannot land in the dossier
 
 
330
  lat = {}
331
  idle_clk = measure_clock(device, ticks=2_000_000, blocks=1)
 
 
 
 
 
 
 
332
 
333
+ def chase(op, ring, warm_hops, hops, extra=None):
334
+ best = None
335
+ for _ in range(3):
336
+ if extra is None:
337
+ op(ring, warm_hops, out64)
338
+ _sync()
339
+ op(ring, hops, out64)
340
+ else:
341
+ op(ring, extra, warm_hops, out64)
342
+ _sync()
343
+ op(ring, extra, hops, out64)
344
+ _sync()
345
+ t = int(out64[0].item()) / hops
346
+ best = t if best is None else min(best, t)
347
+ return best
 
 
348
 
349
+ ring_s = _sattolo_ring(8192, device)
350
+ lat["smem_cycles"] = round(
351
+ chase(lambda r, h, o: ops.mk_chase_shared(r, h, o), ring_s,
352
+ 1 << 14, 1 << 16), 1)
353
+ ring_l1 = _sattolo_ring((16 << 10) // 4, device)
354
+ lat["l1_cycles"] = round(
355
+ chase(lambda r, h, o: ops.mk_chase_global(r, h, o), ring_l1,
356
+ ring_l1.numel(), 1 << 15), 1)
357
+ ring_l2 = _sattolo_ring((2 << 20) // 4, device)
358
+ lat["l2_cycles"] = round(
359
+ chase(lambda r, h, o: ops.mk_chase_global(r, h, o), ring_l2,
360
+ ring_l2.numel(), 1 << 14), 1)
361
  hbm_bytes = min(max(4 * l2, 64 << 20), props.total_memory // 8)
362
  ring_h = _sattolo_ring(int(hbm_bytes) // 4, device)
363
  hops_h = max((2 * l2) // 128, 1 << 17)
364
+ lat["dram_cycles"] = round(
365
+ chase(lambda r, h, o: ops.mk_chase_global(r, h, o), ring_h,
366
+ 1 << 14, hops_h), 1)
 
 
367
  lat["ns_per_cycle"] = round(1.0 / idle_clk, 3)
368
  d["latency"] = _entry(lat, "cycles (single-lane dependent loads)",
369
+ "Sattolo-cycle pointer chase, min of 3 runs; 16 "
370
+ "KB ring for L1, 2 MB TLB-resident ring for L2, "
371
+ "self-evicting chain (2x L2 in touched lines) "
372
+ "for HBM", idle_clk, "high",
373
  "smem includes index arithmetic; HBM includes "
374
  "residual TLB effects at large footprints")
375
  del ring_h, ring_l2, ring_l1, ring_s
376
 
377
+ # ---- fp32/fp64 FMA and tensor-core throughput
378
  blocks = sm * 4
379
  iters = 20_000 if quick else 60_000
380
  ops.mk_fma_f32(blocks, 256, iters, sink)
381
  ms = _median(_events(lambda: ops.mk_fma_f32(blocks, 256, iters, sink),
382
  reps))
383
+ flops = blocks * 256 * iters * 16 * 2 * 2
384
  d["fma_f32"] = _entry(round(flops / (ms * 1e-3) / 1e12, 2), "TFLOP/s",
385
  "register-resident FMA chains", clk, "high")
386
  sink64 = torch.zeros(2, dtype=torch.float64, device=device)
 
392
  d["fma_f64"] = _entry(round(flops64 / (ms * 1e-3) / 1e12, 3), "TFLOP/s",
393
  "register-resident double FMA chains (the DFMA "
394
  "pipe)", clk, "high")
395
+
396
+ # ---- ILP ladder: 1/2/4 chains at low occupancy separates latency-bound from pipe-bound
397
+ ilp = {}
398
+ it_ilp = 20_000 if quick else 60_000
399
+ for ch in (1, 2, 4):
400
+ ops.mk_fma_ilp(ch, sm, 64, it_ilp, sink)
401
+ ms = _median(_events(
402
+ lambda c2=ch: ops.mk_fma_ilp(c2, sm, 64, it_ilp, sink), reps))
403
+ ilp[f"ilp{ch}"] = round(
404
+ sm * 64 * it_ilp * 16 * 2 * ch / (ms * 1e-3) / 1e12, 2)
405
+ ilp["ratio_4_1"] = round(ilp["ilp4"] / ilp["ilp1"], 2)
406
+ d["fma_ilp"] = _entry(ilp, "TFLOP/s",
407
+ "1/2/4 independent dependent FMA chains per "
408
+ "thread at one block of 64 threads per SM; a "
409
+ "ratio near 4 means latency-bound at that "
410
+ "occupancy", clk, "high")
411
+
412
  mma = {}
413
  mma_klanes = {}
414
+ fp8_accum = {}
415
+ for name, (kind, mn, k_nom, nom_lanes) in _MMA_KINDS.items():
416
  it = 30_000 if quick else 100_000
417
  r = ops.mk_mma(kind, blocks, 8, 512, sink)
418
  if r != 0:
419
  mma[name] = "unsupported"
420
  continue
421
  _sync()
422
+ gain, linear = _mma_calibrate(kind, sink)
 
 
 
 
 
 
423
  mma_klanes[name] = {
424
+ "executed_k_per_iter": round(gain, 2),
425
+ "vs_nominal": round(gain / nom_lanes, 3),
426
+ "calibration_linear": linear,
427
  }
428
+ if name.startswith("fp8"):
429
+ fp8_accum[name] = _fp8_accum_stall(kind, sink, k_nom)
430
  ms = _median(_events(
431
  lambda k=kind, i=it: ops.mk_mma(k, blocks, 8, i, sink), reps))
432
+ tflops = blocks * 8 * it * 2.0 * mn * gain / (ms * 1e-3) / 1e12
433
+ if name == "fp16_sparse_2_4":
434
+ mma[name] = {"executed": round(tflops, 1),
435
+ "dense_equivalent": round(2 * tflops, 1)}
436
+ else:
437
+ mma[name] = round(tflops, 1)
438
  d["mma"] = _entry(mma, "TFLOP/s (TOP/s for int8)",
439
  "register-fragment mma_sync loops (wmma; fp8 and "
440
+ "2:4-sparse fp16 via inline PTX m16n8k32); flops "
441
+ "derive from a sub-saturation accumulator read-back "
 
442
  "with every fragment element observed, never the "
443
+ "declared loop shape; sparse reports executed and "
444
+ "dense-equivalent flops",
445
  clk, "medium",
446
  "dense-tile rate without memory traffic; real "
447
  "kernels see this only when staging hides behind it")
448
  d["mma"]["verified_k_lanes"] = mma_klanes
449
+ if fp8_accum:
450
+ d["mma"]["fp8_accum"] = fp8_accum
451
 
452
+ # ---- fed MMA: smem-staged fp16 tiles under cp.async double buffering
453
+ if props.major >= 8:
454
+ gbuf = torch.ones(16 << 20, dtype=torch.float16, device=device)
455
+ sink[1] = 0.0
456
+ r = ops.mk_mma_fed(gbuf, 1, 1, 64, sink)
457
+ if r == 0:
458
+ _sync()
459
+ fed_gain = float(sink[1].item()) / 64
460
+ it_fed = 6_000 if quick else 20_000
461
+ ops.mk_mma_fed(gbuf, sm * 2, 8, it_fed, sink)
462
+ ms = _median(_events(
463
+ lambda: ops.mk_mma_fed(gbuf, sm * 2, 8, it_fed, sink),
464
+ reps))
465
+ fed = sm * 2 * 8 * it_fed * 2.0 * 256 * fed_gain / \
466
+ (ms * 1e-3) / 1e12
467
+ d["mma_fed"] = _entry(
468
+ round(fed, 1), "TFLOP/s",
469
+ "m16n16k16 fp16 tiles fed from an L2-resident buffer "
470
+ "through cp.async double-buffered shared memory; "
471
+ "accumulator-verified", clk, "high",
472
+ "the achieved counterpart to the register peak: staging "
473
+ "exposed, one dependent accumulator chain per warp")
474
+ del gbuf
475
+
476
+ # ---- shared-memory bandwidth and the bank-conflict stride sweep
477
  it_sm = 30_000 if quick else 100_000
478
  ops.mk_smem_bw(blocks, 256, it_sm, sink)
479
  ms = _median(_events(lambda: ops.mk_smem_bw(blocks, 256, it_sm, sink),
 
483
  "block-resident read+write streaming (8 B per "
484
  "thread-iteration), aggregated across SMs", clk,
485
  "medium",
486
+ "bank-conflict-light access pattern; the stride "
487
+ "sweep below prices the conflicted case")
488
+ strides = (1, 8, 32) if quick else (1, 2, 4, 8, 16, 32)
489
+ it_st = 8_000 if quick else 20_000
490
+ st_ms = {}
491
+ for s2 in strides:
492
+ ops.mk_smem_stride(s2, blocks, 256, it_st, sink)
493
+ st_ms[s2] = _median(_events(
494
+ lambda s3=s2: ops.mk_smem_stride(s3, blocks, 256, it_st, sink),
495
+ reps))
496
+ d["smem_conflict_penalty"] = _entry(
497
+ {f"stride_{s2}": round(st_ms[s2] / st_ms[1], 2) for s2 in strides},
498
+ "x (time ratio vs stride 1)",
499
+ "dependent read-FMA-write per thread at warp-lane bank distance "
500
+ "`stride`; stride 32 puts every lane in one bank", clk, "high",
501
+ "the direct conflict measurement that disambiguates a "
502
+ "bank-conflict signature from an ILP stall")
503
 
504
  # ---- PCIe / host transfer bandwidth
505
  nbytes = 256 << 20
 
530
  "d2d counts one direction of traffic")
531
  del host_pin, host_pag, dev_t, dev_u
532
 
533
+ # ---- atomic contention curve
 
 
534
  n_thr = blocks * 256
535
  buf = torch.zeros(n_thr, dtype=torch.float32, device=device)
536
  at = {"curve_slots": {}}
 
540
  buf.zero_()
541
  ops.mk_atomics(1, blocks, 256, it_at, buf, slots)
542
  ms = _median(_events(
543
+ lambda s4=slots: ops.mk_atomics(1, blocks, 256, it_at, buf, s4),
544
  reps))
545
  at["curve_slots"][str(slots)] = round(
546
  n_thr * it_at / (ms * 1e-3) / 1e9, 2)
 
553
  "of distinct global addresses from 1 (full "
554
  "contention) to one per thread", clk, "high")
555
 
556
+ # ---- launch overhead: torch-dispatch path and the raw C++ path
557
  anchor = sink
558
  n_launch = 500 if quick else 2000
559
  _sync()
 
568
  ops.mk_empty(anchor)
569
  _sync()
570
  rt = (time.perf_counter() - t0) / n_rt * 1e6
571
+ raw_q = float(ops.mk_launch_raw(n_launch, 0, anchor))
572
+ raw_rt = float(ops.mk_launch_raw(n_rt, 1, anchor))
573
  d["launch_overhead"] = _entry(
574
+ {"torch_queued_us": round(queued, 2),
575
+ "torch_round_trip_us": round(rt, 2),
576
+ "raw_queued_us": round(raw_q / n_launch * 1e6, 2)
577
+ if raw_q >= 0 else None,
578
+ "raw_round_trip_us": round(raw_rt / n_rt * 1e6, 2)
579
+ if raw_rt >= 0 else None},
580
+ "us", "empty-kernel trains; torch path per-launch through op "
581
+ "dispatch, raw path looped inside one binding call", clk,
582
+ "medium", "host-state-dependent; the torch-minus-raw gap is the "
583
+ "dispatch cost a torch-launched candidate pays")
584
 
585
  # ---- grid barrier
586
  bar_iters = 500 if quick else 2000
 
596
  "unavailable or grid too large", clk,
597
  "high")
598
 
599
+ # ---- occupancy API table plus timed knees
 
 
600
  occ = {}
601
  for threads in (128, 256, 512, 1024):
602
  row = {}
 
617
  ops.mk_fma_dep(knee_blocks, threads, smem_kb * 1024,
618
  knee_iters, sink)
619
  ms = _median(_events(
620
+ lambda t=threads, s5=smem_kb: ops.mk_fma_dep(
621
+ knee_blocks, t, s5 * 1024, knee_iters, sink), reps))
622
  fl = knee_blocks * threads * knee_iters * 16 * 2
623
  row[f"{smem_kb}KB"] = round(fl / (ms * 1e-3) / 1e12, 2)
624
  knees[f"t{threads}"] = row
 
627
  "at forced (threads, dyn smem) occupancies over fixed total work; "
628
  "the cliff marks where occupancy stops covering latency",
629
  clk, "high", "timing-derived counterpart to the API table above")
630
+ if cached:
631
+ import json
632
+ json.dump(d, open(cpath, "w", encoding="utf-8"))
633
  return d
634
 
635
 
636
+ def probe_all_devices(quick=True, cached=True, tenancy_check=True):
637
+ """One dossier per visible device plus a pairwise P2P bandwidth matrix."""
638
+ n = torch.cuda.device_count()
639
+ devices = {}
640
+ for i in range(n):
641
+ devices[i] = probe_device(f"cuda:{i}", quick=quick, cached=cached,
642
+ tenancy_check=tenancy_check)
643
+ p2p = {}
644
+ if n > 1:
645
+ nbytes = 64 << 20
646
+ for i in range(n):
647
+ for j in range(n):
648
+ if i == j:
649
+ continue
650
+ src = torch.rand(nbytes // 4, device=f"cuda:{i}")
651
+ dst = torch.empty(nbytes // 4, device=f"cuda:{j}")
652
+ dst.copy_(src)
653
+ torch.cuda.synchronize(i)
654
+ torch.cuda.synchronize(j)
655
+ t0 = time.perf_counter()
656
+ for _ in range(5):
657
+ dst.copy_(src)
658
+ torch.cuda.synchronize(i)
659
+ torch.cuda.synchronize(j)
660
+ ms = (time.perf_counter() - t0) / 5 * 1e3
661
+ p2p[f"{i}->{j}"] = {
662
+ "gbs": round(nbytes / (ms * 1e-3) / 1e9, 1),
663
+ "peer_access": bool(
664
+ torch.cuda.can_device_access_peer(i, j)),
665
+ }
666
+ del src, dst
667
+ return {"devices": devices, "p2p": p2p}
668
+
669
+
670
+ _CAPTURE_PROBE = r"""
671
+ import pickle, sys, torch
672
+ fn = pickle.load(open(sys.argv[1], "rb"))
673
+ for _ in range(3):
674
+ fn()
675
+ torch.cuda.synchronize()
676
+ try:
677
+ g = torch.cuda.CUDAGraph()
678
+ with torch.cuda.graph(g):
679
+ fn()
680
+ print("capturable", flush=True)
681
+ except Exception as e:
682
+ print("uncapturable: %s: %s" % (type(e).__name__, str(e)[:120]),
683
+ flush=True)
684
+ """
685
+
686
+
687
+ def probe_capturable(fn, timeout_s=180):
688
+ """Test CUDA-graph capturability of fn in a child process, so a capture-wedged RNG never reaches the caller."""
689
+ import pickle
690
+ import subprocess
691
+ import sys as _sys
692
+ import tempfile
693
+ try:
694
+ blob = pickle.dumps(fn)
695
+ except Exception: # noqa: BLE001
696
+ return {"capturable": None,
697
+ "reason": "fn is not picklable; probe skipped (define fn "
698
+ "at module top level to enable it)"}
699
+ path = os.path.join(tempfile.mkdtemp(prefix="mk_cap_"), "fn.pkl")
700
+ with open(path, "wb") as f:
701
+ f.write(blob)
702
+ try:
703
+ r = subprocess.run([_sys.executable, "-c", _CAPTURE_PROBE, path],
704
+ capture_output=True, text=True,
705
+ timeout=timeout_s)
706
+ except Exception as e: # noqa: BLE001
707
+ return {"capturable": None,
708
+ "reason": f"probe child failed: {type(e).__name__}"}
709
+ out = (r.stdout or "").strip()
710
+ if out == "capturable":
711
+ return {"capturable": True, "reason": ""}
712
+ if out.startswith("uncapturable"):
713
+ return {"capturable": False, "reason": out}
714
+ return {"capturable": None,
715
+ "reason": "probe child exit %s: %s" % (
716
+ r.returncode, (r.stderr or "")[-200:])}
717
+
718
+
719
+ def _bench_verdict(rep):
720
+ parts = ["median %.4g ms" % rep["median_ms"]]
721
+ if "achieved_tflops" in rep:
722
+ parts.append("%.4g TFLOP/s" % rep["achieved_tflops"])
723
+ elif "achieved_gbs" in rep:
724
+ parts.append("%.4g GB/s" % rep["achieved_gbs"])
725
+ if "roofline" in rep:
726
+ parts.append("%.0f%% of %s roof" % (rep["roofline"]["pct_of_roof"],
727
+ rep["roofline"]["bound"]))
728
+ if rep["rejected_throttled"]:
729
+ parts.append("%d/%d rejected (%s)" % (
730
+ rep["rejected_throttled"], rep["samples"],
731
+ rep["rejection_basis"]))
732
+ if rep.get("no_clean_window"):
733
+ parts.append("no clean window: the whole run was throttled")
734
+ if rep.get("alloc_growth_bytes_per_call"):
735
+ parts.append("fn allocates %d B/call" %
736
+ rep["alloc_growth_bytes_per_call"])
737
+ if rep.get("host_overhead_suspected"):
738
+ parts.append("fn appears to sync or do host work inside")
739
+ return "; ".join(parts)
740
+
741
+
742
  def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
743
  dtype="fp16", clock_guard=0.97, clock_stride=8, graph=False,
744
+ graph_probe_isolated=False, device="cuda"):
745
+ """Median-of-N cudaEvent timing with throttle rejection, roofline placement, and footgun detection."""
746
+ for name, v in (("iters", iters), ("warmup", warmup)):
747
+ if not isinstance(v, int) or v < (1 if name == "iters" else 0):
748
+ raise ValueError(f"{name} must be a non-negative int; "
749
+ f"got {v!r}")
750
+ for name, v in (("bytes", bytes), ("flops", flops)):
751
+ if v is not None and (not isinstance(v, (int, float)) or v <= 0):
752
+ raise ValueError(f"{name} must be a positive number counting "
753
+ f"{name} per call of fn; got {v!r}")
 
754
  for _ in range(warmup):
755
  fn()
756
  _sync()
757
+ # footgun probes: per-call allocation growth and hidden host work
758
+ alloc0 = torch.cuda.memory_allocated(device)
759
+ fn()
760
+ fn()
761
+ _sync()
762
+ alloc_growth = max(0, (torch.cuda.memory_allocated(device) - alloc0)
763
+ // 2)
764
+ # a burst of enqueues that leaves the stream idle means fn drains it; only trustworthy above the launch-overhead floor
765
+ for _ in range(16):
766
+ fn()
767
+ stream_idle = bool(torch.cuda.current_stream(device).query())
768
+ _sync()
769
+ t0 = time.perf_counter()
770
+ pre = _events(fn, 4)
771
+ wall_ms = (time.perf_counter() - t0) * 1e3
772
+ ev_ms = sum(pre)
773
+ host_suspect = (stream_idle and ev_ms / 4 > 0.05) or \
774
+ wall_ms > ev_ms * 1.5 + 2.0
775
+
776
  graph_state = "off"
777
  graph_reason = ""
778
+ if graph and graph_probe_isolated:
779
+ cap = probe_capturable(fn)
780
+ if cap["capturable"] is False:
781
+ graph = False
782
+ graph_state = "fallback_eager"
783
+ graph_reason = "isolated probe: " + cap["reason"]
784
  if graph:
785
  pre_rng = torch.cuda.get_rng_state(device)
786
  try:
 
795
  except Exception as e: # noqa: BLE001 - fall back, say why
796
  graph_state = "fallback_eager"
797
  graph_reason = f"{type(e).__name__}: {str(e)[:120]}"
798
+ # drain the invalidated stream; a failed capture can wedge the device RNG on some torch versions
 
 
 
799
  rng_ok = True
800
  for _attempt in range(5):
801
  try:
 
825
  samples.append((_events(fn, n), len(clocks) - 1))
826
  clocks.append(measure_clock(device, ticks=probe_ticks))
827
  best_clk = max(clocks)
828
+
829
+ # rejection basis: memory-bound work is judged on its own rate, not the SM clock
830
+ ridge_memory = False
831
+ if dossier is not None and bytes and flops:
832
+ bw0 = max(dossier["dram_bw"]["value"].values()) * 1e9
833
+ mma_v0 = dossier["mma"]["value"].get(dtype)
834
+ if isinstance(mma_v0, dict):
835
+ mma_v0 = mma_v0.get("dense_equivalent")
836
+ peak0 = (mma_v0 if isinstance(mma_v0, (int, float))
837
+ else dossier["fma_f32"]["value"]) * 1e12
838
+ ridge_memory = (flops / bytes) * bw0 < peak0
839
+ basis = "bandwidth_consistency" if (
840
+ bytes is not None and (flops is None or ridge_memory)) \
841
+ else "sm_clock"
842
+
843
  flat = []
844
+ kept = []
845
+ rejected = 0
846
+ if basis == "sm_clock":
847
+ for group, ci in samples:
848
+ window_ok = (clocks[ci] >= clock_guard * best_clk and
849
+ clocks[min(ci + 1, len(clocks) - 1)] >=
850
+ clock_guard * best_clk)
851
+ for ms in group:
852
+ flat.append(ms)
853
+ if window_ok:
854
+ kept.append(ms)
855
+ else:
856
+ rejected += 1
857
+ else:
858
+ for group, _ci in samples:
859
+ flat.extend(group)
860
+ floor = sorted(flat)[max(0, int(0.05 * len(flat)) - 1)]
861
+ for ms in flat:
862
+ if ms <= floor * 1.15:
863
  kept.append(ms)
864
  else:
865
  rejected += 1
866
+ all_rejected = not kept
867
+ if all_rejected:
868
  kept = flat
869
  med = _median(kept)
870
  ks = sorted(kept)
871
  n_k = len(ks)
872
+ # order-statistic 95% CI on the median; degenerate at tiny n
 
873
  half = 1.959964 * (n_k ** 0.5) / 2.0
874
  lo_i = max(0, int(n_k / 2 - half) - 1)
875
  hi_i = min(n_k - 1, int(n_k / 2 + half))
 
884
  "mean_ms": round(sum(kept) / n_k, 4),
885
  "samples": len(flat),
886
  "rejected_throttled": rejected,
887
+ "rejection_basis": basis,
888
  "graph_captured": graph_state == "captured",
889
  "clock_ghz": {"best": round(best_clk, 3),
890
  "worst": round(min(clocks), 3)},
891
  }
892
+ no_clean = all_rejected
893
+ if basis == "sm_clock" and dossier is not None:
894
+ ref_clk = dossier["clock"]["max_ghz"]
895
+ if best_clk < clock_guard * ref_clk:
896
+ no_clean = True
897
+ if no_clean:
898
+ rep["no_clean_window"] = True
899
  if graph_reason:
900
  rep["graph_fallback_reason"] = graph_reason
901
+ if alloc_growth:
902
+ rep["alloc_growth_bytes_per_call"] = int(alloc_growth)
903
+ if host_suspect:
904
+ rep["host_overhead_suspected"] = True
905
+ rep["host_wall_over_event"] = round(wall_ms / max(ev_ms, 1e-9), 2)
906
  if bytes is not None:
907
  rep["achieved_gbs"] = round(bytes / (med * 1e-3) / 1e9, 1)
908
  if flops is not None:
 
912
  if dossier is not None and bytes and flops:
913
  bw = max(dossier["dram_bw"]["value"].values()) * 1e9
914
  mma_v = dossier["mma"]["value"].get(dtype)
915
+ if isinstance(mma_v, dict):
916
+ mma_v = mma_v.get("dense_equivalent")
917
  peak = (mma_v if isinstance(mma_v, (int, float))
918
  else dossier["fma_f32"]["value"]) * 1e12
919
  ai = flops / bytes
 
924
  "pct_of_roof": round(100 * (flops / (med * 1e-3)) / roof, 1),
925
  "peak_dtype": dtype,
926
  }
927
+ rep["verdict"] = _bench_verdict(rep)
928
  return rep
929
 
930
 
931
  def _to_ordered_int(t):
932
+ """Monotone integer mapping of float bit patterns; differences are ULP distances."""
 
 
933
  if t.dtype == torch.float32:
934
  u = t.view(torch.int32).to(torch.int64) & 0xFFFFFFFF
935
  half = 1 << 31
 
945
 
946
 
947
  def ulp_diff(a, b):
948
+ """Elementwise ULP distance; both-NaN counts 0, one-sided NaN 2**62, integers by absolute difference."""
949
+ if a.dtype != b.dtype or a.shape != b.shape:
950
+ raise ValueError("ulp_diff needs matching dtype and shape; got "
951
+ f"{a.dtype}{tuple(a.shape)} vs "
952
+ f"{b.dtype}{tuple(b.shape)}; cast or reshape the "
953
+ "candidate to the reference layout")
954
  if not a.is_floating_point():
955
  return (a.to(torch.int64) - b.to(torch.int64)).abs()
956
  ia, ib = _to_ordered_int(a), _to_ordered_int(b)
 
967
 
968
  @staticmethod
969
  def last_dim():
970
+ """Identical permutation of every arg's last dim: reduction order changes, value does not."""
 
 
971
  def shuffle(args, seed):
972
  g = torch.Generator(device=args[0].device).manual_seed(seed)
973
  p = torch.randperm(args[0].shape[-1], device=args[0].device,
 
977
 
978
  @staticmethod
979
  def matmul_k():
980
+ """Consistent K permutation of (A [.., M, K], B [.., K, N]): product unchanged, accumulation order not."""
 
981
  def shuffle(args, seed):
982
  a_m, b_m = args[0], args[1]
983
  g = torch.Generator(device=a_m.device).manual_seed(seed)
 
991
  return list(x) if isinstance(x, (tuple, list)) else [x]
992
 
993
 
994
+ def _compare_hints(trial):
995
+ hints = []
996
+ if trial.get("bias_one_sided_frac", 0) >= 0.9 and \
997
+ trial.get("frac_above_band", 0) > 0.5:
998
+ hints.append("systematic one-sided bias: check scale factors or "
999
+ "the accumulator dtype")
1000
+ if trial["nan_cand"] > trial["nan_ref"]:
1001
+ hints.append("candidate introduces NaN: check uninitialized "
1002
+ "memory or masking")
1003
+ nd = trial.get("first_divergent_nd")
1004
+ if nd and nd[-1] > 0 and any(nd[-1] % t == 0 for t in (16, 32, 64,
1005
+ 128)):
1006
+ hints.append("divergence starts on a tile-aligned index: check "
1007
+ "boundary indexing")
1008
+ return hints
1009
+
1010
+
1011
+ def compare(ref, cand, gen=None, trials=8, order_shuffle=None,
1012
+ order_trials=8, ref_variants=None, ulp_tol=None, atol=None,
1013
+ rtol=None, band_margin=4.0, zero_atol=None, worst_k=8, seed=0,
1014
+ inputs=None, frac_above_tol=0.5):
1015
+ """Adversarial correctness comparison with pointwise order bands, ULP evidence, and a one-sided-bias gate; the banded route cannot reject errors under band_margin*(band+2) ULP at order-stable elements."""
1016
+ if (gen is None) == (inputs is None):
1017
+ raise ValueError("pass exactly one of gen (a seed -> args "
1018
+ "callable) or inputs (a tuple of concrete "
1019
+ "tensors)")
1020
+ if inputs is not None:
1021
+ if isinstance(inputs, torch.Tensor):
1022
+ inputs = (inputs,)
1023
+ if not all(isinstance(x, torch.Tensor) for x in inputs):
1024
+ raise ValueError("inputs must be tensors; wrap scalars with "
1025
+ "torch.tensor first")
1026
+ fixed = tuple(inputs)
1027
+ gen = lambda seed: fixed # noqa: E731
1028
+ trials = 1
1029
  worst = {"max_ulp": -1}
1030
  total_hist = {}
1031
  per_trial = []
1032
  max_abs_seen = 0.0
1033
  max_rel_seen = 0.0
1034
+ out_shapes = []
1035
  for t in range(trials):
1036
  args = gen(seed + t)
1037
+ if not isinstance(args, (tuple, list)):
1038
+ raise ValueError("gen(seed) must return a tuple of args; got "
1039
+ f"{type(args).__name__}; return (x,) for a "
1040
+ "single-argument op")
1041
  r_outs = _as_outputs(ref(*args))
1042
  c_outs = _as_outputs(cand(*args))
1043
+ if len(r_outs) != len(c_outs):
1044
+ raise ValueError(f"reference returned {len(r_outs)} outputs, "
1045
+ f"candidate {len(c_outs)}; make the arities "
1046
+ "match")
1047
+ for oi, (r, c) in enumerate(zip(r_outs, c_outs)):
1048
+ if r.dtype != c.dtype:
1049
+ raise ValueError(f"output {oi} dtype mismatch: ref "
1050
+ f"{r.dtype}, cand {c.dtype}; cast the "
1051
+ "candidate to the reference dtype")
1052
+ if r.shape != c.shape:
1053
+ raise ValueError(f"output {oi} shape mismatch: ref "
1054
+ f"{tuple(r.shape)}, cand "
1055
+ f"{tuple(c.shape)}")
1056
+ out_shapes = [tuple(r.shape) for r in r_outs]
1057
+ # pointwise band: each element judged against its own measured order sensitivity
1058
  bands = None
1059
  band = 0
1060
  band_score = 0.0
 
1078
  max_ulp = 0
1079
  mean_ulp = 0.0
1080
  first_div = -1
1081
+ first_div_nd = None
1082
  n_el = 0
1083
  n_above_band = 0
1084
+ n_diverging = 0
1085
+ n_cand_high = 0
1086
  nan_r = nan_c = inf_r = inf_c = 0
1087
  worst_elements = []
1088
  for oi, (r, c) in enumerate(zip(r_outs, c_outs)):
 
1100
  mean_ulp += float(d.double().sum().item())
1101
  n_el += d.numel()
1102
  nz = (d > 0)
1103
+ n_diverging += int(nz.sum().item())
1104
+ n_cand_high += int(((c > r) & nz).sum().item())
1105
  if first_div < 0 and nz.any():
1106
  first_div = int(nz.flatten().nonzero()[0].item())
1107
+ first_div_nd = _unravel(first_div, r.shape)
1108
  if nz.any():
1109
  buckets = torch.clamp(
1110
  torch.log2(d[nz].double()).floor(), 0, 62
 
1121
  if val > 0:
1122
  worst_elements.append({
1123
  "output": oi, "index": int(ix),
1124
+ "index_nd": _unravel(int(ix), r.shape),
1125
  "ref": float(rf[ix].item()),
1126
  "cand": float(cf[ix].item()),
1127
  "ulp": int(val)})
 
1141
  inf_r += int(torch.isinf(r).sum().item())
1142
  inf_c += int(torch.isinf(c).sum().item())
1143
  worst_elements.sort(key=lambda w: -w["ulp"])
1144
+ frac_pos = n_cand_high / max(n_diverging, 1)
1145
  trial_rep = {
1146
  "max_ulp": max_ulp,
1147
  "mean_ulp": round(mean_ulp / max(n_el, 1), 3),
1148
  "first_divergent_index": first_div,
1149
+ "first_divergent_nd": first_div_nd,
1150
  "nan_ref": nan_r, "nan_cand": nan_c,
1151
  "inf_ref": inf_r, "inf_cand": inf_c,
1152
  "order_band_ulp": band,
1153
  "band_score": round(band_score, 2),
1154
  "frac_above_band": round(n_above_band / max(n_el, 1), 6),
1155
+ "bias_one_sided_frac": round(max(frac_pos, 1 - frac_pos), 4)
1156
+ if n_diverging else 0.0,
1157
  "worst_elements": worst_elements[:worst_k],
1158
  }
1159
  per_trial.append(trial_rep)
1160
  if max_ulp > worst["max_ulp"]:
1161
  worst = trial_rep
 
1162
  max_seen = max(t["max_ulp"] for t in per_trial)
1163
+ banded = order_shuffle is not None or ref_variants
1164
+ if banded:
1165
  score = max(t["band_score"] for t in per_trial)
1166
  ulp_ok = score <= band_margin
1167
  criterion = (f"pointwise max(ulp / (order_band + 2)) = "
 
1176
  max_rel_seen <= (rtol or 0) or max_abs_seen <= (atol or 0)
1177
  criterion += (f"; OR max_abs {max_abs_seen:.3e} <= atol {atol} / "
1178
  f"max_rel {max_rel_seen:.3e} <= rtol {rtol}")
1179
+ # one-sided bias gate: a uniform small error is above-band nearly everywhere in one direction
1180
+ bias_fail = False
1181
+ if banded and (ulp_ok or tol_ok):
1182
+ w_frac = max(t["frac_above_band"] for t in per_trial)
1183
+ w_side = max(t["bias_one_sided_frac"] for t in per_trial)
1184
+ if w_frac > frac_above_tol and w_side >= 0.9:
1185
+ bias_fail = True
1186
+ criterion += ("; FAILED bias gate: frac_above_band %.3f > %s "
1187
+ "with one-sided fraction %.2f" %
1188
+ (w_frac, frac_above_tol, w_side))
1189
+ passed = bool((ulp_ok or tol_ok) and not bias_fail)
1190
+ hints = [] if passed else _compare_hints(worst)
1191
+ if passed:
1192
+ verdict = "pass: " + (
1193
+ f"max {max_seen} ULP within the order band" if banded
1194
+ else f"max {max_seen} ULP")
1195
+ elif bias_fail:
1196
+ verdict = ("fail: uniform bias, %.0f%% of elements above their "
1197
+ "order band in one direction" %
1198
+ (100 * max(t["frac_above_band"] for t in per_trial)))
1199
+ else:
1200
+ w0 = worst["worst_elements"][0] if worst["worst_elements"] else None
1201
+ verdict = "fail: max %d ULP" % max_seen + (
1202
+ " at index %s (ref %.6g, cand %.6g)" %
1203
+ (w0["index_nd"], w0["ref"], w0["cand"]) if w0 else "")
1204
+ rep = {
1205
+ "pass": passed,
1206
+ "verdict": verdict,
1207
  "criterion": criterion,
1208
  "worst_trial": worst,
1209
  "max_abs": max_abs_seen,
 
1211
  "trials": per_trial,
1212
  "ulp_hist_log2": dict(sorted(total_hist.items())),
1213
  }
1214
+ if hints:
1215
+ rep["hints"] = hints
1216
+ return rep
1217
 
1218
 
1219
  _FUZZ_VARIANTS = ("contig", "transposed", "strided_rows", "offset_slice",
 
1229
  return False
1230
 
1231
 
1232
+ class _RandnMaker:
1233
+ """Picklable default maker: `arity` same-shape randn tensors (zeros for empty shapes)."""
1234
+
1235
+ def __init__(self, arity=1):
1236
+ self.arity = arity
1237
+
1238
+ def __call__(self, shape, dtype=torch.float32):
1239
+ n = math.prod(shape)
1240
+ if n:
1241
+ out = tuple(torch.randn(tuple(shape), device="cuda",
1242
+ dtype=dtype)
1243
+ for _ in range(self.arity))
1244
+ else:
1245
+ out = tuple(torch.zeros(tuple(shape), device="cuda",
1246
+ dtype=dtype)
1247
+ for _ in range(self.arity))
1248
+ return out if self.arity > 1 else out[0]
1249
+
1250
+
1251
+ def _default_make(arity=1):
1252
+ """Default maker instance; a class so isolate=True can transport it."""
1253
+ return _RandnMaker(arity)
1254
+
1255
+
1256
  def _fuzz_build(make, shape, dtype):
1257
  try:
1258
  out = make(tuple(shape), dtype)
 
1269
  if v == "strided_rows":
1270
  return x[::2] if x.dim() >= 1 and x.shape[0] >= 2 else x
1271
  if v == "offset_slice":
1272
+ # storage-offset-1 view: misaligned for vectorized loads
1273
  return x.flatten()[1:] if x.numel() > 1 else x
1274
  if v == "overflow_expand":
1275
  if x.numel() == 0:
 
1283
 
1284
  def _fuzz_group(op, ref, make, shape, dtype, variants, ulp_tol, device,
1285
  stop_on_context_loss=True):
1286
+ """All variant cases for one (shape, dtype) group; returns (rows, context_lost)."""
 
1287
  rows = []
1288
  bases = _fuzz_build(make, shape, dtype)
1289
  lost = False
 
1320
  got = op(*xs)
1321
  want = ref(*xs)
1322
  if v == "overflow_expand":
1323
+ # bounded comparison: head plus the high-index tail where int32 indexing wraps
 
1324
  w = 1 << 20
1325
  gf, wf = got.flatten(), want.flatten()
1326
  pairs = ((gf[:w], wf[:w]), (gf[-w:], wf[-w:]))
 
1345
  return rows, lost
1346
 
1347
 
1348
+ def _fn_spec(fn, name):
1349
+ """Picklable transport spec for a callable, with a source fallback for local defs."""
1350
+ import pickle
1351
+ try:
1352
+ return ("pickle", pickle.dumps(fn), None)
1353
+ except Exception: # noqa: BLE001
1354
+ import inspect
1355
+ import textwrap
1356
+ try:
1357
+ src = textwrap.dedent(inspect.getsource(fn))
1358
+ except (OSError, TypeError):
1359
+ src = ""
1360
+ if not src.startswith("def "):
1361
+ raise ValueError(
1362
+ f"{name} is not picklable and not a plain def; define "
1363
+ "op/ref/make as top-level functions (not lambdas or "
1364
+ "closures) to use isolate=True") from None
1365
+ return ("source", src, fn.__name__)
1366
+
1367
+
1368
+ _POOL_BOOTSTRAP = r"""
1369
+ import json, os, pickle, sys
1370
  meta = json.load(open(sys.argv[1], "r"))
1371
+ sys.path[:0] = meta["sys_path"]
1372
  try:
1373
  import kernels
1374
  _mk = kernels.get_kernel(meta["repo"], version=meta["version"],
 
1381
  import load_local
1382
  _mk = load_local.load()
1383
  import torch
1384
+ _ns = {"torch": torch}
1385
+ def _resolve(spec):
1386
+ kind, payload, name = spec
1387
+ if kind == "pickle":
1388
+ return pickle.loads(payload)
1389
+ exec(payload, _ns)
1390
+ return _ns[name]
1391
+ _op, _ref, _make = (_resolve(s) for s in
1392
+ pickle.load(open(meta["fns"], "rb")))
1393
+ print("ready", flush=True)
1394
+ for line in sys.stdin:
1395
+ job = json.loads(line)
1396
+ rows, _l = _mk._fuzz_group(
1397
+ _op, _ref, _make, tuple(job["shape"]),
1398
+ getattr(torch, job["dtype"]), tuple(job["variants"]),
1399
+ job["ulp_tol"], job["device"], stop_on_context_loss=False)
1400
+ pickle.dump(rows, open(job["out"], "wb"))
1401
+ print("done", flush=True)
1402
  """
1403
 
1404
 
1405
+ class _FuzzPool:
1406
+ """One persistent isolation worker fed groups over stdin, respawned only after a crash."""
1407
+
1408
+ def __init__(self, meta_path, stderr_path, timeout_s):
1409
+ self.meta_path = meta_path
1410
+ self.stderr_path = stderr_path
1411
+ self.timeout_s = timeout_s
1412
+ self.proc = None
1413
+ self.q = None
1414
+
1415
+ def _spawn(self):
1416
+ import queue
1417
+ import subprocess
1418
+ import sys as _sys
1419
+ import threading
1420
+ self.errf = open(self.stderr_path, "ab")
1421
+ self.proc = subprocess.Popen(
1422
+ [_sys.executable, "-c", _POOL_BOOTSTRAP, self.meta_path],
1423
+ stdin=subprocess.PIPE, stdout=subprocess.PIPE,
1424
+ stderr=self.errf, text=True)
1425
+ self.q = queue.Queue()
1426
+
1427
+ def reader(p, q):
1428
+ for line in p.stdout:
1429
+ q.put(line.strip())
1430
+ threading.Thread(target=reader, args=(self.proc, self.q),
1431
+ daemon=True).start()
1432
+ try:
1433
+ first = self.q.get(timeout=self.timeout_s)
1434
+ except Exception: # noqa: BLE001
1435
+ first = None
1436
+ return first == "ready"
1437
+
1438
+ def run(self, job_line):
1439
+ """Returns True when the worker reports done, False on crash or timeout."""
1440
+ import queue
1441
+ if self.proc is None or self.proc.poll() is not None:
1442
+ if not self._spawn():
1443
+ return False
1444
+ try:
1445
+ self.proc.stdin.write(job_line + "\n")
1446
+ self.proc.stdin.flush()
1447
+ except OSError:
1448
+ return False
1449
+ try:
1450
+ return self.q.get(timeout=self.timeout_s) == "done"
1451
+ except queue.Empty:
1452
+ if self.proc.poll() is None:
1453
+ self.proc.kill()
1454
+ return False
1455
+
1456
+ def stderr_tail(self):
1457
+ try:
1458
+ self.errf.flush()
1459
+ with open(self.stderr_path, "rb") as f:
1460
+ return f.read()[-400:].decode(errors="replace")
1461
+ except OSError:
1462
+ return ""
1463
+
1464
+ def close(self):
1465
+ if self.proc is not None and self.proc.poll() is None:
1466
+ self.proc.kill()
1467
+ try:
1468
+ self.errf.close()
1469
+ except Exception: # noqa: BLE001
1470
+ pass
1471
+
1472
+
1473
+ def _fuzz_hints(rows):
1474
+ hints = []
1475
+ bad = [c for c in rows if c["status"] == "mismatch"]
1476
+ if bad and all(c["variant"] in ("transposed", "strided_rows",
1477
+ "offset_slice") for c in bad):
1478
+ hints.append("mismatches only on non-contiguous variants: the op "
1479
+ "assumes contiguity; call .contiguous() or honor "
1480
+ "strides")
1481
+ if any(c["variant"] == "overflow_expand" and
1482
+ c["status"] == "mismatch" for c in rows):
1483
+ hints.append("overflow_expand mismatch: int32 indexing wraps past "
1484
+ "2^31 elements; use 64-bit index arithmetic")
1485
+ return hints
1486
+
1487
+
1488
+ def fuzz_shapes(op, ref, make=None, shapes=None, variants=_FUZZ_VARIANTS,
1489
  dtypes=(torch.float32,), ulp_tol=64, isolate=False,
1490
+ isolate_timeout_s=180, device="cuda", arity=1):
1491
+ """Edge shapes, dtypes, and layout mutations with per-case repro recipes; isolate=True survives context-killing candidates via a pooled worker."""
 
 
 
 
 
 
 
 
 
 
 
 
1492
  if shapes is None:
1493
  shapes = [(), (7,), (0, 8), (1, 1), (1, 17), (3, 63), (17, 129),
1494
  (128, 128), (1023, 5), (2, 4096), (2, 3, 5)]
1495
+ if make is None:
1496
+ make = _default_make(arity)
1497
 
1498
  rows = []
1499
  lost = False
1500
  if isolate:
1501
  import json
1502
  import pickle
 
1503
  import sys as _sys
1504
  import tempfile
1505
  tmpdir = tempfile.mkdtemp(prefix="mk_fuzz_")
1506
  fns = os.path.join(tmpdir, "fns.pkl")
1507
+ specs = [_fn_spec(f, n) for f, n in ((op, "op"), (ref, "ref"),
1508
+ (make, "make"))]
1509
  with open(fns, "wb") as f:
1510
+ pickle.dump(specs, f)
1511
+ meta_path = os.path.join(tmpdir, "meta.json")
1512
+ with open(meta_path, "w", encoding="utf-8") as f:
1513
+ json.dump({"sys_path": [p for p in _sys.path
1514
+ if isinstance(p, str)],
1515
+ "repo": _REPO_ID, "version": _REPO_VERSION,
1516
+ "fns": fns}, f)
1517
+ pool = _FuzzPool(meta_path, os.path.join(tmpdir, "stderr.log"),
1518
+ isolate_timeout_s)
1519
+ try:
1520
+ for dtype in dtypes:
1521
+ for shape in shapes:
1522
+ out = os.path.join(tmpdir, "rows.pkl")
1523
+ if os.path.exists(out):
1524
+ os.remove(out)
1525
+ job = json.dumps({
1526
  "shape": list(shape),
1527
  "dtype": str(dtype).replace("torch.", ""),
1528
  "variants": list(variants),
1529
  "ulp_tol": ulp_tol, "device": device,
1530
+ "out": out})
1531
+ ok = pool.run(job)
1532
+ if ok and os.path.exists(out):
 
 
 
 
 
 
1533
  with open(out, "rb") as f:
1534
+ rows.extend(pickle.load(f))
1535
+ else:
1536
+ rows.append({
1537
+ "shape": tuple(shape), "variant": "*",
1538
+ "dtype": str(dtype).replace("torch.", ""),
1539
+ "status": "crashed_isolated",
1540
+ "stderr_tail": pool.stderr_tail()})
1541
+ finally:
1542
+ pool.close()
 
 
 
 
1543
  else:
1544
  pending = [(dt, sh) for dt in dtypes for sh in shapes]
1545
  for gi, (dtype, shape) in enumerate(pending):
 
1556
  n_bad = sum(1 for r in rows
1557
  if r["status"] not in ("ok", "skipped_vram",
1558
  "skipped_empty"))
1559
+ rep = {"cases": rows, "failed": n_bad, "total": len(rows),
1560
+ "context_lost": lost}
1561
+ hints = _fuzz_hints(rows)
1562
+ if hints:
1563
+ rep["hints"] = hints
1564
+ if n_bad == 0:
1565
+ rep["verdict"] = "pass: %d/%d cases ok" % (len(rows) - n_bad,
1566
+ len(rows))
1567
+ else:
1568
+ worst_case = next(c for c in rows
1569
+ if c["status"] not in ("ok", "skipped_vram",
1570
+ "skipped_empty"))
1571
+ rep["verdict"] = "fail: %d/%d cases (%s at %s %s)" % (
1572
+ n_bad, len(rows), worst_case["status"], worst_case["shape"],
1573
+ worst_case["variant"])
1574
+ return rep
1575
 
1576
 
1577
  def sweep(factory, grid, iters=30, warmup=5, bytes=None, flops=None,
1578
  check=None, budget_s=None, race=False):
1579
+ """Launch-configuration search, fastest first, with a correctness gate per row and no silently dropped points."""
 
 
 
 
 
 
 
 
1580
  keys = list(grid.keys())
1581
 
1582
  def expand(i, acc):
 
1641
  continue
1642
  rows.append(run_point(p, iters))
1643
  rows.sort(key=lambda r: (not r.get("check", True), r["median_ms"]))
1644
+ if rows:
1645
+ n_pruned = sum(1 for r in rows if r.get("status") == "pruned_race")
1646
+ n_skip = sum(1 for r in rows if r.get("status") == "not_run")
1647
+ best = rows[0]
1648
+ rows_verdict = "fastest %s at %.4g ms" % (
1649
+ {k: best[k] for k in keys}, best["median_ms"])
1650
+ if n_pruned:
1651
+ rows_verdict += "; %d pruned" % n_pruned
1652
+ if n_skip:
1653
+ rows_verdict += "; %d not run" % n_skip
1654
+ best["verdict"] = rows_verdict
1655
  return rows
1656
 
1657
 
1658
+ def judge(cand, ref, inputs=None, gen=None, bytes=None, flops=None,
1659
+ dtype=None, order_shuffle=None, make=None, quick_fuzz=True,
1660
+ device="cuda"):
1661
+ """One-call verdict: compare, a quick fuzz, and a roofline bench against the cached dossier."""
1662
+ if (gen is None) == (inputs is None):
1663
+ raise ValueError("pass exactly one of inputs (concrete tensors) "
1664
+ "or gen (seed -> args)")
1665
+ if inputs is not None and isinstance(inputs, torch.Tensor):
1666
+ inputs = (inputs,)
1667
+ args0 = tuple(inputs) if inputs is not None else gen(0)
1668
+ arity = len(args0)
1669
+ if dtype is None:
1670
+ dt0 = args0[0].dtype
1671
+ dtype = {torch.float16: "fp16", torch.bfloat16: "bf16"}.get(
1672
+ dt0, "f32")
1673
+ dossier = probe_device(device, quick=True, cached=True,
1674
+ tenancy_check=False)
1675
+ cmp_rep = compare(ref, cand, gen=gen, inputs=inputs, trials=4,
1676
+ order_shuffle=order_shuffle)
1677
+ fuzz_rep = None
1678
+ if quick_fuzz:
1679
+ fuzz_rep = fuzz_shapes(
1680
+ cand, ref, make=make, arity=arity,
1681
+ shapes=[(1, 17), (3, 63), (128, 128)],
1682
+ variants=("contig", "transposed", "offset_slice"),
1683
+ dtypes=(args0[0].dtype,))
1684
+ bench_rep = None
1685
+ if inputs is not None:
1686
+ bench_rep = bench(lambda: cand(*inputs), iters=32, warmup=6,
1687
+ bytes=bytes, flops=flops, dossier=dossier,
1688
+ dtype=dtype, device=device)
1689
+ passed = cmp_rep["pass"] and (fuzz_rep is None or
1690
+ fuzz_rep["failed"] == 0)
1691
+ parts = [cmp_rep["verdict"]]
1692
+ if fuzz_rep is not None:
1693
+ parts.append(fuzz_rep["verdict"])
1694
+ if bench_rep is not None:
1695
+ parts.append(bench_rep["verdict"])
1696
+ rep = {"pass": passed, "verdict" : " | ".join(parts),
1697
+ "compare": cmp_rep, "fuzz": fuzz_rep, "bench": bench_rep,
1698
+ "device": dossier["device"]["name"]}
1699
+ hints = list(cmp_rep.get("hints", []))
1700
+ if fuzz_rep is not None:
1701
+ hints += fuzz_rep.get("hints", [])
1702
+ if hints:
1703
+ rep["hints"] = hints
1704
+ return rep
1705
+
1706
+
1707
  def alloc_stamps(n, blocks=None, device="cuda"):
1708
+ """Device buffer for MK_STAMP (1-D) or MK_STAMP_BLOCK ([blocks, n]) stamps."""
 
 
1709
  if blocks is not None:
1710
  return torch.zeros(blocks, n, dtype=torch.int64, device=device)
1711
  return torch.zeros(n, dtype=torch.int64, device=device)
 
1715
 
1716
 
1717
  def pinned_dossiers():
1718
+ """Names of the release-pinned dossiers, one per certified architecture."""
 
1719
  return sorted(_PINNED.keys())
1720
 
1721
 
 
1734
 
1735
 
1736
  def dossier_drift(current, pinned=None, rel_tol=0.35, skip=_DRIFT_SKIP):
1737
+ """Per-leaf relative drift of a fresh dossier against the release pin for the same architecture."""
 
 
 
 
1738
  if pinned is None:
1739
  arch = current["device"]["sm_arch"]
1740
  matches = [k for k, v in _PINNED.items()
 
1757
  "rel": round(rel, 4)})
1758
  worst = max((e["rel"] for e in entries), default=0.0)
1759
  over = [e for e in entries if e["rel"] > rel_tol]
1760
+ rep = {"entries": entries, "worst_rel": worst, "over_tol": over,
1761
+ "rel_tol": rel_tol, "compared": len(entries),
1762
+ "pass": len(over) == 0}
1763
+ rep["verdict"] = ("pass: worst drift %.3f over %d entries" %
1764
+ (worst, len(entries))) if rep["pass"] else \
1765
+ ("fail: %d entries over %.2f, worst %s at %.3f" %
1766
+ (len(over), rel_tol, over[0]["path"], worst))
1767
+ return rep
1768
 
1769
 
1770
  def stamped_demo(n=1 << 22, fma_iters=4000, blocks=None, threads=256,
1771
  device="cuda"):
1772
+ """Run the shipped MK_STAMP_BLOCK example (load, compute, drained store) and decode its stamps."""
 
 
1773
  props = torch.cuda.get_device_properties(device)
1774
  if blocks is None:
1775
  blocks = props.multi_processor_count
 
1782
 
1783
 
1784
  def read_stamps(stamps, labels=None, clock_ghz=None):
1785
+ """Decode MK_STAMP deltas to ticks and microseconds; 2-D buffers yield min/median/max across blocks."""
 
 
 
 
1786
  if clock_ghz is None:
1787
  clock_ghz = measure_clock(stamps.device)
1788
 
 
1818
  return {"phases": phases, "clock_ghz": round(clock_ghz, 3),
1819
  "blocks_reporting": int(live.sum().item()),
1820
  "blocks_total": int(live.numel())}
1821
+
1822
+
1823
+ _SCHEMAS = {
1824
+ "probe_device": {
1825
+ "device": "name, sm_arch, sms, l2_bytes, vram_bytes, torch, "
1826
+ "cuda_runtime, ecc, driver, power_limit_w",
1827
+ "clock": "max/min/final GHz under sustained FMA load plus sag_pct",
1828
+ "dram_bw": "GB/s: triad_4B/8B/16B, read_only, write_only, "
1829
+ "random_gather_16B",
1830
+ "l2_bw": "GB/s over an L2-resident working set",
1831
+ "latency": "cycles: smem, l1, l2, dram (min of 3 chases) plus "
1832
+ "ns_per_cycle",
1833
+ "fma_f32 / fma_f64": "TFLOP/s of the register-resident pipes",
1834
+ "fma_ilp": "TFLOP/s at 1/2/4 chains plus ratio_4_1; near 4 means "
1835
+ "latency-bound",
1836
+ "mma": "TFLOP/s per dtype; sparse carries executed and "
1837
+ "dense_equivalent; verified_k_lanes carries "
1838
+ "executed_k_per_iter, vs_nominal, calibration_linear; "
1839
+ "fp8_accum carries the accumulate-precision stall",
1840
+ "mma_fed": "TFLOP/s of smem-staged fp16 tiles (staging exposed)",
1841
+ "smem_bw": "GB/s aggregate, conflict-light pattern",
1842
+ "smem_conflict_penalty": "time ratio vs stride 1 per lane stride",
1843
+ "transfer_bw": "GB/s: h2d/d2h pinned and pageable, d2d",
1844
+ "atomics_f32": "Gop/s curve over distinct addresses plus "
1845
+ "shared_same_addr",
1846
+ "launch_overhead": "us: torch_queued, torch_round_trip, "
1847
+ "raw_queued, raw_round_trip",
1848
+ "grid_barrier": "us per grid.sync or 'unsupported'",
1849
+ "occupancy_blocks_per_sm": "API table by (threads, dyn smem)",
1850
+ "occupancy_knees_fma_tflops": "timed dependent-chain table",
1851
+ },
1852
+ "bench": {
1853
+ "median_ms": "median over accepted samples",
1854
+ "sustained_ms": "median over all samples",
1855
+ "median_ci95_ms / p05_ms / p95_ms / min_ms / mean_ms":
1856
+ "distribution of accepted samples",
1857
+ "samples / rejected_throttled / rejection_basis":
1858
+ "counts and whether rejection used sm_clock or "
1859
+ "bandwidth_consistency",
1860
+ "no_clean_window": "present when every window ran throttled; the "
1861
+ "median is then a throttled-regime number",
1862
+ "clock_ghz": "best and worst SM clock probes around the run",
1863
+ "achieved_gbs / achieved_tflops / arithmetic_intensity":
1864
+ "rates from declared traffic",
1865
+ "roofline": "bound, ridge_flops_per_byte, pct_of_roof, peak_dtype",
1866
+ "graph_captured / graph_fallback_reason": "CUDA-graph state",
1867
+ "alloc_growth_bytes_per_call": "present when fn leaks allocations",
1868
+ "host_overhead_suspected": "present when fn syncs or does host "
1869
+ "work inside; ops under ~50 us are "
1870
+ "below the detection floor",
1871
+ "verdict": "one-sentence summary",
1872
+ },
1873
+ "compare": {
1874
+ "pass": "final verdict incl. the one-sided bias gate",
1875
+ "verdict / criterion / hints": "summary, acceptance rule, and "
1876
+ "failure-pattern suggestions",
1877
+ "worst_trial": "max_ulp, mean_ulp, first_divergent_index(+_nd), "
1878
+ "NaN/Inf census, order_band_ulp, band_score, "
1879
+ "frac_above_band, bias_one_sided_frac, "
1880
+ "worst_elements (index, index_nd, ref, cand, ulp)",
1881
+ "max_abs / max_rel / ulp_hist_log2": "aggregate evidence",
1882
+ "floor": "the banded route cannot reject errors under "
1883
+ "band_margin*(band+2) ULP at order-stable elements",
1884
+ },
1885
+ "fuzz_shapes": {
1886
+ "cases": "per-case shape, variant, dtype, status, repro recipe",
1887
+ "status values": "ok | mismatch | exception | context_killed | "
1888
+ "context_lost | crashed_isolated | skipped_vram "
1889
+ "| skipped_empty",
1890
+ "failed / total / context_lost / verdict / hints": "aggregates",
1891
+ },
1892
+ "sweep": {
1893
+ "rows": "grid point plus median_ms, rejected_throttled, check?, "
1894
+ "achieved rates, status in pruned_race | not_run",
1895
+ "verdict": "on the fastest row",
1896
+ },
1897
+ "dossier_drift": {
1898
+ "entries": "path, current, pinned, rel per intersecting leaf",
1899
+ "worst_rel / over_tol / compared / pass / verdict": "aggregates",
1900
+ },
1901
+ "judge": {
1902
+ "pass / verdict / hints": "composite over compare, fuzz, bench",
1903
+ "compare / fuzz / bench": "the full sub-reports",
1904
+ },
1905
+ "read_stamps": {
1906
+ "phases": "ticks and us per phase; 2-D input adds min/median/max "
1907
+ "across blocks",
1908
+ "note": "stamps record issue; DRAIN variants include the store "
1909
+ "drain to the coherence point",
1910
+ },
1911
+ }
1912
+
1913
+
1914
+ def schema(name=None):
1915
+ """Field layout of every report, so an agent can look up what a number means."""
1916
+ if name is None:
1917
+ return _SCHEMAS
1918
+ if name not in _SCHEMAS:
1919
+ raise KeyError(f"no schema for {name!r}; have "
1920
+ f"{sorted(_SCHEMAS)}")
1921
+ return _SCHEMAS[name]
build/{torch212-cxx11-cu130-x86_64-linux/_metakernel_cuda_b62a1ce.abi3.so → torch212-cxx11-cu132-x86_64-linux/_metakernel_cuda_0b9de42.abi3.so} RENAMED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:d58e8d3c6c8341efcdd44e58bd6b7ee088c150f7887e65e74b8199afed4a52da
3
- size 1180120
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:5db8a870d3df57f2fab7ca97d0d323183a1985635ee09af76797864ecd65520e
3
+ size 1438880
build/torch212-cxx11-cu132-x86_64-linux/_ops.py CHANGED
@@ -1,9 +1,9 @@
1
  import torch
2
- from . import _metakernel_cuda_b62a1ce
3
- ops = torch.ops._metakernel_cuda_b62a1ce
4
 
5
  def add_op_namespace_prefix(op_name: str):
6
  """
7
  Prefix op by namespace.
8
  """
9
- return f"_metakernel_cuda_b62a1ce::{op_name}"
 
1
  import torch
2
+ from . import _metakernel_cuda_0b9de42
3
+ ops = torch.ops._metakernel_cuda_0b9de42
4
 
5
  def add_op_namespace_prefix(op_name: str):
6
  """
7
  Prefix op by namespace.
8
  """
9
+ return f"_metakernel_cuda_0b9de42::{op_name}"
build/torch212-cxx11-cu132-x86_64-linux/_pinned.py CHANGED
@@ -1,8 +1,4 @@
1
- """Release-pinned device dossiers, one per certified architecture.
2
-
3
- Generated by dev/pin_dossier.py from full probe_device() runs on idle,
4
- single-tenant cards; consumed by metakernel.dossier_drift.
5
- """
6
 
7
  PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
8
  'confidence': 'high',
@@ -295,7 +291,7 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
295
  'd2h_pinned': 10.8,
296
  'h2d_pageable': 10.3,
297
  'h2d_pinned': 11.8}}},
298
- 'sm89_nvidiartx6000adagenerati': {'atomics_f32': {'clock_ghz': 2.706,
299
  'confidence': 'high',
300
  'method': 'atomicAdd '
301
  'hammer; curve '
@@ -308,21 +304,21 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
308
  'contention) to '
309
  'one per thread',
310
  'units': 'Gop/s',
311
- 'value': {'curve_slots': {'1': 0.72,
312
- '145408': 483.54,
313
- '4096': 209.84,
314
- '512': 55.6,
315
- '64': 13.82,
316
- '8': 5.77},
317
- 'shared_same_addr': 12.88}},
318
  'clock': {'confidence': 'high',
319
- 'final_ghz': 2.699,
320
- 'max_ghz': 2.706,
321
  'method': '24 back-to-back FMA '
322
  'spin windows of 30M '
323
  'ticks; wall/ticks '
324
  'per window',
325
- 'min_ghz': 2.501,
326
  'sag_pct': 0.3,
327
  'units': 'GHz'},
328
  'device': {'cuda_runtime': '12.6',
@@ -336,7 +332,7 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
336
  'sms': 142,
337
  'torch': '2.10.0+cu126',
338
  'vram_bytes': 48305274880},
339
- 'dram_bw': {'clock_ghz': 2.706,
340
  'confidence': 'high',
341
  'method': 'streaming triad '
342
  'c=a+s*b per access '
@@ -356,26 +352,41 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
356
  'line-efficiency '
357
  'claim',
358
  'units': 'GB/s',
359
- 'value': {'random_gather_16B': 218.5,
360
- 'read_only': 771.0,
361
- 'triad_16B': 663.1,
362
- 'triad_4B': 638.3,
363
- 'triad_8B': 705.3,
364
  'write_only': 766.5}},
365
- 'fma_f32': {'clock_ghz': 2.706,
366
  'confidence': 'high',
367
  'method': 'register-resident '
368
  'FMA chains',
369
  'units': 'TFLOP/s',
370
- 'value': 90.85},
371
- 'fma_f64': {'clock_ghz': 2.706,
372
  'confidence': 'high',
373
  'method': 'register-resident '
374
  'double FMA chains '
375
  '(the DFMA pipe)',
376
  'units': 'TFLOP/s',
377
- 'value': 1.282},
378
- 'grid_barrier': {'clock_ghz': 2.706,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
379
  'confidence': 'high',
380
  'method': 'cooperative '
381
  'kernel, 142 '
@@ -383,8 +394,8 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
383
  'threads',
384
  'units': 'us per '
385
  'grid.sync',
386
- 'value': 0.54},
387
- 'l2_bw': {'clock_ghz': 2.706,
388
  'confidence': 'medium',
389
  'method': 'in-kernel repeated '
390
  'triad passes over 72 '
@@ -394,14 +405,15 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
394
  'from HBM; amortized '
395
  'over 64 passes',
396
  'units': 'GB/s',
397
- 'value': 9325.3},
398
- 'latency': {'clock_ghz': 2.687,
399
  'confidence': 'high',
400
  'method': 'Sattolo-cycle '
401
- 'pointer chase; 16 '
402
- 'KB ring for L1, 2 '
403
- 'MB TLB-resident '
404
- 'ring for L2, '
 
405
  'self-evicting '
406
  'chain (2x L2 in '
407
  'touched lines) for '
@@ -413,40 +425,58 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
413
  'footprints',
414
  'units': 'cycles (single-lane '
415
  'dependent loads)',
416
- 'value': {'dram_cycles': 649.4,
417
  'l1_cycles': 45.8,
418
- 'l2_cycles': 291.5,
419
  'ns_per_cycle': 0.372,
420
  'smem_cycles': 30.0}},
421
- 'launch_overhead': {'clock_ghz': 2.706,
422
- 'confidence': 'high',
423
  'method': 'empty-kernel '
424
- 'trains, '
425
- 'queued vs '
426
- 'launch+sync',
427
- 'note': 'host-dependent: '
428
- 'includes '
429
- 'torch op '
430
- 'dispatch',
 
 
 
 
 
 
 
 
 
 
 
 
431
  'units': 'us',
432
- 'value': {'queued_us': 5.55,
433
- 'round_trip_us': 14.1}},
434
- 'mma': {'clock_ghz': 2.706,
 
 
435
  'confidence': 'medium',
 
 
 
 
436
  'method': 'register-fragment '
437
  'mma_sync loops (wmma; '
438
  'fp8 and 2:4-sparse '
439
  'fp16 via inline PTX '
440
- 'm16n8k32; sparse '
441
- 'counted '
442
- 'dense-equivalent, the '
443
- 'vendor convention); '
444
- 'flops derive from the '
445
- 'mean accumulator '
446
- 'read-back with every '
447
- 'fragment element '
448
- 'observed, never the '
449
- 'declared loop shape',
450
  'note': 'dense-tile rate without '
451
  'memory traffic; real '
452
  'kernels see this only '
@@ -454,25 +484,54 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
454
  'behind it',
455
  'units': 'TFLOP/s (TOP/s for '
456
  'int8)',
457
- 'value': {'bf16': 365.5,
458
- 'fp16': 375.9,
459
- 'fp16_sparse_2_4': 713.4,
460
- 'fp8_e4m3': 193.7,
461
- 'int8': 770.4,
462
- 'tf32': 177.1},
463
- 'verified_k_lanes': {'bf16': {'executed_k_per_iter': 64.0,
 
 
 
464
  'vs_nominal': 1.0},
465
- 'fp16': {'executed_k_per_iter': 64.0,
 
466
  'vs_nominal': 1.0},
467
- 'fp16_sparse_2_4': {'executed_k_per_iter': 128.0,
 
468
  'vs_nominal': 1.0},
469
- 'fp8_e4m3': {'executed_k_per_iter': 64.0,
470
- 'vs_nominal': 0.25},
471
- 'int8': {'executed_k_per_iter': 64.0,
 
 
 
 
 
472
  'vs_nominal': 1.0},
473
- 'tf32': {'executed_k_per_iter': 32.0,
 
474
  'vs_nominal': 1.0}}},
475
- 'occupancy_blocks_per_sm': {'clock_ghz': 2.706,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
476
  'confidence': 'high',
477
  'method': 'occupancy '
478
  'API '
@@ -501,7 +560,7 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
501
  '16KB': 3,
502
  '32KB': 3,
503
  '48KB': 2}}},
504
- 'occupancy_knees_fma_tflops': {'clock_ghz': 2.706,
505
  'confidence': 'high',
506
  'method': 'a '
507
  'single '
@@ -521,17 +580,6 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
521
  'fixed '
522
  'total '
523
  'work; '
524
- 'throughput '
525
- 'is '
526
- 'proportional '
527
- 'to '
528
- 'resident '
529
- 'threads '
530
- 'until '
531
- 'the '
532
- 'pipe '
533
- 'saturates, '
534
- 'so '
535
  'the '
536
  'cliff '
537
  'marks '
@@ -549,15 +597,15 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
549
  'above',
550
  'units': 'TFLOP/s',
551
  'value': {'t1024': {'0KB': 75.5,
552
- '32KB': 73.66,
553
- '48KB': 74.95},
554
- 't128': {'0KB': 79.86,
555
  '32KB': 60.31,
556
- '48KB': 44.57},
557
- 't256': {'0KB': 81.69,
558
- '32KB': 79.48,
559
- '48KB': 71.47}}},
560
- 'smem_bw': {'clock_ghz': 2.706,
561
  'confidence': 'medium',
562
  'method': 'block-resident '
563
  'read+write '
@@ -566,12 +614,56 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
566
  'aggregated across '
567
  'SMs',
568
  'note': 'bank-conflict-light '
569
- 'access pattern; a '
570
- 'conflicted pattern '
571
- 'can be far lower',
 
572
  'units': 'GB/s',
573
- 'value': 21250.0},
574
- 'transfer_bw': {'clock_ghz': 2.706,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
575
  'confidence': 'high',
576
  'method': '256 MB torch '
577
  'copies, '
@@ -583,8 +675,8 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
583
  'direction of '
584
  'traffic',
585
  'units': 'GB/s',
586
- 'value': {'d2d': 330.7,
587
- 'd2h_pageable': 15.6,
588
- 'd2h_pinned': 26.2,
589
- 'h2d_pageable': 16.6,
590
  'h2d_pinned': 24.0}}}}
 
1
+ '''Release-pinned device dossiers, one per certified architecture; generated by dev/pin_dossier.py from idle single-tenant runs, consumed by dossier_drift.'''
 
 
 
 
2
 
3
  PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
4
  'confidence': 'high',
 
291
  'd2h_pinned': 10.8,
292
  'h2d_pageable': 10.3,
293
  'h2d_pinned': 11.8}}},
294
+ 'sm89_nvidiartx6000adagenerati': {'atomics_f32': {'clock_ghz': 2.705,
295
  'confidence': 'high',
296
  'method': 'atomicAdd '
297
  'hammer; curve '
 
304
  'contention) to '
305
  'one per thread',
306
  'units': 'Gop/s',
307
+ 'value': {'curve_slots': {'1': 0.71,
308
+ '145408': 413.99,
309
+ '4096': 184.9,
310
+ '512': 49.84,
311
+ '64': 13.25,
312
+ '8': 5.74},
313
+ 'shared_same_addr': 12.23}},
314
  'clock': {'confidence': 'high',
315
+ 'final_ghz': 2.698,
316
+ 'max_ghz': 2.705,
317
  'method': '24 back-to-back FMA '
318
  'spin windows of 30M '
319
  'ticks; wall/ticks '
320
  'per window',
321
+ 'min_ghz': 2.5,
322
  'sag_pct': 0.3,
323
  'units': 'GHz'},
324
  'device': {'cuda_runtime': '12.6',
 
332
  'sms': 142,
333
  'torch': '2.10.0+cu126',
334
  'vram_bytes': 48305274880},
335
+ 'dram_bw': {'clock_ghz': 2.705,
336
  'confidence': 'high',
337
  'method': 'streaming triad '
338
  'c=a+s*b per access '
 
352
  'line-efficiency '
353
  'claim',
354
  'units': 'GB/s',
355
+ 'value': {'random_gather_16B': 218.1,
356
+ 'read_only': 766.5,
357
+ 'triad_16B': 682.1,
358
+ 'triad_4B': 654.8,
359
+ 'triad_8B': 670.9,
360
  'write_only': 766.5}},
361
+ 'fma_f32': {'clock_ghz': 2.705,
362
  'confidence': 'high',
363
  'method': 'register-resident '
364
  'FMA chains',
365
  'units': 'TFLOP/s',
366
+ 'value': 86.58},
367
+ 'fma_f64': {'clock_ghz': 2.705,
368
  'confidence': 'high',
369
  'method': 'register-resident '
370
  'double FMA chains '
371
  '(the DFMA pipe)',
372
  'units': 'TFLOP/s',
373
+ 'value': 1.257},
374
+ 'fma_ilp': {'clock_ghz': 2.705,
375
+ 'confidence': 'high',
376
+ 'method': '1/2/4 independent '
377
+ 'dependent FMA '
378
+ 'chains per thread '
379
+ 'at one block of 64 '
380
+ 'threads per SM; a '
381
+ 'ratio near 4 means '
382
+ 'latency-bound at '
383
+ 'that occupancy',
384
+ 'units': 'TFLOP/s',
385
+ 'value': {'ilp1': 11.75,
386
+ 'ilp2': 23.37,
387
+ 'ilp4': 43.03,
388
+ 'ratio_4_1': 3.66}},
389
+ 'grid_barrier': {'clock_ghz': 2.705,
390
  'confidence': 'high',
391
  'method': 'cooperative '
392
  'kernel, 142 '
 
394
  'threads',
395
  'units': 'us per '
396
  'grid.sync',
397
+ 'value': 0.55},
398
+ 'l2_bw': {'clock_ghz': 2.705,
399
  'confidence': 'medium',
400
  'method': 'in-kernel repeated '
401
  'triad passes over 72 '
 
405
  'from HBM; amortized '
406
  'over 64 passes',
407
  'units': 'GB/s',
408
+ 'value': 9590.6},
409
+ 'latency': {'clock_ghz': 2.691,
410
  'confidence': 'high',
411
  'method': 'Sattolo-cycle '
412
+ 'pointer chase, min '
413
+ 'of 3 runs; 16 KB '
414
+ 'ring for L1, 2 MB '
415
+ 'TLB-resident ring '
416
+ 'for L2, '
417
  'self-evicting '
418
  'chain (2x L2 in '
419
  'touched lines) for '
 
425
  'footprints',
426
  'units': 'cycles (single-lane '
427
  'dependent loads)',
428
+ 'value': {'dram_cycles': 656.9,
429
  'l1_cycles': 45.8,
430
+ 'l2_cycles': 292.0,
431
  'ns_per_cycle': 0.372,
432
  'smem_cycles': 30.0}},
433
+ 'launch_overhead': {'clock_ghz': 2.705,
434
+ 'confidence': 'medium',
435
  'method': 'empty-kernel '
436
+ 'trains; '
437
+ 'torch path '
438
+ 'per-launch '
439
+ 'through op '
440
+ 'dispatch, '
441
+ 'raw path '
442
+ 'looped '
443
+ 'inside one '
444
+ 'binding '
445
+ 'call',
446
+ 'note': 'host-state-dependent; '
447
+ 'the '
448
+ 'torch-minus-raw '
449
+ 'gap is the '
450
+ 'dispatch '
451
+ 'cost a '
452
+ 'torch-launched '
453
+ 'candidate '
454
+ 'pays',
455
  'units': 'us',
456
+ 'value': {'raw_queued_us': 6.75,
457
+ 'raw_round_trip_us': 13.87,
458
+ 'torch_queued_us': 7.09,
459
+ 'torch_round_trip_us': 18.53}},
460
+ 'mma': {'clock_ghz': 2.705,
461
  'confidence': 'medium',
462
+ 'fp8_accum': {'fp8_e4m3': {'effective_accum_mantissa_bits': 8,
463
+ 'stall_element_value': 16384.0},
464
+ 'fp8_e5m2': {'effective_accum_mantissa_bits': 8,
465
+ 'stall_element_value': 16384.0}},
466
  'method': 'register-fragment '
467
  'mma_sync loops (wmma; '
468
  'fp8 and 2:4-sparse '
469
  'fp16 via inline PTX '
470
+ 'm16n8k32); flops '
471
+ 'derive from a '
472
+ 'sub-saturation '
473
+ 'accumulator read-back '
474
+ 'with every fragment '
475
+ 'element observed, '
476
+ 'never the declared '
477
+ 'loop shape; sparse '
478
+ 'reports executed and '
479
+ 'dense-equivalent flops',
480
  'note': 'dense-tile rate without '
481
  'memory traffic; real '
482
  'kernels see this only '
 
484
  'behind it',
485
  'units': 'TFLOP/s (TOP/s for '
486
  'int8)',
487
+ 'value': {'bf16': 369.6,
488
+ 'fp16': 365.3,
489
+ 'fp16_sparse_2_4': {'dense_equivalent': 711.1,
490
+ 'executed': 355.6},
491
+ 'fp8_e4m3': 772.3,
492
+ 'fp8_e5m2': 771.2,
493
+ 'int8': 750.0,
494
+ 'tf32': 178.3},
495
+ 'verified_k_lanes': {'bf16': {'calibration_linear': True,
496
+ 'executed_k_per_iter': 64.0,
497
  'vs_nominal': 1.0},
498
+ 'fp16': {'calibration_linear': True,
499
+ 'executed_k_per_iter': 64.0,
500
  'vs_nominal': 1.0},
501
+ 'fp16_sparse_2_4': {'calibration_linear': True,
502
+ 'executed_k_per_iter': 128.0,
503
  'vs_nominal': 1.0},
504
+ 'fp8_e4m3': {'calibration_linear': True,
505
+ 'executed_k_per_iter': 256.0,
506
+ 'vs_nominal': 1.0},
507
+ 'fp8_e5m2': {'calibration_linear': True,
508
+ 'executed_k_per_iter': 256.0,
509
+ 'vs_nominal': 1.0},
510
+ 'int8': {'calibration_linear': True,
511
+ 'executed_k_per_iter': 64.0,
512
  'vs_nominal': 1.0},
513
+ 'tf32': {'calibration_linear': True,
514
+ 'executed_k_per_iter': 32.0,
515
  'vs_nominal': 1.0}}},
516
+ 'mma_fed': {'clock_ghz': 2.705,
517
+ 'confidence': 'high',
518
+ 'method': 'm16n16k16 fp16 '
519
+ 'tiles fed from an '
520
+ 'L2-resident buffer '
521
+ 'through cp.async '
522
+ 'double-buffered '
523
+ 'shared memory; '
524
+ 'accumulator-verified',
525
+ 'note': 'the achieved '
526
+ 'counterpart to the '
527
+ 'register peak: '
528
+ 'staging exposed, one '
529
+ 'dependent '
530
+ 'accumulator chain '
531
+ 'per warp',
532
+ 'units': 'TFLOP/s',
533
+ 'value': 85.2},
534
+ 'occupancy_blocks_per_sm': {'clock_ghz': 2.705,
535
  'confidence': 'high',
536
  'method': 'occupancy '
537
  'API '
 
560
  '16KB': 3,
561
  '32KB': 3,
562
  '48KB': 2}}},
563
+ 'occupancy_knees_fma_tflops': {'clock_ghz': 2.705,
564
  'confidence': 'high',
565
  'method': 'a '
566
  'single '
 
580
  'fixed '
581
  'total '
582
  'work; '
 
 
 
 
 
 
 
 
 
 
 
583
  'the '
584
  'cliff '
585
  'marks '
 
597
  'above',
598
  'units': 'TFLOP/s',
599
  'value': {'t1024': {'0KB': 75.5,
600
+ '32KB': 73.71,
601
+ '48KB': 72.69},
602
+ 't128': {'0KB': 79.72,
603
  '32KB': 60.31,
604
+ '48KB': 45.81},
605
+ 't256': {'0KB': 78.79,
606
+ '32KB': 78.85,
607
+ '48KB': 72.97}}},
608
+ 'smem_bw': {'clock_ghz': 2.705,
609
  'confidence': 'medium',
610
  'method': 'block-resident '
611
  'read+write '
 
614
  'aggregated across '
615
  'SMs',
616
  'note': 'bank-conflict-light '
617
+ 'access pattern; the '
618
+ 'stride sweep below '
619
+ 'prices the '
620
+ 'conflicted case',
621
  'units': 'GB/s',
622
+ 'value': 22244.0},
623
+ 'smem_conflict_penalty': {'clock_ghz': 2.705,
624
+ 'confidence': 'high',
625
+ 'method': 'dependent '
626
+ 'read-FMA-write '
627
+ 'per '
628
+ 'thread '
629
+ 'at '
630
+ 'warp-lane '
631
+ 'bank '
632
+ 'distance '
633
+ '`stride`; '
634
+ 'stride '
635
+ '32 '
636
+ 'puts '
637
+ 'every '
638
+ 'lane '
639
+ 'in '
640
+ 'one '
641
+ 'bank',
642
+ 'note': 'the '
643
+ 'direct '
644
+ 'conflict '
645
+ 'measurement '
646
+ 'that '
647
+ 'disambiguates '
648
+ 'a '
649
+ 'bank-conflict '
650
+ 'signature '
651
+ 'from '
652
+ 'an ILP '
653
+ 'stall',
654
+ 'units': 'x '
655
+ '(time '
656
+ 'ratio '
657
+ 'vs '
658
+ 'stride '
659
+ '1)',
660
+ 'value': {'stride_1': 1.0,
661
+ 'stride_16': 7.13,
662
+ 'stride_2': 1.07,
663
+ 'stride_32': 14.08,
664
+ 'stride_4': 2.04,
665
+ 'stride_8': 3.91}},
666
+ 'transfer_bw': {'clock_ghz': 2.705,
667
  'confidence': 'high',
668
  'method': '256 MB torch '
669
  'copies, '
 
675
  'direction of '
676
  'traffic',
677
  'units': 'GB/s',
678
+ 'value': {'d2d': 324.0,
679
+ 'd2h_pageable': 7.8,
680
+ 'd2h_pinned': 26.3,
681
+ 'h2d_pageable': 7.6,
682
  'h2d_pinned': 24.0}}}}
build/torch212-cxx11-cu132-x86_64-linux/metadata.json CHANGED
@@ -1,9 +1,10 @@
1
  {
2
  "name": "metakernel",
3
- "id": "_metakernel_cuda_b62a1ce",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "python-depends": [],
 
7
  "backend": {
8
  "type": "cuda",
9
  "archs": [
@@ -18,21 +19,20 @@
18
  "digest": {
19
  "algorithm": "sha256",
20
  "files": {
21
- "__init__.py": "e/P3LIoxZqzuhgyc26YJRkdLj4OilextyRiU8YbAvIA=",
22
- "_metakernel_cuda_b62a1ce.abi3.so": "oMRXKRwpfLXZHf/BkZKrSQ2/2cu3jMA4G338ND92JKE=",
23
- "_ops.py": "QokZXDYGh+rLEKO/wNDRcMkJPtPWK568RiTlCHopsHM=",
24
- "_pinned.py": "4i1FvV3kWanEdFLd9+rGOjHcHDAyOjKnjLt5LUzgASs=",
25
- "metakernel/__init__.py": "DFYPlrhXwYjEqCl/8n0SmWGZV8NFml5DPhMjKfv98GY="
26
  }
27
  },
28
  "provenance": {
29
  "kernel-builder": {
30
  "version": "0.17.0-dev0",
31
- "sha": "19aaa6421e674e9fecc352bbae6eab81d19a6bf4",
32
  "dirty": false
33
  },
34
  "kernel": {
35
- "sha": "b62a1cef64b6e3b2cabf774c82ea7ce61dcac6c0",
36
  "dirty": false
37
  }
38
  }
 
1
  {
2
  "name": "metakernel",
3
+ "id": "_metakernel_cuda_0b9de42",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "python-depends": [],
7
+ "kernel-depends": [],
8
  "backend": {
9
  "type": "cuda",
10
  "archs": [
 
19
  "digest": {
20
  "algorithm": "sha256",
21
  "files": {
22
+ "__init__.py": "lDQ+fJ0JnI3sQGR3UMnDovBbG4ciYBsaFjmgYdKDTKI=",
23
+ "_metakernel_cuda_0b9de42.abi3.so": "XbiocNPfV/L6t8qX0NMjGDoZhWNe4Jr3Z5eGTs1lUg4=",
24
+ "_ops.py": "x88Bkm1nxRli0nIu2u4ztaJopS1IAZWdN9LZPfspJdo=",
25
+ "_pinned.py": "UyzMniAjfVMIrIbbrSq92KKo5dX5l/GwV3pMVA+eNtM="
 
26
  }
27
  },
28
  "provenance": {
29
  "kernel-builder": {
30
  "version": "0.17.0-dev0",
31
+ "sha": "2c40e10d3da1392245ee81bd05b55fb3103b2b34",
32
  "dirty": false
33
  },
34
  "kernel": {
35
+ "sha": "0b9de42fee8a1e52593f2f1f58460c0acc93d7e8",
36
  "dirty": false
37
  }
38
  }
build/torch212-cxx11-cu132-x86_64-linux/metakernel/__init__.py DELETED
@@ -1,26 +0,0 @@
1
- import ctypes
2
- import importlib.util
3
- import sys
4
- from pathlib import Path
5
- from types import ModuleType
6
-
7
-
8
- def _import_from_path(file_path: Path) -> ModuleType:
9
- # We cannot use the module name as-is, after adding it to `sys.modules`,
10
- # it would also be used for other imports. So, we make a module name that
11
- # depends on the path for it to be unique using the hex-encoded hash of
12
- # the path.
13
- path_hash = "{:x}".format(ctypes.c_size_t(hash(file_path.absolute())).value)
14
- module_name = path_hash
15
- spec = importlib.util.spec_from_file_location(module_name, file_path)
16
- if spec is None:
17
- raise ImportError(f"Cannot load spec for {module_name} from {file_path}")
18
- module = importlib.util.module_from_spec(spec)
19
- if module is None:
20
- raise ImportError(f"Cannot load module {module_name} from spec")
21
- sys.modules[module_name] = module
22
- spec.loader.exec_module(module) # type: ignore
23
- return module
24
-
25
-
26
- globals().update(vars(_import_from_path(Path(__file__).parent.parent / "__init__.py")))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
build/torch213-cxx11-cu126-x86_64-linux/__init__.py CHANGED
@@ -1,20 +1,4 @@
1
- """Measuring instrument for the kernel-design loop: timing, adversarial
2
- correctness comparison, and measured device facts, as loadable ops.
3
-
4
- from kernels import get_kernel
5
- mk = get_kernel("phanerozoic/metakernel", version=1,
6
- trust_remote_code=True)
7
-
8
- dossier = mk.probe_device() # measured facts
9
- report = mk.bench(lambda: op(x), iters=200) # throttle-rejected
10
- verdict = mk.compare(ref, cand, gen) # ULP evidence
11
- table = mk.sweep(factory, {"BLOCK": [64, 128]}) # config search
12
-
13
- Every op returns plain dicts and tensors. Every dossier entry is
14
- measured by a dedicated probe kernel, carries its SM clock, and labels
15
- throttled samples; no hardware counters, so entries carry a confidence
16
- field.
17
- """
18
  import math
19
  import os
20
  import time
@@ -24,9 +8,7 @@ import torch
24
  try:
25
  from ._ops import ops
26
  except ImportError: # pragma: no cover - spawned child in a source tree
27
- # Subprocess-isolation children re-import this package; in a source
28
- # checkout there is no generated _ops module, so rebuild the op table
29
- # from the (cached) JIT extension that load_local advertised.
30
  _root = os.environ.get("METAKERNEL_DEV_ROOT")
31
  if not _root:
32
  raise
@@ -48,24 +30,25 @@ try:
48
  except ImportError: # pragma: no cover - fresh tree before first pin
49
  _PINNED = {}
50
 
51
- __all__ = ["probe_device", "bench", "compare", "order_shuffles",
52
- "fuzz_shapes", "sweep", "alloc_stamps", "read_stamps",
53
- "stamped_demo", "measure_clock", "ulp_diff", "dossier_drift",
54
- "pinned_dossiers", "ops"]
55
-
56
- # name -> (kind, M*N of the C tile, nominal K, work multiplier, nominal
57
- # K-lanes per unrolled iteration). The accumulator read-back yields
58
- # executed K-lanes per op (column sums of B equal K), so flops =
59
- # 2 x M*N x gain x multiplier with nothing declared trusted; multiplier
60
- # 2.0 is the sparse dense-equivalent convention. Lanes/iter: wmma K x 4
61
- # chained ops; PTX kinds x 8 over two chains.
62
  _MMA_KINDS = {
63
- "fp16": (0, 16 * 16, 16, 1.0, 64.0),
64
- "bf16": (1, 16 * 16, 16, 1.0, 64.0),
65
- "tf32": (2, 16 * 16, 8, 1.0, 32.0),
66
- "int8": (3, 16 * 16, 16, 1.0, 64.0),
67
- "fp8_e4m3": (4, 16 * 8, 32, 1.0, 256.0),
68
- "fp16_sparse_2_4": (5, 16 * 8, 32, 2.0, 128.0), # dense-equivalent
 
69
  }
70
 
71
 
@@ -74,7 +57,7 @@ def _sync():
74
 
75
 
76
  def _events(fn, reps):
77
- """Time reps calls of fn with one event pair each; returns ms list."""
78
  starts = [torch.cuda.Event(enable_timing=True) for _ in range(reps)]
79
  stops = [torch.cuda.Event(enable_timing=True) for _ in range(reps)]
80
  for s, e in zip(starts, stops):
@@ -86,8 +69,7 @@ def _events(fn, reps):
86
 
87
 
88
  def measure_clock(device="cuda", ticks=4_000_000, blocks=None):
89
- """Effective SM clock in GHz under an FMA spin of `ticks` clock64 ticks.
90
- Wall time over elapsed ticks is the frequency the ticks ran at."""
91
  props = torch.cuda.get_device_properties(device)
92
  if blocks is None:
93
  blocks = props.multi_processor_count
@@ -120,8 +102,7 @@ def _entry(value, units, method, clock_ghz, confidence, note=""):
120
 
121
 
122
  def _sattolo_ring(n, device, generator=None):
123
- """A random single-cycle permutation as an int32 next-pointer table, so
124
- a pointer chase visits every slot before repeating."""
125
  perm = torch.randperm(n, generator=generator)
126
  ring = torch.empty(n, dtype=torch.int32)
127
  ring[perm] = torch.roll(perm, -1).to(torch.int32)
@@ -129,8 +110,7 @@ def _sattolo_ring(n, device, generator=None):
129
 
130
 
131
  def _smi_settings(index):
132
- """ECC mode, driver, and power limit via nvidia-smi; None fields
133
- when the tool is unavailable."""
134
  import subprocess
135
  try:
136
  out = subprocess.run(
@@ -150,10 +130,19 @@ def _smi_settings(index):
150
  return {"ecc": None, "driver": None, "power_limit_w": None}
151
 
152
 
 
 
 
 
 
 
 
 
 
 
 
153
  def mma_tile_check(device="cuda", trials=8):
154
- """Random small-integer m16n16k16 tiles through the wmma fragment
155
- path, compared exactly to the reference GEMM; pins M, N, K, and
156
- operand mapping (integer operands make fp32 accumulation exact)."""
157
  worst = 0.0
158
  for t in range(trials):
159
  g = torch.Generator(device=device).manual_seed(1000 + t)
@@ -171,10 +160,83 @@ def mma_tile_check(device="cuda", trials=8):
171
  "trials": trials}
172
 
173
 
174
- def probe_device(device="cuda", quick=False):
175
- """Empirical device dossier. Each entry is measured by a dedicated
176
- probe kernel and carries units, method, the SM clock it ran under, and
177
- a confidence field."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
  props = torch.cuda.get_device_properties(device)
179
  sm = props.multi_processor_count
180
  l2 = props.L2_cache_size
@@ -187,8 +249,7 @@ def probe_device(device="cuda", quick=False):
187
  "vram_bytes": props.total_memory,
188
  "torch": torch.__version__,
189
  "cuda_runtime": torch.version.cuda,
190
- # instrument settings: readings depend on them, so the
191
- # dossier records them (None when nvidia-smi is unavailable)
192
  **_smi_settings(torch.device(device).index or 0),
193
  }
194
  }
@@ -213,9 +274,8 @@ def probe_device(device="cuda", quick=False):
213
  }
214
  clk = max(traj)
215
 
216
- # ---- device-memory bandwidth: triad per access width, plus read-only,
217
- # write-only, and random-gather
218
- n4 = 16 << 20 # 16M float4 = 256 MB per buffer
219
  if props.total_memory < 4 << 30:
220
  n4 = 4 << 20
221
  a = torch.rand(n4 * 4, device=device)
@@ -239,7 +299,6 @@ def probe_device(device="cuda", quick=False):
239
  dtype=torch.int32).to(device)
240
  ops.mk_gather(a, idx, c, 256, 0)
241
  ms = _median(_events(lambda: ops.mk_gather(a, idx, c, 256, 0), reps))
242
- # traffic accounting: idx reads + 16B random loads + streamed stores
243
  hbm["random_gather_16B"] = round(
244
  (idx.numel() * 4 + 2 * n4 * 16) / (ms * 1e-3) / 1e9, 1)
245
  d["dram_bw"] = _entry(hbm, "GB/s", "streaming triad c=a+s*b per access "
@@ -250,9 +309,9 @@ def probe_device(device="cuda", quick=False):
250
  "so its nominal number is the achievable rate for "
251
  "random access, not a line-efficiency claim")
252
 
253
- # ---- L2 bandwidth (working set 3 buffers inside L2)
254
  ws = max(l2 // 4, 1 << 20)
255
- nl2 = ws // 16 # float4 elems per buffer
256
  al = a[:nl2 * 4].contiguous()
257
  bl = b[:nl2 * 4].contiguous()
258
  cl = c[:nl2 * 4].contiguous()
@@ -267,62 +326,61 @@ def probe_device(device="cuda", quick=False):
267
  "medium", "first pass streams from HBM; amortized "
268
  f"over {passes} passes")
269
 
270
- # ---- latencies: pointer chase per level. The 2 MB L2 ring exceeds
271
- # L1, fits L2 everywhere, and stays TLB-resident after a warm walk;
272
- # the DRAM chain touches 2x L2 in lines, so it evicts itself.
273
  lat = {}
274
  idle_clk = measure_clock(device, ticks=2_000_000, blocks=1)
275
- ring_s = _sattolo_ring(8192, device)
276
- ops.mk_chase_shared(ring_s, 1 << 14, out64)
277
- _sync()
278
- hops = 1 << 16
279
- ops.mk_chase_shared(ring_s, hops, out64)
280
- _sync()
281
- lat["smem_cycles"] = round(int(out64[0].item()) / hops, 1)
282
 
283
- l1_ring_n = (16 << 10) // 4 # 16 KB: L1-resident after a warm walk
284
- ring_l1 = _sattolo_ring(l1_ring_n, device)
285
- ops.mk_chase_global(ring_l1, l1_ring_n, out64)
286
- _sync()
287
- hops_l1 = 1 << 15
288
- ops.mk_chase_global(ring_l1, hops_l1, out64)
289
- _sync()
290
- lat["l1_cycles"] = round(int(out64[0].item()) / hops_l1, 1)
291
-
292
- l2_ring_n = (2 << 20) // 4
293
- ring_l2 = _sattolo_ring(l2_ring_n, device)
294
- ops.mk_chase_global(ring_l2, l2_ring_n, out64) # full-cycle warm walk
295
- _sync()
296
- hops_l2 = 1 << 14
297
- ops.mk_chase_global(ring_l2, hops_l2, out64)
298
- _sync()
299
- lat["l2_cycles"] = round(int(out64[0].item()) / hops_l2, 1)
300
 
 
 
 
 
 
 
 
 
 
 
 
 
301
  hbm_bytes = min(max(4 * l2, 64 << 20), props.total_memory // 8)
302
  ring_h = _sattolo_ring(int(hbm_bytes) // 4, device)
303
  hops_h = max((2 * l2) // 128, 1 << 17)
304
- ops.mk_chase_global(ring_h, 1 << 14, out64) # fault pages / warm TLB
305
- _sync()
306
- ops.mk_chase_global(ring_h, hops_h, out64)
307
- _sync()
308
- lat["dram_cycles"] = round(int(out64[0].item()) / hops_h, 1)
309
  lat["ns_per_cycle"] = round(1.0 / idle_clk, 3)
310
  d["latency"] = _entry(lat, "cycles (single-lane dependent loads)",
311
- "Sattolo-cycle pointer chase; 16 KB ring for L1, "
312
- "2 MB TLB-resident ring for L2, self-evicting "
313
- "chain (2x L2 in touched lines) for HBM",
314
- idle_clk, "high",
315
  "smem includes index arithmetic; HBM includes "
316
  "residual TLB effects at large footprints")
317
  del ring_h, ring_l2, ring_l1, ring_s
318
 
319
- # ---- fp32 FMA and tensor-core throughput
320
  blocks = sm * 4
321
  iters = 20_000 if quick else 60_000
322
  ops.mk_fma_f32(blocks, 256, iters, sink)
323
  ms = _median(_events(lambda: ops.mk_fma_f32(blocks, 256, iters, sink),
324
  reps))
325
- flops = blocks * 256 * iters * 16 * 2 * 2 # 2 chains x 2 flops x unroll
326
  d["fma_f32"] = _entry(round(flops / (ms * 1e-3) / 1e12, 2), "TFLOP/s",
327
  "register-resident FMA chains", clk, "high")
328
  sink64 = torch.zeros(2, dtype=torch.float64, device=device)
@@ -334,43 +392,88 @@ def probe_device(device="cuda", quick=False):
334
  d["fma_f64"] = _entry(round(flops64 / (ms * 1e-3) / 1e12, 3), "TFLOP/s",
335
  "register-resident double FMA chains (the DFMA "
336
  "pipe)", clk, "high")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
337
  mma = {}
338
  mma_klanes = {}
339
- for name, (kind, mn, k_nom, mult, nom_lanes) in _MMA_KINDS.items():
 
340
  it = 30_000 if quick else 100_000
341
  r = ops.mk_mma(kind, blocks, 8, 512, sink)
342
  if r != 0:
343
  mma[name] = "unsupported"
344
  continue
345
  _sync()
346
- # verification pass: with unit operands the accumulator gain IS
347
- # the executed K-lane count (exact at these magnitudes)
348
- cal_iters = 512
349
- sink[1] = 0.0
350
- ops.mk_mma(kind, 1, 1, cal_iters, sink)
351
- _sync()
352
- gain_per_iter = float(sink[1].item()) / cal_iters
353
  mma_klanes[name] = {
354
- "executed_k_per_iter": round(gain_per_iter, 2),
355
- "vs_nominal": round(gain_per_iter / nom_lanes, 3),
 
356
  }
 
 
357
  ms = _median(_events(
358
  lambda k=kind, i=it: ops.mk_mma(k, blocks, 8, i, sink), reps))
359
- flops = blocks * 8 * it * 2.0 * mn * gain_per_iter * mult
360
- mma[name] = round(flops / (ms * 1e-3) / 1e12, 1)
 
 
 
 
361
  d["mma"] = _entry(mma, "TFLOP/s (TOP/s for int8)",
362
  "register-fragment mma_sync loops (wmma; fp8 and "
363
- "2:4-sparse fp16 via inline PTX m16n8k32; sparse "
364
- "counted dense-equivalent, the vendor convention); "
365
- "flops derive from the mean accumulator read-back "
366
  "with every fragment element observed, never the "
367
- "declared loop shape",
 
368
  clk, "medium",
369
  "dense-tile rate without memory traffic; real "
370
  "kernels see this only when staging hides behind it")
371
  d["mma"]["verified_k_lanes"] = mma_klanes
 
 
372
 
373
- # ---- shared-memory bandwidth
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
374
  it_sm = 30_000 if quick else 100_000
375
  ops.mk_smem_bw(blocks, 256, it_sm, sink)
376
  ms = _median(_events(lambda: ops.mk_smem_bw(blocks, 256, it_sm, sink),
@@ -380,8 +483,23 @@ def probe_device(device="cuda", quick=False):
380
  "block-resident read+write streaming (8 B per "
381
  "thread-iteration), aggregated across SMs", clk,
382
  "medium",
383
- "bank-conflict-light access pattern; a conflicted "
384
- "pattern can be far lower")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
385
 
386
  # ---- PCIe / host transfer bandwidth
387
  nbytes = 256 << 20
@@ -412,9 +530,7 @@ def probe_device(device="cuda", quick=False):
412
  "d2d counts one direction of traffic")
413
  del host_pin, host_pag, dev_t, dev_u
414
 
415
- # ---- atomic contention curve: throughput vs number of distinct
416
- # addresses the whole grid hammers (slots = 1 is full contention;
417
- # slots = threads is contention-free), plus the shared-memory point
418
  n_thr = blocks * 256
419
  buf = torch.zeros(n_thr, dtype=torch.float32, device=device)
420
  at = {"curve_slots": {}}
@@ -424,7 +540,7 @@ def probe_device(device="cuda", quick=False):
424
  buf.zero_()
425
  ops.mk_atomics(1, blocks, 256, it_at, buf, slots)
426
  ms = _median(_events(
427
- lambda s=slots: ops.mk_atomics(1, blocks, 256, it_at, buf, s),
428
  reps))
429
  at["curve_slots"][str(slots)] = round(
430
  n_thr * it_at / (ms * 1e-3) / 1e9, 2)
@@ -437,7 +553,7 @@ def probe_device(device="cuda", quick=False):
437
  "of distinct global addresses from 1 (full "
438
  "contention) to one per thread", clk, "high")
439
 
440
- # ---- launch overhead
441
  anchor = sink
442
  n_launch = 500 if quick else 2000
443
  _sync()
@@ -452,10 +568,19 @@ def probe_device(device="cuda", quick=False):
452
  ops.mk_empty(anchor)
453
  _sync()
454
  rt = (time.perf_counter() - t0) / n_rt * 1e6
 
 
455
  d["launch_overhead"] = _entry(
456
- {"queued_us": round(queued, 2), "round_trip_us": round(rt, 2)},
457
- "us", "empty-kernel trains, queued vs launch+sync", clk, "high",
458
- "host-dependent: includes torch op dispatch")
 
 
 
 
 
 
 
459
 
460
  # ---- grid barrier
461
  bar_iters = 500 if quick else 2000
@@ -471,9 +596,7 @@ def probe_device(device="cuda", quick=False):
471
  "unavailable or grid too large", clk,
472
  "high")
473
 
474
- # ---- occupancy table (API) plus timed knees: the same random gather
475
- # at every (threads, dyn_smem) point; when the forced blocks/SM stop
476
- # covering memory latency, achieved bandwidth falls off the cliff
477
  occ = {}
478
  for threads in (128, 256, 512, 1024):
479
  row = {}
@@ -494,8 +617,8 @@ def probe_device(device="cuda", quick=False):
494
  ops.mk_fma_dep(knee_blocks, threads, smem_kb * 1024,
495
  knee_iters, sink)
496
  ms = _median(_events(
497
- lambda t=threads, s=smem_kb: ops.mk_fma_dep(
498
- knee_blocks, t, s * 1024, knee_iters, sink), reps))
499
  fl = knee_blocks * threads * knee_iters * 16 * 2
500
  row[f"{smem_kb}KB"] = round(fl / (ms * 1e-3) / 1e12, 2)
501
  knees[f"t{threads}"] = row
@@ -504,27 +627,160 @@ def probe_device(device="cuda", quick=False):
504
  "at forced (threads, dyn smem) occupancies over fixed total work; "
505
  "the cliff marks where occupancy stops covering latency",
506
  clk, "high", "timing-derived counterpart to the API table above")
 
 
 
507
  return d
508
 
509
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
510
  def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
511
  dtype="fp16", clock_guard=0.97, clock_stride=8, graph=False,
512
- device="cuda"):
513
- """Median-of-N cudaEvent timing with throttle rejection.
514
-
515
- The SM clock is probed every `clock_stride` samples; samples in a
516
- window below `clock_guard` x the best observed clock are rejected
517
- and counted. Declared `bytes`/`flops` yield achieved GB/s / TFLOP/s;
518
- a `dossier` adds roofline placement. `graph=True` times CUDA-graph
519
- replays, stripping Python and torch dispatch (fn must be graph-safe:
520
- no sync, no allocation growth, static shapes; a failed capture falls
521
- back to eager with the reason reported). `clock_stride=1` probes
522
- around every sample, ~0.3 ms each."""
523
  for _ in range(warmup):
524
  fn()
525
  _sync()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
526
  graph_state = "off"
527
  graph_reason = ""
 
 
 
 
 
 
528
  if graph:
529
  pre_rng = torch.cuda.get_rng_state(device)
530
  try:
@@ -539,10 +795,7 @@ def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
539
  except Exception as e: # noqa: BLE001 - fall back, say why
540
  graph_state = "fallback_eager"
541
  graph_reason = f"{type(e).__name__}: {str(e)[:120]}"
542
- # drain the invalidated stream so eager timing can proceed.
543
- # On some torch versions a failed capture also wedges the
544
- # device RNG with no in-process recovery; timing uses no RNG,
545
- # but the caller's later randn may raise.
546
  rng_ok = True
547
  for _attempt in range(5):
548
  try:
@@ -572,25 +825,51 @@ def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
572
  samples.append((_events(fn, n), len(clocks) - 1))
573
  clocks.append(measure_clock(device, ticks=probe_ticks))
574
  best_clk = max(clocks)
575
- kept, rejected = [], 0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
576
  flat = []
577
- for group, ci in samples:
578
- window_ok = (clocks[ci] >= clock_guard * best_clk and
579
- clocks[min(ci + 1, len(clocks) - 1)] >=
580
- clock_guard * best_clk)
581
- for ms in group:
582
- flat.append(ms)
583
- if window_ok:
 
 
 
 
 
 
 
 
 
 
 
 
584
  kept.append(ms)
585
  else:
586
  rejected += 1
587
- if not kept:
 
588
  kept = flat
589
  med = _median(kept)
590
  ks = sorted(kept)
591
  n_k = len(ks)
592
- # order-statistic 95% CI on the median (normal approximation to the
593
- # binomial ranks); degenerate at tiny n, where it spans the sample
594
  half = 1.959964 * (n_k ** 0.5) / 2.0
595
  lo_i = max(0, int(n_k / 2 - half) - 1)
596
  hi_i = min(n_k - 1, int(n_k / 2 + half))
@@ -605,12 +884,25 @@ def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
605
  "mean_ms": round(sum(kept) / n_k, 4),
606
  "samples": len(flat),
607
  "rejected_throttled": rejected,
 
608
  "graph_captured": graph_state == "captured",
609
  "clock_ghz": {"best": round(best_clk, 3),
610
  "worst": round(min(clocks), 3)},
611
  }
 
 
 
 
 
 
 
612
  if graph_reason:
613
  rep["graph_fallback_reason"] = graph_reason
 
 
 
 
 
614
  if bytes is not None:
615
  rep["achieved_gbs"] = round(bytes / (med * 1e-3) / 1e9, 1)
616
  if flops is not None:
@@ -620,6 +912,8 @@ def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
620
  if dossier is not None and bytes and flops:
621
  bw = max(dossier["dram_bw"]["value"].values()) * 1e9
622
  mma_v = dossier["mma"]["value"].get(dtype)
 
 
623
  peak = (mma_v if isinstance(mma_v, (int, float))
624
  else dossier["fma_f32"]["value"]) * 1e12
625
  ai = flops / bytes
@@ -630,13 +924,12 @@ def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
630
  "pct_of_roof": round(100 * (flops / (med * 1e-3)) / roof, 1),
631
  "peak_dtype": dtype,
632
  }
 
633
  return rep
634
 
635
 
636
  def _to_ordered_int(t):
637
- """Map float bit patterns to integers whose difference is the ULP
638
- distance: positive floats keep their bits, negative floats mirror
639
- below zero, so the mapping is monotone in the float value."""
640
  if t.dtype == torch.float32:
641
  u = t.view(torch.int32).to(torch.int64) & 0xFFFFFFFF
642
  half = 1 << 31
@@ -652,11 +945,12 @@ def _to_ordered_int(t):
652
 
653
 
654
  def ulp_diff(a, b):
655
- """Elementwise ULP distance between same-dtype tensors. For floats,
656
- NaNs in the same position count as 0 and a one-sided NaN counts as
657
- 2**62; for integer and bool tensors the distance is the absolute
658
- numeric difference (an exact op must be exactly equal)."""
659
- assert a.dtype == b.dtype and a.shape == b.shape
 
660
  if not a.is_floating_point():
661
  return (a.to(torch.int64) - b.to(torch.int64)).abs()
662
  ia, ib = _to_ordered_int(a), _to_ordered_int(b)
@@ -673,9 +967,7 @@ class order_shuffles:
673
 
674
  @staticmethod
675
  def last_dim():
676
- """Permute the last dim of every tensor arg identically: the
677
- accumulation order of a last-dim reduction changes, the value
678
- does not."""
679
  def shuffle(args, seed):
680
  g = torch.Generator(device=args[0].device).manual_seed(seed)
681
  p = torch.randperm(args[0].shape[-1], device=args[0].device,
@@ -685,8 +977,7 @@ class order_shuffles:
685
 
686
  @staticmethod
687
  def matmul_k():
688
- """Permute K of (A [.., M, K], B [.., K, N]) consistently: the
689
- product is unchanged, the accumulation order is not."""
690
  def shuffle(args, seed):
691
  a_m, b_m = args[0], args[1]
692
  g = torch.Generator(device=a_m.device).manual_seed(seed)
@@ -700,37 +991,70 @@ def _as_outputs(x):
700
  return list(x) if isinstance(x, (tuple, list)) else [x]
701
 
702
 
703
- def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
704
- ref_variants=None, ulp_tol=None, atol=None, rtol=None,
705
- band_margin=4.0, zero_atol=None, worst_k=8, seed=0):
706
- """Adversarial correctness comparison with ULP evidence.
707
-
708
- `gen(seed) -> args`; `ref(*args)` and `cand(*args)` return a tensor
709
- or tuple (worst output governs). With `order_shuffle(args, seed)`
710
- reordering contractual-noise dimensions (see `order_shuffles`), each
711
- element is judged against its own measured POINTWISE reduction-order
712
- band: pass is max(ulp / (own_band + 2)) <= band_margin; without a
713
- band, max ULP <= ulp_tol (default 2); `atol`/`rtol` are an
714
- alternative acceptance route. `zero_atol`: elements with
715
- |ref| < zero_atol are judged by absolute difference (ULP explodes
716
- across the denormal range). The report carries the top-`worst_k`
717
- offenders (index, ref, cand, ULP). Order shuffles cover the
718
- reference's own topology; a candidate with a different legitimate
719
- topology (split-K) needs `ref_variants`, callables `f(args) -> out`
720
- whose outputs widen the pointwise band."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
721
  worst = {"max_ulp": -1}
722
  total_hist = {}
723
  per_trial = []
724
  max_abs_seen = 0.0
725
  max_rel_seen = 0.0
 
726
  for t in range(trials):
727
  args = gen(seed + t)
 
 
 
 
728
  r_outs = _as_outputs(ref(*args))
729
  c_outs = _as_outputs(cand(*args))
730
- assert len(r_outs) == len(c_outs), "output arity mismatch"
731
- # pointwise band: each element is judged against its own measured
732
- # order sensitivity (cancellation makes near-zero outputs
733
- # legitimately order-sensitive while neighbors are stable)
 
 
 
 
 
 
 
 
 
 
 
734
  bands = None
735
  band = 0
736
  band_score = 0.0
@@ -754,8 +1078,11 @@ def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
754
  max_ulp = 0
755
  mean_ulp = 0.0
756
  first_div = -1
 
757
  n_el = 0
758
  n_above_band = 0
 
 
759
  nan_r = nan_c = inf_r = inf_c = 0
760
  worst_elements = []
761
  for oi, (r, c) in enumerate(zip(r_outs, c_outs)):
@@ -773,8 +1100,11 @@ def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
773
  mean_ulp += float(d.double().sum().item())
774
  n_el += d.numel()
775
  nz = (d > 0)
 
 
776
  if first_div < 0 and nz.any():
777
  first_div = int(nz.flatten().nonzero()[0].item())
 
778
  if nz.any():
779
  buckets = torch.clamp(
780
  torch.log2(d[nz].double()).floor(), 0, 62
@@ -791,6 +1121,7 @@ def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
791
  if val > 0:
792
  worst_elements.append({
793
  "output": oi, "index": int(ix),
 
794
  "ref": float(rf[ix].item()),
795
  "cand": float(cf[ix].item()),
796
  "ulp": int(val)})
@@ -810,23 +1141,27 @@ def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
810
  inf_r += int(torch.isinf(r).sum().item())
811
  inf_c += int(torch.isinf(c).sum().item())
812
  worst_elements.sort(key=lambda w: -w["ulp"])
 
813
  trial_rep = {
814
  "max_ulp": max_ulp,
815
  "mean_ulp": round(mean_ulp / max(n_el, 1), 3),
816
  "first_divergent_index": first_div,
 
817
  "nan_ref": nan_r, "nan_cand": nan_c,
818
  "inf_ref": inf_r, "inf_cand": inf_c,
819
  "order_band_ulp": band,
820
  "band_score": round(band_score, 2),
821
  "frac_above_band": round(n_above_band / max(n_el, 1), 6),
 
 
822
  "worst_elements": worst_elements[:worst_k],
823
  }
824
  per_trial.append(trial_rep)
825
  if max_ulp > worst["max_ulp"]:
826
  worst = trial_rep
827
- band_max = max(t["order_band_ulp"] for t in per_trial)
828
  max_seen = max(t["max_ulp"] for t in per_trial)
829
- if order_shuffle is not None or ref_variants:
 
830
  score = max(t["band_score"] for t in per_trial)
831
  ulp_ok = score <= band_margin
832
  criterion = (f"pointwise max(ulp / (order_band + 2)) = "
@@ -841,8 +1176,34 @@ def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
841
  max_rel_seen <= (rtol or 0) or max_abs_seen <= (atol or 0)
842
  criterion += (f"; OR max_abs {max_abs_seen:.3e} <= atol {atol} / "
843
  f"max_rel {max_rel_seen:.3e} <= rtol {rtol}")
844
- return {
845
- "pass": bool(ulp_ok or tol_ok),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
846
  "criterion": criterion,
847
  "worst_trial": worst,
848
  "max_abs": max_abs_seen,
@@ -850,6 +1211,9 @@ def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
850
  "trials": per_trial,
851
  "ulp_hist_log2": dict(sorted(total_hist.items())),
852
  }
 
 
 
853
 
854
 
855
  _FUZZ_VARIANTS = ("contig", "transposed", "strided_rows", "offset_slice",
@@ -865,6 +1229,30 @@ def _context_alive(device):
865
  return False
866
 
867
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
868
  def _fuzz_build(make, shape, dtype):
869
  try:
870
  out = make(tuple(shape), dtype)
@@ -881,7 +1269,7 @@ def _fuzz_variant(x, v):
881
  if v == "strided_rows":
882
  return x[::2] if x.dim() >= 1 and x.shape[0] >= 2 else x
883
  if v == "offset_slice":
884
- # a storage-offset-1 view: misaligned for vectorized loads
885
  return x.flatten()[1:] if x.numel() > 1 else x
886
  if v == "overflow_expand":
887
  if x.numel() == 0:
@@ -895,8 +1283,7 @@ def _fuzz_variant(x, v):
895
 
896
  def _fuzz_group(op, ref, make, shape, dtype, variants, ulp_tol, device,
897
  stop_on_context_loss=True):
898
- """All variant cases for one (shape, dtype) group; returns (rows,
899
- context_lost)."""
900
  rows = []
901
  bases = _fuzz_build(make, shape, dtype)
902
  lost = False
@@ -933,8 +1320,7 @@ def _fuzz_group(op, ref, make, shape, dtype, variants, ulp_tol, device,
933
  got = op(*xs)
934
  want = ref(*xs)
935
  if v == "overflow_expand":
936
- # bounded comparison: the head plus the high-index tail,
937
- # which is where int32 flat indexing wraps
938
  w = 1 << 20
939
  gf, wf = got.flatten(), want.flatten()
940
  pairs = ((gf[:w], wf[:w]), (gf[-w:], wf[-w:]))
@@ -959,20 +1345,30 @@ def _fuzz_group(op, ref, make, shape, dtype, variants, ulp_tol, device,
959
  return rows, lost
960
 
961
 
962
- # isolate child: a plain subprocess that bootstraps its own metakernel
963
- # (get_kernel, else the dev-root JIT fallback) and unpickles op/ref/make
964
- # by module reference, so it works under get_kernel's synthetic names
965
- _REPO_ID = "phanerozoic/metakernel"
966
- _REPO_VERSION = 1
967
-
968
- _ISOLATE_BOOTSTRAP = r"""
969
- import json
970
- import os
971
- import pickle
972
- import sys
973
-
 
 
 
 
 
 
 
 
 
 
974
  meta = json.load(open(sys.argv[1], "r"))
975
- sys.path[:0] = meta["sys_path"] # BEFORE unpickling module-ref functions
976
  try:
977
  import kernels
978
  _mk = kernels.get_kernel(meta["repo"], version=meta["version"],
@@ -985,85 +1381,165 @@ except Exception:
985
  import load_local
986
  _mk = load_local.load()
987
  import torch
988
- op, ref, make = pickle.load(open(sys.argv[2], "rb"))
989
- rows, _lost = _mk._fuzz_group(
990
- op, ref, make, tuple(meta["shape"]), getattr(torch, meta["dtype"]),
991
- tuple(meta["variants"]), meta["ulp_tol"], meta["device"],
992
- stop_on_context_loss=False)
993
- pickle.dump(rows, open(sys.argv[3], "wb"))
 
 
 
 
 
 
 
 
 
 
 
 
994
  """
995
 
996
 
997
- def fuzz_shapes(op, ref, make, shapes=None, variants=_FUZZ_VARIANTS,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
998
  dtypes=(torch.float32,), ulp_tol=64, isolate=False,
999
- isolate_timeout_s=180, device="cuda"):
1000
- """Run op vs ref across edge shapes, dtypes, and layout mutations;
1001
- report ok / mismatch / exception per case.
1002
-
1003
- `make(shape, dtype)` (or `make(shape)`) builds a tensor or tuple;
1004
- layout variants derive non-contiguous and misaligned views whose
1005
- content equals a contiguous copy. `overflow_expand` stride-0-expands
1006
- past 2**31 logical elements to catch int32 indexing (`skipped_vram`
1007
- when it would not fit). Every case carries a `repro` recipe.
1008
- In-process, the CUDA context is health-checked after exceptions and
1009
- remaining cases marked `context_lost` if killed; `isolate=True`
1010
- spawns each (shape, dtype) group in a subprocess, so a
1011
- context-killing candidate costs one group (`crashed_isolated`);
1012
- op/ref/make must be picklable."""
1013
  if shapes is None:
1014
  shapes = [(), (7,), (0, 8), (1, 1), (1, 17), (3, 63), (17, 129),
1015
  (128, 128), (1023, 5), (2, 4096), (2, 3, 5)]
 
 
1016
 
1017
  rows = []
1018
  lost = False
1019
  if isolate:
1020
  import json
1021
  import pickle
1022
- import subprocess
1023
  import sys as _sys
1024
  import tempfile
1025
  tmpdir = tempfile.mkdtemp(prefix="mk_fuzz_")
1026
  fns = os.path.join(tmpdir, "fns.pkl")
 
 
1027
  with open(fns, "wb") as f:
1028
- pickle.dump((op, ref, make), f)
1029
- for dtype in dtypes:
1030
- for shape in shapes:
1031
- meta = os.path.join(tmpdir, "meta.json")
1032
- out = os.path.join(tmpdir, "rows.pkl")
1033
- if os.path.exists(out):
1034
- os.remove(out)
1035
- with open(meta, "w", encoding="utf-8") as f:
1036
- json.dump({
1037
- "sys_path": [p for p in _sys.path
1038
- if isinstance(p, str)],
1039
- "repo": _REPO_ID, "version": _REPO_VERSION,
 
 
 
 
1040
  "shape": list(shape),
1041
  "dtype": str(dtype).replace("torch.", ""),
1042
  "variants": list(variants),
1043
  "ulp_tol": ulp_tol, "device": device,
1044
- }, f)
1045
- group = None
1046
- stderr_tail = ""
1047
- try:
1048
- r = subprocess.run(
1049
- [_sys.executable, "-c", _ISOLATE_BOOTSTRAP, meta,
1050
- fns, out], timeout=isolate_timeout_s,
1051
- capture_output=True)
1052
- if r.returncode == 0 and os.path.exists(out):
1053
  with open(out, "rb") as f:
1054
- group = pickle.load(f)
1055
- exitcode = r.returncode
1056
- stderr_tail = r.stderr.decode(
1057
- errors="replace")[-400:]
1058
- except subprocess.TimeoutExpired:
1059
- exitcode = "timeout"
1060
- if group is None:
1061
- group = [{"shape": tuple(shape), "variant": "*",
1062
- "dtype": str(dtype).replace("torch.", ""),
1063
- "status": "crashed_isolated",
1064
- "exitcode": exitcode,
1065
- "stderr_tail": stderr_tail}]
1066
- rows.extend(group)
1067
  else:
1068
  pending = [(dt, sh) for dt in dtypes for sh in shapes]
1069
  for gi, (dtype, shape) in enumerate(pending):
@@ -1080,21 +1556,27 @@ def fuzz_shapes(op, ref, make, shapes=None, variants=_FUZZ_VARIANTS,
1080
  n_bad = sum(1 for r in rows
1081
  if r["status"] not in ("ok", "skipped_vram",
1082
  "skipped_empty"))
1083
- return {"cases": rows, "failed": n_bad, "total": len(rows),
1084
- "context_lost": lost}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1085
 
1086
 
1087
  def sweep(factory, grid, iters=30, warmup=5, bytes=None, flops=None,
1088
  check=None, budget_s=None, race=False):
1089
- """Launch-configuration search: `factory(**point) -> callable`; one
1090
- bench row per grid point, sorted fastest first. A point whose factory
1091
- or run raises is reported, not skipped silently. `check(fn) -> bool`
1092
- is an optional correctness gate recorded per row (a fast wrong config
1093
- must not win a sweep). `budget_s` caps total sweep time; points not
1094
- reached are reported as `not_run` rather than silently dropped.
1095
- `race=True` runs successive halving: a quick pass over every point at
1096
- iters/4, then the full budget only on the fastest quartile; pruned
1097
- points keep their quick measurement and status `pruned_race`."""
1098
  keys = list(grid.keys())
1099
 
1100
  def expand(i, acc):
@@ -1159,13 +1641,71 @@ def sweep(factory, grid, iters=30, warmup=5, bytes=None, flops=None,
1159
  continue
1160
  rows.append(run_point(p, iters))
1161
  rows.sort(key=lambda r: (not r.get("check", True), r["median_ms"]))
 
 
 
 
 
 
 
 
 
 
 
1162
  return rows
1163
 
1164
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1165
  def alloc_stamps(n, blocks=None, device="cuda"):
1166
- """Device buffer for the MK_STAMP convention (metakernel_cuda/
1167
- stamps.cuh): the candidate kernel writes clock64() into slot i. With
1168
- `blocks`, allocates the [blocks, n] layout for MK_STAMP_BLOCK."""
1169
  if blocks is not None:
1170
  return torch.zeros(blocks, n, dtype=torch.int64, device=device)
1171
  return torch.zeros(n, dtype=torch.int64, device=device)
@@ -1175,8 +1715,7 @@ _DRIFT_SKIP = ("device", "clock", "launch_overhead", "transfer_bw")
1175
 
1176
 
1177
  def pinned_dossiers():
1178
- """Names of the dossiers pinned in this release (one per certified
1179
- architecture)."""
1180
  return sorted(_PINNED.keys())
1181
 
1182
 
@@ -1195,11 +1734,7 @@ def _numeric_leaves(d, prefix=""):
1195
 
1196
 
1197
  def dossier_drift(current, pinned=None, rel_tol=0.35, skip=_DRIFT_SKIP):
1198
- """Compare a fresh dossier against the release-pinned one for the
1199
- same architecture: per-leaf relative drift, worst drift, entries
1200
- over `rel_tol`. Host-dependent entries and raw clocks are skipped by
1201
- default (`skip=()` compares everything); `pinned=None` auto-selects
1202
- by sm_arch and raises if no pin exists."""
1203
  if pinned is None:
1204
  arch = current["device"]["sm_arch"]
1205
  matches = [k for k, v in _PINNED.items()
@@ -1222,16 +1757,19 @@ def dossier_drift(current, pinned=None, rel_tol=0.35, skip=_DRIFT_SKIP):
1222
  "rel": round(rel, 4)})
1223
  worst = max((e["rel"] for e in entries), default=0.0)
1224
  over = [e for e in entries if e["rel"] > rel_tol]
1225
- return {"entries": entries, "worst_rel": worst, "over_tol": over,
1226
- "rel_tol": rel_tol, "compared": len(entries),
1227
- "pass": len(over) == 0}
 
 
 
 
 
1228
 
1229
 
1230
  def stamped_demo(n=1 << 22, fma_iters=4000, blocks=None, threads=256,
1231
  device="cuda"):
1232
- """Run the shipped MK_STAMP_BLOCK example kernel (global load, FMA
1233
- compute, global store) and decode its stamps; a template for
1234
- instrumenting a candidate."""
1235
  props = torch.cuda.get_device_properties(device)
1236
  if blocks is None:
1237
  blocks = props.multi_processor_count
@@ -1244,11 +1782,7 @@ def stamped_demo(n=1 << 22, fma_iters=4000, blocks=None, threads=256,
1244
 
1245
 
1246
  def read_stamps(stamps, labels=None, clock_ghz=None):
1247
- """Decode MK_STAMP phases: consecutive deltas in ticks and in
1248
- microseconds under the measured (or provided) SM clock. A 1-D buffer
1249
- (MK_STAMP: block 0 only) yields one delta per phase; a 2-D [B, n]
1250
- buffer (MK_STAMP_BLOCK) yields min / median / max across blocks per
1251
- phase, with blocks whose stamps stayed zero excluded."""
1252
  if clock_ghz is None:
1253
  clock_ghz = measure_clock(stamps.device)
1254
 
@@ -1284,3 +1818,104 @@ def read_stamps(stamps, labels=None, clock_ghz=None):
1284
  return {"phases": phases, "clock_ghz": round(clock_ghz, 3),
1285
  "blocks_reporting": int(live.sum().item()),
1286
  "blocks_total": int(live.numel())}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Measuring instrument for the kernel-design loop: measured device facts, throttle-aware timing, adversarial correctness comparison, all as loadable ops returning plain data."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  import math
3
  import os
4
  import time
 
8
  try:
9
  from ._ops import ops
10
  except ImportError: # pragma: no cover - spawned child in a source tree
11
+ # children re-import the package; a source checkout rebuilds the op table from the cached JIT extension
 
 
12
  _root = os.environ.get("METAKERNEL_DEV_ROOT")
13
  if not _root:
14
  raise
 
30
  except ImportError: # pragma: no cover - fresh tree before first pin
31
  _PINNED = {}
32
 
33
+ __all__ = ["probe_device", "probe_all_devices", "bench", "compare", "judge",
34
+ "order_shuffles", "fuzz_shapes", "sweep", "alloc_stamps",
35
+ "read_stamps", "stamped_demo", "measure_clock", "ulp_diff",
36
+ "dossier_drift", "pinned_dossiers", "mma_tile_check", "schema",
37
+ "probe_capturable", "ops"]
38
+
39
+ _REPO_ID = "phanerozoic/metakernel"
40
+ _REPO_VERSION = 1
41
+ _PROBE_SCHEMA = 2
42
+
43
+ # kind id, C-tile M*N, nominal K, nominal K-lanes per unrolled loop iteration
44
  _MMA_KINDS = {
45
+ "fp16": (0, 16 * 16, 16, 64.0),
46
+ "bf16": (1, 16 * 16, 16, 64.0),
47
+ "tf32": (2, 16 * 16, 8, 32.0),
48
+ "int8": (3, 16 * 16, 16, 64.0),
49
+ "fp8_e4m3": (4, 16 * 8, 32, 256.0),
50
+ "fp16_sparse_2_4": (5, 16 * 8, 32, 128.0),
51
+ "fp8_e5m2": (6, 16 * 8, 32, 256.0),
52
  }
53
 
54
 
 
57
 
58
 
59
  def _events(fn, reps):
60
+ """Time reps calls of fn with one cudaEvent pair each; returns a ms list."""
61
  starts = [torch.cuda.Event(enable_timing=True) for _ in range(reps)]
62
  stops = [torch.cuda.Event(enable_timing=True) for _ in range(reps)]
63
  for s, e in zip(starts, stops):
 
69
 
70
 
71
  def measure_clock(device="cuda", ticks=4_000_000, blocks=None):
72
+ """Effective SM clock in GHz from clock64 ticks over wall time under an FMA spin."""
 
73
  props = torch.cuda.get_device_properties(device)
74
  if blocks is None:
75
  blocks = props.multi_processor_count
 
102
 
103
 
104
  def _sattolo_ring(n, device, generator=None):
105
+ """Random single-cycle permutation as an int32 next-pointer table."""
 
106
  perm = torch.randperm(n, generator=generator)
107
  ring = torch.empty(n, dtype=torch.int32)
108
  ring[perm] = torch.roll(perm, -1).to(torch.int32)
 
110
 
111
 
112
  def _smi_settings(index):
113
+ """ECC mode, driver, power limit via nvidia-smi; None fields when unavailable."""
 
114
  import subprocess
115
  try:
116
  out = subprocess.run(
 
130
  return {"ecc": None, "driver": None, "power_limit_w": None}
131
 
132
 
133
+ def _unravel(flat, shape):
134
+ """Multi-dimensional index for a flat index into shape."""
135
+ if not shape:
136
+ return []
137
+ out = []
138
+ for s in reversed(list(shape)):
139
+ out.append(int(flat % s))
140
+ flat //= s
141
+ return list(reversed(out))
142
+
143
+
144
  def mma_tile_check(device="cuda", trials=8):
145
+ """Random small-integer m16n16k16 tiles through the wmma path, compared exactly to the reference GEMM."""
 
 
146
  worst = 0.0
147
  for t in range(trials):
148
  g = torch.Generator(device=device).manual_seed(1000 + t)
 
160
  "trials": trials}
161
 
162
 
163
+ def _tenancy_canary(device, rel_tol=0.10, attempts=3):
164
+ """Fixed-cost triad timed in paired rounds; busy only when every attempt disagrees."""
165
+ n4 = 4 << 20
166
+ a = torch.rand(n4 * 4, device=device)
167
+ b = torch.rand(n4 * 4, device=device)
168
+ c = torch.empty(n4 * 4, device=device)
169
+ ops.mk_triad(a, b, c, 4, 1.5)
170
+ rels = []
171
+ for _ in range(attempts):
172
+ r1 = _median(_events(lambda: ops.mk_triad(a, b, c, 4, 1.5), 3))
173
+ r2 = _median(_events(lambda: ops.mk_triad(a, b, c, 4, 1.5), 3))
174
+ rel = abs(r1 - r2) / min(r1, r2)
175
+ rels.append(round(rel, 4))
176
+ if rel <= rel_tol:
177
+ return False, {"rel_diffs": rels, "rel_tol": rel_tol}
178
+ return True, {"rel_diffs": rels, "rel_tol": rel_tol}
179
+
180
+
181
+ def _cache_dir():
182
+ base = os.environ.get("LOCALAPPDATA") or os.path.expanduser("~/.cache")
183
+ d = os.path.join(base, "metakernel")
184
+ os.makedirs(d, exist_ok=True)
185
+ return d
186
+
187
+
188
+ def _cache_path(device, quick):
189
+ import re
190
+ props = torch.cuda.get_device_properties(device)
191
+ smi = _smi_settings(torch.device(device).index or 0)
192
+ slug = re.sub(r"[^a-z0-9]+", "", props.name.lower())[:24]
193
+ key = "sm%d%d_%s_drv%s_cu%s_q%d_s%d" % (
194
+ props.major, props.minor, slug, smi["driver"] or "na",
195
+ torch.version.cuda, int(quick), _PROBE_SCHEMA)
196
+ return os.path.join(_cache_dir(), key + ".json")
197
+
198
+
199
+ def _mma_calibrate(kind_id, sink):
200
+ """Executed K-lanes per loop iteration from a sub-saturation accumulator read-back, with a linearity check."""
201
+ gains = []
202
+ for cal in (4, 32):
203
+ sink[1] = 0.0
204
+ ops.mk_mma(kind_id, 1, 1, cal, sink)
205
+ _sync()
206
+ gains.append(float(sink[1].item()) / cal)
207
+ linear = gains[0] > 0 and abs(gains[1] - gains[0]) / gains[0] < 0.02
208
+ return gains[0], linear
209
+
210
+
211
+ def _fp8_accum_stall(kind_id, sink, k_exec):
212
+ """Growth curve of the fp8 accumulator; a stall reveals the effective accumulate mantissa width."""
213
+ vals = []
214
+ for it in (32, 64, 128, 256, 512):
215
+ sink[1] = 0.0
216
+ ops.mk_mma(kind_id, 1, 1, it, sink)
217
+ _sync()
218
+ vals.append(float(sink[1].item()) / 2.0)
219
+ if vals[-1] == vals[-2] and vals[-1] < vals[0] * 16 * 0.99:
220
+ bits = int(round(math.log2(vals[-1] / (2.0 * k_exec))))
221
+ return {"stall_element_value": vals[-1],
222
+ "effective_accum_mantissa_bits": bits}
223
+ return {"stall_element_value": None,
224
+ "effective_accum_mantissa_bits": None}
225
+
226
+
227
+ def probe_device(device="cuda", quick=False, cached=True,
228
+ tenancy_check=True):
229
+ """Empirical device dossier; every entry measured by a dedicated probe kernel and stamped with its SM clock."""
230
+ cpath = _cache_path(device, quick)
231
+ if cached and os.path.exists(cpath):
232
+ import json
233
+ return json.load(open(cpath, encoding="utf-8"))
234
+ if tenancy_check:
235
+ busy, ev = _tenancy_canary(device)
236
+ if busy:
237
+ raise RuntimeError("device_busy: canary rounds disagree %s; "
238
+ "idle the device or pass "
239
+ "tenancy_check=False" % ev)
240
  props = torch.cuda.get_device_properties(device)
241
  sm = props.multi_processor_count
242
  l2 = props.L2_cache_size
 
249
  "vram_bytes": props.total_memory,
250
  "torch": torch.__version__,
251
  "cuda_runtime": torch.version.cuda,
252
+ # instrument settings recorded because readings depend on them
 
253
  **_smi_settings(torch.device(device).index or 0),
254
  }
255
  }
 
274
  }
275
  clk = max(traj)
276
 
277
+ # ---- device-memory bandwidth per access width plus read/write/gather
278
+ n4 = 16 << 20
 
279
  if props.total_memory < 4 << 30:
280
  n4 = 4 << 20
281
  a = torch.rand(n4 * 4, device=device)
 
299
  dtype=torch.int32).to(device)
300
  ops.mk_gather(a, idx, c, 256, 0)
301
  ms = _median(_events(lambda: ops.mk_gather(a, idx, c, 256, 0), reps))
 
302
  hbm["random_gather_16B"] = round(
303
  (idx.numel() * 4 + 2 * n4 * 16) / (ms * 1e-3) / 1e9, 1)
304
  d["dram_bw"] = _entry(hbm, "GB/s", "streaming triad c=a+s*b per access "
 
309
  "so its nominal number is the achievable rate for "
310
  "random access, not a line-efficiency claim")
311
 
312
+ # ---- L2 bandwidth over an L2-resident working set
313
  ws = max(l2 // 4, 1 << 20)
314
+ nl2 = ws // 16
315
  al = a[:nl2 * 4].contiguous()
316
  bl = b[:nl2 * 4].contiguous()
317
  cl = c[:nl2 * 4].contiguous()
 
326
  "medium", "first pass streams from HBM; amortized "
327
  f"over {passes} passes")
328
 
329
+ # ---- latencies: min of 3 chases per level so a co-tenant burst cannot land in the dossier
 
 
330
  lat = {}
331
  idle_clk = measure_clock(device, ticks=2_000_000, blocks=1)
 
 
 
 
 
 
 
332
 
333
+ def chase(op, ring, warm_hops, hops, extra=None):
334
+ best = None
335
+ for _ in range(3):
336
+ if extra is None:
337
+ op(ring, warm_hops, out64)
338
+ _sync()
339
+ op(ring, hops, out64)
340
+ else:
341
+ op(ring, extra, warm_hops, out64)
342
+ _sync()
343
+ op(ring, extra, hops, out64)
344
+ _sync()
345
+ t = int(out64[0].item()) / hops
346
+ best = t if best is None else min(best, t)
347
+ return best
 
 
348
 
349
+ ring_s = _sattolo_ring(8192, device)
350
+ lat["smem_cycles"] = round(
351
+ chase(lambda r, h, o: ops.mk_chase_shared(r, h, o), ring_s,
352
+ 1 << 14, 1 << 16), 1)
353
+ ring_l1 = _sattolo_ring((16 << 10) // 4, device)
354
+ lat["l1_cycles"] = round(
355
+ chase(lambda r, h, o: ops.mk_chase_global(r, h, o), ring_l1,
356
+ ring_l1.numel(), 1 << 15), 1)
357
+ ring_l2 = _sattolo_ring((2 << 20) // 4, device)
358
+ lat["l2_cycles"] = round(
359
+ chase(lambda r, h, o: ops.mk_chase_global(r, h, o), ring_l2,
360
+ ring_l2.numel(), 1 << 14), 1)
361
  hbm_bytes = min(max(4 * l2, 64 << 20), props.total_memory // 8)
362
  ring_h = _sattolo_ring(int(hbm_bytes) // 4, device)
363
  hops_h = max((2 * l2) // 128, 1 << 17)
364
+ lat["dram_cycles"] = round(
365
+ chase(lambda r, h, o: ops.mk_chase_global(r, h, o), ring_h,
366
+ 1 << 14, hops_h), 1)
 
 
367
  lat["ns_per_cycle"] = round(1.0 / idle_clk, 3)
368
  d["latency"] = _entry(lat, "cycles (single-lane dependent loads)",
369
+ "Sattolo-cycle pointer chase, min of 3 runs; 16 "
370
+ "KB ring for L1, 2 MB TLB-resident ring for L2, "
371
+ "self-evicting chain (2x L2 in touched lines) "
372
+ "for HBM", idle_clk, "high",
373
  "smem includes index arithmetic; HBM includes "
374
  "residual TLB effects at large footprints")
375
  del ring_h, ring_l2, ring_l1, ring_s
376
 
377
+ # ---- fp32/fp64 FMA and tensor-core throughput
378
  blocks = sm * 4
379
  iters = 20_000 if quick else 60_000
380
  ops.mk_fma_f32(blocks, 256, iters, sink)
381
  ms = _median(_events(lambda: ops.mk_fma_f32(blocks, 256, iters, sink),
382
  reps))
383
+ flops = blocks * 256 * iters * 16 * 2 * 2
384
  d["fma_f32"] = _entry(round(flops / (ms * 1e-3) / 1e12, 2), "TFLOP/s",
385
  "register-resident FMA chains", clk, "high")
386
  sink64 = torch.zeros(2, dtype=torch.float64, device=device)
 
392
  d["fma_f64"] = _entry(round(flops64 / (ms * 1e-3) / 1e12, 3), "TFLOP/s",
393
  "register-resident double FMA chains (the DFMA "
394
  "pipe)", clk, "high")
395
+
396
+ # ---- ILP ladder: 1/2/4 chains at low occupancy separates latency-bound from pipe-bound
397
+ ilp = {}
398
+ it_ilp = 20_000 if quick else 60_000
399
+ for ch in (1, 2, 4):
400
+ ops.mk_fma_ilp(ch, sm, 64, it_ilp, sink)
401
+ ms = _median(_events(
402
+ lambda c2=ch: ops.mk_fma_ilp(c2, sm, 64, it_ilp, sink), reps))
403
+ ilp[f"ilp{ch}"] = round(
404
+ sm * 64 * it_ilp * 16 * 2 * ch / (ms * 1e-3) / 1e12, 2)
405
+ ilp["ratio_4_1"] = round(ilp["ilp4"] / ilp["ilp1"], 2)
406
+ d["fma_ilp"] = _entry(ilp, "TFLOP/s",
407
+ "1/2/4 independent dependent FMA chains per "
408
+ "thread at one block of 64 threads per SM; a "
409
+ "ratio near 4 means latency-bound at that "
410
+ "occupancy", clk, "high")
411
+
412
  mma = {}
413
  mma_klanes = {}
414
+ fp8_accum = {}
415
+ for name, (kind, mn, k_nom, nom_lanes) in _MMA_KINDS.items():
416
  it = 30_000 if quick else 100_000
417
  r = ops.mk_mma(kind, blocks, 8, 512, sink)
418
  if r != 0:
419
  mma[name] = "unsupported"
420
  continue
421
  _sync()
422
+ gain, linear = _mma_calibrate(kind, sink)
 
 
 
 
 
 
423
  mma_klanes[name] = {
424
+ "executed_k_per_iter": round(gain, 2),
425
+ "vs_nominal": round(gain / nom_lanes, 3),
426
+ "calibration_linear": linear,
427
  }
428
+ if name.startswith("fp8"):
429
+ fp8_accum[name] = _fp8_accum_stall(kind, sink, k_nom)
430
  ms = _median(_events(
431
  lambda k=kind, i=it: ops.mk_mma(k, blocks, 8, i, sink), reps))
432
+ tflops = blocks * 8 * it * 2.0 * mn * gain / (ms * 1e-3) / 1e12
433
+ if name == "fp16_sparse_2_4":
434
+ mma[name] = {"executed": round(tflops, 1),
435
+ "dense_equivalent": round(2 * tflops, 1)}
436
+ else:
437
+ mma[name] = round(tflops, 1)
438
  d["mma"] = _entry(mma, "TFLOP/s (TOP/s for int8)",
439
  "register-fragment mma_sync loops (wmma; fp8 and "
440
+ "2:4-sparse fp16 via inline PTX m16n8k32); flops "
441
+ "derive from a sub-saturation accumulator read-back "
 
442
  "with every fragment element observed, never the "
443
+ "declared loop shape; sparse reports executed and "
444
+ "dense-equivalent flops",
445
  clk, "medium",
446
  "dense-tile rate without memory traffic; real "
447
  "kernels see this only when staging hides behind it")
448
  d["mma"]["verified_k_lanes"] = mma_klanes
449
+ if fp8_accum:
450
+ d["mma"]["fp8_accum"] = fp8_accum
451
 
452
+ # ---- fed MMA: smem-staged fp16 tiles under cp.async double buffering
453
+ if props.major >= 8:
454
+ gbuf = torch.ones(16 << 20, dtype=torch.float16, device=device)
455
+ sink[1] = 0.0
456
+ r = ops.mk_mma_fed(gbuf, 1, 1, 64, sink)
457
+ if r == 0:
458
+ _sync()
459
+ fed_gain = float(sink[1].item()) / 64
460
+ it_fed = 6_000 if quick else 20_000
461
+ ops.mk_mma_fed(gbuf, sm * 2, 8, it_fed, sink)
462
+ ms = _median(_events(
463
+ lambda: ops.mk_mma_fed(gbuf, sm * 2, 8, it_fed, sink),
464
+ reps))
465
+ fed = sm * 2 * 8 * it_fed * 2.0 * 256 * fed_gain / \
466
+ (ms * 1e-3) / 1e12
467
+ d["mma_fed"] = _entry(
468
+ round(fed, 1), "TFLOP/s",
469
+ "m16n16k16 fp16 tiles fed from an L2-resident buffer "
470
+ "through cp.async double-buffered shared memory; "
471
+ "accumulator-verified", clk, "high",
472
+ "the achieved counterpart to the register peak: staging "
473
+ "exposed, one dependent accumulator chain per warp")
474
+ del gbuf
475
+
476
+ # ---- shared-memory bandwidth and the bank-conflict stride sweep
477
  it_sm = 30_000 if quick else 100_000
478
  ops.mk_smem_bw(blocks, 256, it_sm, sink)
479
  ms = _median(_events(lambda: ops.mk_smem_bw(blocks, 256, it_sm, sink),
 
483
  "block-resident read+write streaming (8 B per "
484
  "thread-iteration), aggregated across SMs", clk,
485
  "medium",
486
+ "bank-conflict-light access pattern; the stride "
487
+ "sweep below prices the conflicted case")
488
+ strides = (1, 8, 32) if quick else (1, 2, 4, 8, 16, 32)
489
+ it_st = 8_000 if quick else 20_000
490
+ st_ms = {}
491
+ for s2 in strides:
492
+ ops.mk_smem_stride(s2, blocks, 256, it_st, sink)
493
+ st_ms[s2] = _median(_events(
494
+ lambda s3=s2: ops.mk_smem_stride(s3, blocks, 256, it_st, sink),
495
+ reps))
496
+ d["smem_conflict_penalty"] = _entry(
497
+ {f"stride_{s2}": round(st_ms[s2] / st_ms[1], 2) for s2 in strides},
498
+ "x (time ratio vs stride 1)",
499
+ "dependent read-FMA-write per thread at warp-lane bank distance "
500
+ "`stride`; stride 32 puts every lane in one bank", clk, "high",
501
+ "the direct conflict measurement that disambiguates a "
502
+ "bank-conflict signature from an ILP stall")
503
 
504
  # ---- PCIe / host transfer bandwidth
505
  nbytes = 256 << 20
 
530
  "d2d counts one direction of traffic")
531
  del host_pin, host_pag, dev_t, dev_u
532
 
533
+ # ---- atomic contention curve
 
 
534
  n_thr = blocks * 256
535
  buf = torch.zeros(n_thr, dtype=torch.float32, device=device)
536
  at = {"curve_slots": {}}
 
540
  buf.zero_()
541
  ops.mk_atomics(1, blocks, 256, it_at, buf, slots)
542
  ms = _median(_events(
543
+ lambda s4=slots: ops.mk_atomics(1, blocks, 256, it_at, buf, s4),
544
  reps))
545
  at["curve_slots"][str(slots)] = round(
546
  n_thr * it_at / (ms * 1e-3) / 1e9, 2)
 
553
  "of distinct global addresses from 1 (full "
554
  "contention) to one per thread", clk, "high")
555
 
556
+ # ---- launch overhead: torch-dispatch path and the raw C++ path
557
  anchor = sink
558
  n_launch = 500 if quick else 2000
559
  _sync()
 
568
  ops.mk_empty(anchor)
569
  _sync()
570
  rt = (time.perf_counter() - t0) / n_rt * 1e6
571
+ raw_q = float(ops.mk_launch_raw(n_launch, 0, anchor))
572
+ raw_rt = float(ops.mk_launch_raw(n_rt, 1, anchor))
573
  d["launch_overhead"] = _entry(
574
+ {"torch_queued_us": round(queued, 2),
575
+ "torch_round_trip_us": round(rt, 2),
576
+ "raw_queued_us": round(raw_q / n_launch * 1e6, 2)
577
+ if raw_q >= 0 else None,
578
+ "raw_round_trip_us": round(raw_rt / n_rt * 1e6, 2)
579
+ if raw_rt >= 0 else None},
580
+ "us", "empty-kernel trains; torch path per-launch through op "
581
+ "dispatch, raw path looped inside one binding call", clk,
582
+ "medium", "host-state-dependent; the torch-minus-raw gap is the "
583
+ "dispatch cost a torch-launched candidate pays")
584
 
585
  # ---- grid barrier
586
  bar_iters = 500 if quick else 2000
 
596
  "unavailable or grid too large", clk,
597
  "high")
598
 
599
+ # ---- occupancy API table plus timed knees
 
 
600
  occ = {}
601
  for threads in (128, 256, 512, 1024):
602
  row = {}
 
617
  ops.mk_fma_dep(knee_blocks, threads, smem_kb * 1024,
618
  knee_iters, sink)
619
  ms = _median(_events(
620
+ lambda t=threads, s5=smem_kb: ops.mk_fma_dep(
621
+ knee_blocks, t, s5 * 1024, knee_iters, sink), reps))
622
  fl = knee_blocks * threads * knee_iters * 16 * 2
623
  row[f"{smem_kb}KB"] = round(fl / (ms * 1e-3) / 1e12, 2)
624
  knees[f"t{threads}"] = row
 
627
  "at forced (threads, dyn smem) occupancies over fixed total work; "
628
  "the cliff marks where occupancy stops covering latency",
629
  clk, "high", "timing-derived counterpart to the API table above")
630
+ if cached:
631
+ import json
632
+ json.dump(d, open(cpath, "w", encoding="utf-8"))
633
  return d
634
 
635
 
636
+ def probe_all_devices(quick=True, cached=True, tenancy_check=True):
637
+ """One dossier per visible device plus a pairwise P2P bandwidth matrix."""
638
+ n = torch.cuda.device_count()
639
+ devices = {}
640
+ for i in range(n):
641
+ devices[i] = probe_device(f"cuda:{i}", quick=quick, cached=cached,
642
+ tenancy_check=tenancy_check)
643
+ p2p = {}
644
+ if n > 1:
645
+ nbytes = 64 << 20
646
+ for i in range(n):
647
+ for j in range(n):
648
+ if i == j:
649
+ continue
650
+ src = torch.rand(nbytes // 4, device=f"cuda:{i}")
651
+ dst = torch.empty(nbytes // 4, device=f"cuda:{j}")
652
+ dst.copy_(src)
653
+ torch.cuda.synchronize(i)
654
+ torch.cuda.synchronize(j)
655
+ t0 = time.perf_counter()
656
+ for _ in range(5):
657
+ dst.copy_(src)
658
+ torch.cuda.synchronize(i)
659
+ torch.cuda.synchronize(j)
660
+ ms = (time.perf_counter() - t0) / 5 * 1e3
661
+ p2p[f"{i}->{j}"] = {
662
+ "gbs": round(nbytes / (ms * 1e-3) / 1e9, 1),
663
+ "peer_access": bool(
664
+ torch.cuda.can_device_access_peer(i, j)),
665
+ }
666
+ del src, dst
667
+ return {"devices": devices, "p2p": p2p}
668
+
669
+
670
+ _CAPTURE_PROBE = r"""
671
+ import pickle, sys, torch
672
+ fn = pickle.load(open(sys.argv[1], "rb"))
673
+ for _ in range(3):
674
+ fn()
675
+ torch.cuda.synchronize()
676
+ try:
677
+ g = torch.cuda.CUDAGraph()
678
+ with torch.cuda.graph(g):
679
+ fn()
680
+ print("capturable", flush=True)
681
+ except Exception as e:
682
+ print("uncapturable: %s: %s" % (type(e).__name__, str(e)[:120]),
683
+ flush=True)
684
+ """
685
+
686
+
687
+ def probe_capturable(fn, timeout_s=180):
688
+ """Test CUDA-graph capturability of fn in a child process, so a capture-wedged RNG never reaches the caller."""
689
+ import pickle
690
+ import subprocess
691
+ import sys as _sys
692
+ import tempfile
693
+ try:
694
+ blob = pickle.dumps(fn)
695
+ except Exception: # noqa: BLE001
696
+ return {"capturable": None,
697
+ "reason": "fn is not picklable; probe skipped (define fn "
698
+ "at module top level to enable it)"}
699
+ path = os.path.join(tempfile.mkdtemp(prefix="mk_cap_"), "fn.pkl")
700
+ with open(path, "wb") as f:
701
+ f.write(blob)
702
+ try:
703
+ r = subprocess.run([_sys.executable, "-c", _CAPTURE_PROBE, path],
704
+ capture_output=True, text=True,
705
+ timeout=timeout_s)
706
+ except Exception as e: # noqa: BLE001
707
+ return {"capturable": None,
708
+ "reason": f"probe child failed: {type(e).__name__}"}
709
+ out = (r.stdout or "").strip()
710
+ if out == "capturable":
711
+ return {"capturable": True, "reason": ""}
712
+ if out.startswith("uncapturable"):
713
+ return {"capturable": False, "reason": out}
714
+ return {"capturable": None,
715
+ "reason": "probe child exit %s: %s" % (
716
+ r.returncode, (r.stderr or "")[-200:])}
717
+
718
+
719
+ def _bench_verdict(rep):
720
+ parts = ["median %.4g ms" % rep["median_ms"]]
721
+ if "achieved_tflops" in rep:
722
+ parts.append("%.4g TFLOP/s" % rep["achieved_tflops"])
723
+ elif "achieved_gbs" in rep:
724
+ parts.append("%.4g GB/s" % rep["achieved_gbs"])
725
+ if "roofline" in rep:
726
+ parts.append("%.0f%% of %s roof" % (rep["roofline"]["pct_of_roof"],
727
+ rep["roofline"]["bound"]))
728
+ if rep["rejected_throttled"]:
729
+ parts.append("%d/%d rejected (%s)" % (
730
+ rep["rejected_throttled"], rep["samples"],
731
+ rep["rejection_basis"]))
732
+ if rep.get("no_clean_window"):
733
+ parts.append("no clean window: the whole run was throttled")
734
+ if rep.get("alloc_growth_bytes_per_call"):
735
+ parts.append("fn allocates %d B/call" %
736
+ rep["alloc_growth_bytes_per_call"])
737
+ if rep.get("host_overhead_suspected"):
738
+ parts.append("fn appears to sync or do host work inside")
739
+ return "; ".join(parts)
740
+
741
+
742
  def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
743
  dtype="fp16", clock_guard=0.97, clock_stride=8, graph=False,
744
+ graph_probe_isolated=False, device="cuda"):
745
+ """Median-of-N cudaEvent timing with throttle rejection, roofline placement, and footgun detection."""
746
+ for name, v in (("iters", iters), ("warmup", warmup)):
747
+ if not isinstance(v, int) or v < (1 if name == "iters" else 0):
748
+ raise ValueError(f"{name} must be a non-negative int; "
749
+ f"got {v!r}")
750
+ for name, v in (("bytes", bytes), ("flops", flops)):
751
+ if v is not None and (not isinstance(v, (int, float)) or v <= 0):
752
+ raise ValueError(f"{name} must be a positive number counting "
753
+ f"{name} per call of fn; got {v!r}")
 
754
  for _ in range(warmup):
755
  fn()
756
  _sync()
757
+ # footgun probes: per-call allocation growth and hidden host work
758
+ alloc0 = torch.cuda.memory_allocated(device)
759
+ fn()
760
+ fn()
761
+ _sync()
762
+ alloc_growth = max(0, (torch.cuda.memory_allocated(device) - alloc0)
763
+ // 2)
764
+ # a burst of enqueues that leaves the stream idle means fn drains it; only trustworthy above the launch-overhead floor
765
+ for _ in range(16):
766
+ fn()
767
+ stream_idle = bool(torch.cuda.current_stream(device).query())
768
+ _sync()
769
+ t0 = time.perf_counter()
770
+ pre = _events(fn, 4)
771
+ wall_ms = (time.perf_counter() - t0) * 1e3
772
+ ev_ms = sum(pre)
773
+ host_suspect = (stream_idle and ev_ms / 4 > 0.05) or \
774
+ wall_ms > ev_ms * 1.5 + 2.0
775
+
776
  graph_state = "off"
777
  graph_reason = ""
778
+ if graph and graph_probe_isolated:
779
+ cap = probe_capturable(fn)
780
+ if cap["capturable"] is False:
781
+ graph = False
782
+ graph_state = "fallback_eager"
783
+ graph_reason = "isolated probe: " + cap["reason"]
784
  if graph:
785
  pre_rng = torch.cuda.get_rng_state(device)
786
  try:
 
795
  except Exception as e: # noqa: BLE001 - fall back, say why
796
  graph_state = "fallback_eager"
797
  graph_reason = f"{type(e).__name__}: {str(e)[:120]}"
798
+ # drain the invalidated stream; a failed capture can wedge the device RNG on some torch versions
 
 
 
799
  rng_ok = True
800
  for _attempt in range(5):
801
  try:
 
825
  samples.append((_events(fn, n), len(clocks) - 1))
826
  clocks.append(measure_clock(device, ticks=probe_ticks))
827
  best_clk = max(clocks)
828
+
829
+ # rejection basis: memory-bound work is judged on its own rate, not the SM clock
830
+ ridge_memory = False
831
+ if dossier is not None and bytes and flops:
832
+ bw0 = max(dossier["dram_bw"]["value"].values()) * 1e9
833
+ mma_v0 = dossier["mma"]["value"].get(dtype)
834
+ if isinstance(mma_v0, dict):
835
+ mma_v0 = mma_v0.get("dense_equivalent")
836
+ peak0 = (mma_v0 if isinstance(mma_v0, (int, float))
837
+ else dossier["fma_f32"]["value"]) * 1e12
838
+ ridge_memory = (flops / bytes) * bw0 < peak0
839
+ basis = "bandwidth_consistency" if (
840
+ bytes is not None and (flops is None or ridge_memory)) \
841
+ else "sm_clock"
842
+
843
  flat = []
844
+ kept = []
845
+ rejected = 0
846
+ if basis == "sm_clock":
847
+ for group, ci in samples:
848
+ window_ok = (clocks[ci] >= clock_guard * best_clk and
849
+ clocks[min(ci + 1, len(clocks) - 1)] >=
850
+ clock_guard * best_clk)
851
+ for ms in group:
852
+ flat.append(ms)
853
+ if window_ok:
854
+ kept.append(ms)
855
+ else:
856
+ rejected += 1
857
+ else:
858
+ for group, _ci in samples:
859
+ flat.extend(group)
860
+ floor = sorted(flat)[max(0, int(0.05 * len(flat)) - 1)]
861
+ for ms in flat:
862
+ if ms <= floor * 1.15:
863
  kept.append(ms)
864
  else:
865
  rejected += 1
866
+ all_rejected = not kept
867
+ if all_rejected:
868
  kept = flat
869
  med = _median(kept)
870
  ks = sorted(kept)
871
  n_k = len(ks)
872
+ # order-statistic 95% CI on the median; degenerate at tiny n
 
873
  half = 1.959964 * (n_k ** 0.5) / 2.0
874
  lo_i = max(0, int(n_k / 2 - half) - 1)
875
  hi_i = min(n_k - 1, int(n_k / 2 + half))
 
884
  "mean_ms": round(sum(kept) / n_k, 4),
885
  "samples": len(flat),
886
  "rejected_throttled": rejected,
887
+ "rejection_basis": basis,
888
  "graph_captured": graph_state == "captured",
889
  "clock_ghz": {"best": round(best_clk, 3),
890
  "worst": round(min(clocks), 3)},
891
  }
892
+ no_clean = all_rejected
893
+ if basis == "sm_clock" and dossier is not None:
894
+ ref_clk = dossier["clock"]["max_ghz"]
895
+ if best_clk < clock_guard * ref_clk:
896
+ no_clean = True
897
+ if no_clean:
898
+ rep["no_clean_window"] = True
899
  if graph_reason:
900
  rep["graph_fallback_reason"] = graph_reason
901
+ if alloc_growth:
902
+ rep["alloc_growth_bytes_per_call"] = int(alloc_growth)
903
+ if host_suspect:
904
+ rep["host_overhead_suspected"] = True
905
+ rep["host_wall_over_event"] = round(wall_ms / max(ev_ms, 1e-9), 2)
906
  if bytes is not None:
907
  rep["achieved_gbs"] = round(bytes / (med * 1e-3) / 1e9, 1)
908
  if flops is not None:
 
912
  if dossier is not None and bytes and flops:
913
  bw = max(dossier["dram_bw"]["value"].values()) * 1e9
914
  mma_v = dossier["mma"]["value"].get(dtype)
915
+ if isinstance(mma_v, dict):
916
+ mma_v = mma_v.get("dense_equivalent")
917
  peak = (mma_v if isinstance(mma_v, (int, float))
918
  else dossier["fma_f32"]["value"]) * 1e12
919
  ai = flops / bytes
 
924
  "pct_of_roof": round(100 * (flops / (med * 1e-3)) / roof, 1),
925
  "peak_dtype": dtype,
926
  }
927
+ rep["verdict"] = _bench_verdict(rep)
928
  return rep
929
 
930
 
931
  def _to_ordered_int(t):
932
+ """Monotone integer mapping of float bit patterns; differences are ULP distances."""
 
 
933
  if t.dtype == torch.float32:
934
  u = t.view(torch.int32).to(torch.int64) & 0xFFFFFFFF
935
  half = 1 << 31
 
945
 
946
 
947
  def ulp_diff(a, b):
948
+ """Elementwise ULP distance; both-NaN counts 0, one-sided NaN 2**62, integers by absolute difference."""
949
+ if a.dtype != b.dtype or a.shape != b.shape:
950
+ raise ValueError("ulp_diff needs matching dtype and shape; got "
951
+ f"{a.dtype}{tuple(a.shape)} vs "
952
+ f"{b.dtype}{tuple(b.shape)}; cast or reshape the "
953
+ "candidate to the reference layout")
954
  if not a.is_floating_point():
955
  return (a.to(torch.int64) - b.to(torch.int64)).abs()
956
  ia, ib = _to_ordered_int(a), _to_ordered_int(b)
 
967
 
968
  @staticmethod
969
  def last_dim():
970
+ """Identical permutation of every arg's last dim: reduction order changes, value does not."""
 
 
971
  def shuffle(args, seed):
972
  g = torch.Generator(device=args[0].device).manual_seed(seed)
973
  p = torch.randperm(args[0].shape[-1], device=args[0].device,
 
977
 
978
  @staticmethod
979
  def matmul_k():
980
+ """Consistent K permutation of (A [.., M, K], B [.., K, N]): product unchanged, accumulation order not."""
 
981
  def shuffle(args, seed):
982
  a_m, b_m = args[0], args[1]
983
  g = torch.Generator(device=a_m.device).manual_seed(seed)
 
991
  return list(x) if isinstance(x, (tuple, list)) else [x]
992
 
993
 
994
+ def _compare_hints(trial):
995
+ hints = []
996
+ if trial.get("bias_one_sided_frac", 0) >= 0.9 and \
997
+ trial.get("frac_above_band", 0) > 0.5:
998
+ hints.append("systematic one-sided bias: check scale factors or "
999
+ "the accumulator dtype")
1000
+ if trial["nan_cand"] > trial["nan_ref"]:
1001
+ hints.append("candidate introduces NaN: check uninitialized "
1002
+ "memory or masking")
1003
+ nd = trial.get("first_divergent_nd")
1004
+ if nd and nd[-1] > 0 and any(nd[-1] % t == 0 for t in (16, 32, 64,
1005
+ 128)):
1006
+ hints.append("divergence starts on a tile-aligned index: check "
1007
+ "boundary indexing")
1008
+ return hints
1009
+
1010
+
1011
+ def compare(ref, cand, gen=None, trials=8, order_shuffle=None,
1012
+ order_trials=8, ref_variants=None, ulp_tol=None, atol=None,
1013
+ rtol=None, band_margin=4.0, zero_atol=None, worst_k=8, seed=0,
1014
+ inputs=None, frac_above_tol=0.5):
1015
+ """Adversarial correctness comparison with pointwise order bands, ULP evidence, and a one-sided-bias gate; the banded route cannot reject errors under band_margin*(band+2) ULP at order-stable elements."""
1016
+ if (gen is None) == (inputs is None):
1017
+ raise ValueError("pass exactly one of gen (a seed -> args "
1018
+ "callable) or inputs (a tuple of concrete "
1019
+ "tensors)")
1020
+ if inputs is not None:
1021
+ if isinstance(inputs, torch.Tensor):
1022
+ inputs = (inputs,)
1023
+ if not all(isinstance(x, torch.Tensor) for x in inputs):
1024
+ raise ValueError("inputs must be tensors; wrap scalars with "
1025
+ "torch.tensor first")
1026
+ fixed = tuple(inputs)
1027
+ gen = lambda seed: fixed # noqa: E731
1028
+ trials = 1
1029
  worst = {"max_ulp": -1}
1030
  total_hist = {}
1031
  per_trial = []
1032
  max_abs_seen = 0.0
1033
  max_rel_seen = 0.0
1034
+ out_shapes = []
1035
  for t in range(trials):
1036
  args = gen(seed + t)
1037
+ if not isinstance(args, (tuple, list)):
1038
+ raise ValueError("gen(seed) must return a tuple of args; got "
1039
+ f"{type(args).__name__}; return (x,) for a "
1040
+ "single-argument op")
1041
  r_outs = _as_outputs(ref(*args))
1042
  c_outs = _as_outputs(cand(*args))
1043
+ if len(r_outs) != len(c_outs):
1044
+ raise ValueError(f"reference returned {len(r_outs)} outputs, "
1045
+ f"candidate {len(c_outs)}; make the arities "
1046
+ "match")
1047
+ for oi, (r, c) in enumerate(zip(r_outs, c_outs)):
1048
+ if r.dtype != c.dtype:
1049
+ raise ValueError(f"output {oi} dtype mismatch: ref "
1050
+ f"{r.dtype}, cand {c.dtype}; cast the "
1051
+ "candidate to the reference dtype")
1052
+ if r.shape != c.shape:
1053
+ raise ValueError(f"output {oi} shape mismatch: ref "
1054
+ f"{tuple(r.shape)}, cand "
1055
+ f"{tuple(c.shape)}")
1056
+ out_shapes = [tuple(r.shape) for r in r_outs]
1057
+ # pointwise band: each element judged against its own measured order sensitivity
1058
  bands = None
1059
  band = 0
1060
  band_score = 0.0
 
1078
  max_ulp = 0
1079
  mean_ulp = 0.0
1080
  first_div = -1
1081
+ first_div_nd = None
1082
  n_el = 0
1083
  n_above_band = 0
1084
+ n_diverging = 0
1085
+ n_cand_high = 0
1086
  nan_r = nan_c = inf_r = inf_c = 0
1087
  worst_elements = []
1088
  for oi, (r, c) in enumerate(zip(r_outs, c_outs)):
 
1100
  mean_ulp += float(d.double().sum().item())
1101
  n_el += d.numel()
1102
  nz = (d > 0)
1103
+ n_diverging += int(nz.sum().item())
1104
+ n_cand_high += int(((c > r) & nz).sum().item())
1105
  if first_div < 0 and nz.any():
1106
  first_div = int(nz.flatten().nonzero()[0].item())
1107
+ first_div_nd = _unravel(first_div, r.shape)
1108
  if nz.any():
1109
  buckets = torch.clamp(
1110
  torch.log2(d[nz].double()).floor(), 0, 62
 
1121
  if val > 0:
1122
  worst_elements.append({
1123
  "output": oi, "index": int(ix),
1124
+ "index_nd": _unravel(int(ix), r.shape),
1125
  "ref": float(rf[ix].item()),
1126
  "cand": float(cf[ix].item()),
1127
  "ulp": int(val)})
 
1141
  inf_r += int(torch.isinf(r).sum().item())
1142
  inf_c += int(torch.isinf(c).sum().item())
1143
  worst_elements.sort(key=lambda w: -w["ulp"])
1144
+ frac_pos = n_cand_high / max(n_diverging, 1)
1145
  trial_rep = {
1146
  "max_ulp": max_ulp,
1147
  "mean_ulp": round(mean_ulp / max(n_el, 1), 3),
1148
  "first_divergent_index": first_div,
1149
+ "first_divergent_nd": first_div_nd,
1150
  "nan_ref": nan_r, "nan_cand": nan_c,
1151
  "inf_ref": inf_r, "inf_cand": inf_c,
1152
  "order_band_ulp": band,
1153
  "band_score": round(band_score, 2),
1154
  "frac_above_band": round(n_above_band / max(n_el, 1), 6),
1155
+ "bias_one_sided_frac": round(max(frac_pos, 1 - frac_pos), 4)
1156
+ if n_diverging else 0.0,
1157
  "worst_elements": worst_elements[:worst_k],
1158
  }
1159
  per_trial.append(trial_rep)
1160
  if max_ulp > worst["max_ulp"]:
1161
  worst = trial_rep
 
1162
  max_seen = max(t["max_ulp"] for t in per_trial)
1163
+ banded = order_shuffle is not None or ref_variants
1164
+ if banded:
1165
  score = max(t["band_score"] for t in per_trial)
1166
  ulp_ok = score <= band_margin
1167
  criterion = (f"pointwise max(ulp / (order_band + 2)) = "
 
1176
  max_rel_seen <= (rtol or 0) or max_abs_seen <= (atol or 0)
1177
  criterion += (f"; OR max_abs {max_abs_seen:.3e} <= atol {atol} / "
1178
  f"max_rel {max_rel_seen:.3e} <= rtol {rtol}")
1179
+ # one-sided bias gate: a uniform small error is above-band nearly everywhere in one direction
1180
+ bias_fail = False
1181
+ if banded and (ulp_ok or tol_ok):
1182
+ w_frac = max(t["frac_above_band"] for t in per_trial)
1183
+ w_side = max(t["bias_one_sided_frac"] for t in per_trial)
1184
+ if w_frac > frac_above_tol and w_side >= 0.9:
1185
+ bias_fail = True
1186
+ criterion += ("; FAILED bias gate: frac_above_band %.3f > %s "
1187
+ "with one-sided fraction %.2f" %
1188
+ (w_frac, frac_above_tol, w_side))
1189
+ passed = bool((ulp_ok or tol_ok) and not bias_fail)
1190
+ hints = [] if passed else _compare_hints(worst)
1191
+ if passed:
1192
+ verdict = "pass: " + (
1193
+ f"max {max_seen} ULP within the order band" if banded
1194
+ else f"max {max_seen} ULP")
1195
+ elif bias_fail:
1196
+ verdict = ("fail: uniform bias, %.0f%% of elements above their "
1197
+ "order band in one direction" %
1198
+ (100 * max(t["frac_above_band"] for t in per_trial)))
1199
+ else:
1200
+ w0 = worst["worst_elements"][0] if worst["worst_elements"] else None
1201
+ verdict = "fail: max %d ULP" % max_seen + (
1202
+ " at index %s (ref %.6g, cand %.6g)" %
1203
+ (w0["index_nd"], w0["ref"], w0["cand"]) if w0 else "")
1204
+ rep = {
1205
+ "pass": passed,
1206
+ "verdict": verdict,
1207
  "criterion": criterion,
1208
  "worst_trial": worst,
1209
  "max_abs": max_abs_seen,
 
1211
  "trials": per_trial,
1212
  "ulp_hist_log2": dict(sorted(total_hist.items())),
1213
  }
1214
+ if hints:
1215
+ rep["hints"] = hints
1216
+ return rep
1217
 
1218
 
1219
  _FUZZ_VARIANTS = ("contig", "transposed", "strided_rows", "offset_slice",
 
1229
  return False
1230
 
1231
 
1232
+ class _RandnMaker:
1233
+ """Picklable default maker: `arity` same-shape randn tensors (zeros for empty shapes)."""
1234
+
1235
+ def __init__(self, arity=1):
1236
+ self.arity = arity
1237
+
1238
+ def __call__(self, shape, dtype=torch.float32):
1239
+ n = math.prod(shape)
1240
+ if n:
1241
+ out = tuple(torch.randn(tuple(shape), device="cuda",
1242
+ dtype=dtype)
1243
+ for _ in range(self.arity))
1244
+ else:
1245
+ out = tuple(torch.zeros(tuple(shape), device="cuda",
1246
+ dtype=dtype)
1247
+ for _ in range(self.arity))
1248
+ return out if self.arity > 1 else out[0]
1249
+
1250
+
1251
+ def _default_make(arity=1):
1252
+ """Default maker instance; a class so isolate=True can transport it."""
1253
+ return _RandnMaker(arity)
1254
+
1255
+
1256
  def _fuzz_build(make, shape, dtype):
1257
  try:
1258
  out = make(tuple(shape), dtype)
 
1269
  if v == "strided_rows":
1270
  return x[::2] if x.dim() >= 1 and x.shape[0] >= 2 else x
1271
  if v == "offset_slice":
1272
+ # storage-offset-1 view: misaligned for vectorized loads
1273
  return x.flatten()[1:] if x.numel() > 1 else x
1274
  if v == "overflow_expand":
1275
  if x.numel() == 0:
 
1283
 
1284
  def _fuzz_group(op, ref, make, shape, dtype, variants, ulp_tol, device,
1285
  stop_on_context_loss=True):
1286
+ """All variant cases for one (shape, dtype) group; returns (rows, context_lost)."""
 
1287
  rows = []
1288
  bases = _fuzz_build(make, shape, dtype)
1289
  lost = False
 
1320
  got = op(*xs)
1321
  want = ref(*xs)
1322
  if v == "overflow_expand":
1323
+ # bounded comparison: head plus the high-index tail where int32 indexing wraps
 
1324
  w = 1 << 20
1325
  gf, wf = got.flatten(), want.flatten()
1326
  pairs = ((gf[:w], wf[:w]), (gf[-w:], wf[-w:]))
 
1345
  return rows, lost
1346
 
1347
 
1348
+ def _fn_spec(fn, name):
1349
+ """Picklable transport spec for a callable, with a source fallback for local defs."""
1350
+ import pickle
1351
+ try:
1352
+ return ("pickle", pickle.dumps(fn), None)
1353
+ except Exception: # noqa: BLE001
1354
+ import inspect
1355
+ import textwrap
1356
+ try:
1357
+ src = textwrap.dedent(inspect.getsource(fn))
1358
+ except (OSError, TypeError):
1359
+ src = ""
1360
+ if not src.startswith("def "):
1361
+ raise ValueError(
1362
+ f"{name} is not picklable and not a plain def; define "
1363
+ "op/ref/make as top-level functions (not lambdas or "
1364
+ "closures) to use isolate=True") from None
1365
+ return ("source", src, fn.__name__)
1366
+
1367
+
1368
+ _POOL_BOOTSTRAP = r"""
1369
+ import json, os, pickle, sys
1370
  meta = json.load(open(sys.argv[1], "r"))
1371
+ sys.path[:0] = meta["sys_path"]
1372
  try:
1373
  import kernels
1374
  _mk = kernels.get_kernel(meta["repo"], version=meta["version"],
 
1381
  import load_local
1382
  _mk = load_local.load()
1383
  import torch
1384
+ _ns = {"torch": torch}
1385
+ def _resolve(spec):
1386
+ kind, payload, name = spec
1387
+ if kind == "pickle":
1388
+ return pickle.loads(payload)
1389
+ exec(payload, _ns)
1390
+ return _ns[name]
1391
+ _op, _ref, _make = (_resolve(s) for s in
1392
+ pickle.load(open(meta["fns"], "rb")))
1393
+ print("ready", flush=True)
1394
+ for line in sys.stdin:
1395
+ job = json.loads(line)
1396
+ rows, _l = _mk._fuzz_group(
1397
+ _op, _ref, _make, tuple(job["shape"]),
1398
+ getattr(torch, job["dtype"]), tuple(job["variants"]),
1399
+ job["ulp_tol"], job["device"], stop_on_context_loss=False)
1400
+ pickle.dump(rows, open(job["out"], "wb"))
1401
+ print("done", flush=True)
1402
  """
1403
 
1404
 
1405
+ class _FuzzPool:
1406
+ """One persistent isolation worker fed groups over stdin, respawned only after a crash."""
1407
+
1408
+ def __init__(self, meta_path, stderr_path, timeout_s):
1409
+ self.meta_path = meta_path
1410
+ self.stderr_path = stderr_path
1411
+ self.timeout_s = timeout_s
1412
+ self.proc = None
1413
+ self.q = None
1414
+
1415
+ def _spawn(self):
1416
+ import queue
1417
+ import subprocess
1418
+ import sys as _sys
1419
+ import threading
1420
+ self.errf = open(self.stderr_path, "ab")
1421
+ self.proc = subprocess.Popen(
1422
+ [_sys.executable, "-c", _POOL_BOOTSTRAP, self.meta_path],
1423
+ stdin=subprocess.PIPE, stdout=subprocess.PIPE,
1424
+ stderr=self.errf, text=True)
1425
+ self.q = queue.Queue()
1426
+
1427
+ def reader(p, q):
1428
+ for line in p.stdout:
1429
+ q.put(line.strip())
1430
+ threading.Thread(target=reader, args=(self.proc, self.q),
1431
+ daemon=True).start()
1432
+ try:
1433
+ first = self.q.get(timeout=self.timeout_s)
1434
+ except Exception: # noqa: BLE001
1435
+ first = None
1436
+ return first == "ready"
1437
+
1438
+ def run(self, job_line):
1439
+ """Returns True when the worker reports done, False on crash or timeout."""
1440
+ import queue
1441
+ if self.proc is None or self.proc.poll() is not None:
1442
+ if not self._spawn():
1443
+ return False
1444
+ try:
1445
+ self.proc.stdin.write(job_line + "\n")
1446
+ self.proc.stdin.flush()
1447
+ except OSError:
1448
+ return False
1449
+ try:
1450
+ return self.q.get(timeout=self.timeout_s) == "done"
1451
+ except queue.Empty:
1452
+ if self.proc.poll() is None:
1453
+ self.proc.kill()
1454
+ return False
1455
+
1456
+ def stderr_tail(self):
1457
+ try:
1458
+ self.errf.flush()
1459
+ with open(self.stderr_path, "rb") as f:
1460
+ return f.read()[-400:].decode(errors="replace")
1461
+ except OSError:
1462
+ return ""
1463
+
1464
+ def close(self):
1465
+ if self.proc is not None and self.proc.poll() is None:
1466
+ self.proc.kill()
1467
+ try:
1468
+ self.errf.close()
1469
+ except Exception: # noqa: BLE001
1470
+ pass
1471
+
1472
+
1473
+ def _fuzz_hints(rows):
1474
+ hints = []
1475
+ bad = [c for c in rows if c["status"] == "mismatch"]
1476
+ if bad and all(c["variant"] in ("transposed", "strided_rows",
1477
+ "offset_slice") for c in bad):
1478
+ hints.append("mismatches only on non-contiguous variants: the op "
1479
+ "assumes contiguity; call .contiguous() or honor "
1480
+ "strides")
1481
+ if any(c["variant"] == "overflow_expand" and
1482
+ c["status"] == "mismatch" for c in rows):
1483
+ hints.append("overflow_expand mismatch: int32 indexing wraps past "
1484
+ "2^31 elements; use 64-bit index arithmetic")
1485
+ return hints
1486
+
1487
+
1488
+ def fuzz_shapes(op, ref, make=None, shapes=None, variants=_FUZZ_VARIANTS,
1489
  dtypes=(torch.float32,), ulp_tol=64, isolate=False,
1490
+ isolate_timeout_s=180, device="cuda", arity=1):
1491
+ """Edge shapes, dtypes, and layout mutations with per-case repro recipes; isolate=True survives context-killing candidates via a pooled worker."""
 
 
 
 
 
 
 
 
 
 
 
 
1492
  if shapes is None:
1493
  shapes = [(), (7,), (0, 8), (1, 1), (1, 17), (3, 63), (17, 129),
1494
  (128, 128), (1023, 5), (2, 4096), (2, 3, 5)]
1495
+ if make is None:
1496
+ make = _default_make(arity)
1497
 
1498
  rows = []
1499
  lost = False
1500
  if isolate:
1501
  import json
1502
  import pickle
 
1503
  import sys as _sys
1504
  import tempfile
1505
  tmpdir = tempfile.mkdtemp(prefix="mk_fuzz_")
1506
  fns = os.path.join(tmpdir, "fns.pkl")
1507
+ specs = [_fn_spec(f, n) for f, n in ((op, "op"), (ref, "ref"),
1508
+ (make, "make"))]
1509
  with open(fns, "wb") as f:
1510
+ pickle.dump(specs, f)
1511
+ meta_path = os.path.join(tmpdir, "meta.json")
1512
+ with open(meta_path, "w", encoding="utf-8") as f:
1513
+ json.dump({"sys_path": [p for p in _sys.path
1514
+ if isinstance(p, str)],
1515
+ "repo": _REPO_ID, "version": _REPO_VERSION,
1516
+ "fns": fns}, f)
1517
+ pool = _FuzzPool(meta_path, os.path.join(tmpdir, "stderr.log"),
1518
+ isolate_timeout_s)
1519
+ try:
1520
+ for dtype in dtypes:
1521
+ for shape in shapes:
1522
+ out = os.path.join(tmpdir, "rows.pkl")
1523
+ if os.path.exists(out):
1524
+ os.remove(out)
1525
+ job = json.dumps({
1526
  "shape": list(shape),
1527
  "dtype": str(dtype).replace("torch.", ""),
1528
  "variants": list(variants),
1529
  "ulp_tol": ulp_tol, "device": device,
1530
+ "out": out})
1531
+ ok = pool.run(job)
1532
+ if ok and os.path.exists(out):
 
 
 
 
 
 
1533
  with open(out, "rb") as f:
1534
+ rows.extend(pickle.load(f))
1535
+ else:
1536
+ rows.append({
1537
+ "shape": tuple(shape), "variant": "*",
1538
+ "dtype": str(dtype).replace("torch.", ""),
1539
+ "status": "crashed_isolated",
1540
+ "stderr_tail": pool.stderr_tail()})
1541
+ finally:
1542
+ pool.close()
 
 
 
 
1543
  else:
1544
  pending = [(dt, sh) for dt in dtypes for sh in shapes]
1545
  for gi, (dtype, shape) in enumerate(pending):
 
1556
  n_bad = sum(1 for r in rows
1557
  if r["status"] not in ("ok", "skipped_vram",
1558
  "skipped_empty"))
1559
+ rep = {"cases": rows, "failed": n_bad, "total": len(rows),
1560
+ "context_lost": lost}
1561
+ hints = _fuzz_hints(rows)
1562
+ if hints:
1563
+ rep["hints"] = hints
1564
+ if n_bad == 0:
1565
+ rep["verdict"] = "pass: %d/%d cases ok" % (len(rows) - n_bad,
1566
+ len(rows))
1567
+ else:
1568
+ worst_case = next(c for c in rows
1569
+ if c["status"] not in ("ok", "skipped_vram",
1570
+ "skipped_empty"))
1571
+ rep["verdict"] = "fail: %d/%d cases (%s at %s %s)" % (
1572
+ n_bad, len(rows), worst_case["status"], worst_case["shape"],
1573
+ worst_case["variant"])
1574
+ return rep
1575
 
1576
 
1577
  def sweep(factory, grid, iters=30, warmup=5, bytes=None, flops=None,
1578
  check=None, budget_s=None, race=False):
1579
+ """Launch-configuration search, fastest first, with a correctness gate per row and no silently dropped points."""
 
 
 
 
 
 
 
 
1580
  keys = list(grid.keys())
1581
 
1582
  def expand(i, acc):
 
1641
  continue
1642
  rows.append(run_point(p, iters))
1643
  rows.sort(key=lambda r: (not r.get("check", True), r["median_ms"]))
1644
+ if rows:
1645
+ n_pruned = sum(1 for r in rows if r.get("status") == "pruned_race")
1646
+ n_skip = sum(1 for r in rows if r.get("status") == "not_run")
1647
+ best = rows[0]
1648
+ rows_verdict = "fastest %s at %.4g ms" % (
1649
+ {k: best[k] for k in keys}, best["median_ms"])
1650
+ if n_pruned:
1651
+ rows_verdict += "; %d pruned" % n_pruned
1652
+ if n_skip:
1653
+ rows_verdict += "; %d not run" % n_skip
1654
+ best["verdict"] = rows_verdict
1655
  return rows
1656
 
1657
 
1658
+ def judge(cand, ref, inputs=None, gen=None, bytes=None, flops=None,
1659
+ dtype=None, order_shuffle=None, make=None, quick_fuzz=True,
1660
+ device="cuda"):
1661
+ """One-call verdict: compare, a quick fuzz, and a roofline bench against the cached dossier."""
1662
+ if (gen is None) == (inputs is None):
1663
+ raise ValueError("pass exactly one of inputs (concrete tensors) "
1664
+ "or gen (seed -> args)")
1665
+ if inputs is not None and isinstance(inputs, torch.Tensor):
1666
+ inputs = (inputs,)
1667
+ args0 = tuple(inputs) if inputs is not None else gen(0)
1668
+ arity = len(args0)
1669
+ if dtype is None:
1670
+ dt0 = args0[0].dtype
1671
+ dtype = {torch.float16: "fp16", torch.bfloat16: "bf16"}.get(
1672
+ dt0, "f32")
1673
+ dossier = probe_device(device, quick=True, cached=True,
1674
+ tenancy_check=False)
1675
+ cmp_rep = compare(ref, cand, gen=gen, inputs=inputs, trials=4,
1676
+ order_shuffle=order_shuffle)
1677
+ fuzz_rep = None
1678
+ if quick_fuzz:
1679
+ fuzz_rep = fuzz_shapes(
1680
+ cand, ref, make=make, arity=arity,
1681
+ shapes=[(1, 17), (3, 63), (128, 128)],
1682
+ variants=("contig", "transposed", "offset_slice"),
1683
+ dtypes=(args0[0].dtype,))
1684
+ bench_rep = None
1685
+ if inputs is not None:
1686
+ bench_rep = bench(lambda: cand(*inputs), iters=32, warmup=6,
1687
+ bytes=bytes, flops=flops, dossier=dossier,
1688
+ dtype=dtype, device=device)
1689
+ passed = cmp_rep["pass"] and (fuzz_rep is None or
1690
+ fuzz_rep["failed"] == 0)
1691
+ parts = [cmp_rep["verdict"]]
1692
+ if fuzz_rep is not None:
1693
+ parts.append(fuzz_rep["verdict"])
1694
+ if bench_rep is not None:
1695
+ parts.append(bench_rep["verdict"])
1696
+ rep = {"pass": passed, "verdict" : " | ".join(parts),
1697
+ "compare": cmp_rep, "fuzz": fuzz_rep, "bench": bench_rep,
1698
+ "device": dossier["device"]["name"]}
1699
+ hints = list(cmp_rep.get("hints", []))
1700
+ if fuzz_rep is not None:
1701
+ hints += fuzz_rep.get("hints", [])
1702
+ if hints:
1703
+ rep["hints"] = hints
1704
+ return rep
1705
+
1706
+
1707
  def alloc_stamps(n, blocks=None, device="cuda"):
1708
+ """Device buffer for MK_STAMP (1-D) or MK_STAMP_BLOCK ([blocks, n]) stamps."""
 
 
1709
  if blocks is not None:
1710
  return torch.zeros(blocks, n, dtype=torch.int64, device=device)
1711
  return torch.zeros(n, dtype=torch.int64, device=device)
 
1715
 
1716
 
1717
  def pinned_dossiers():
1718
+ """Names of the release-pinned dossiers, one per certified architecture."""
 
1719
  return sorted(_PINNED.keys())
1720
 
1721
 
 
1734
 
1735
 
1736
  def dossier_drift(current, pinned=None, rel_tol=0.35, skip=_DRIFT_SKIP):
1737
+ """Per-leaf relative drift of a fresh dossier against the release pin for the same architecture."""
 
 
 
 
1738
  if pinned is None:
1739
  arch = current["device"]["sm_arch"]
1740
  matches = [k for k, v in _PINNED.items()
 
1757
  "rel": round(rel, 4)})
1758
  worst = max((e["rel"] for e in entries), default=0.0)
1759
  over = [e for e in entries if e["rel"] > rel_tol]
1760
+ rep = {"entries": entries, "worst_rel": worst, "over_tol": over,
1761
+ "rel_tol": rel_tol, "compared": len(entries),
1762
+ "pass": len(over) == 0}
1763
+ rep["verdict"] = ("pass: worst drift %.3f over %d entries" %
1764
+ (worst, len(entries))) if rep["pass"] else \
1765
+ ("fail: %d entries over %.2f, worst %s at %.3f" %
1766
+ (len(over), rel_tol, over[0]["path"], worst))
1767
+ return rep
1768
 
1769
 
1770
  def stamped_demo(n=1 << 22, fma_iters=4000, blocks=None, threads=256,
1771
  device="cuda"):
1772
+ """Run the shipped MK_STAMP_BLOCK example (load, compute, drained store) and decode its stamps."""
 
 
1773
  props = torch.cuda.get_device_properties(device)
1774
  if blocks is None:
1775
  blocks = props.multi_processor_count
 
1782
 
1783
 
1784
  def read_stamps(stamps, labels=None, clock_ghz=None):
1785
+ """Decode MK_STAMP deltas to ticks and microseconds; 2-D buffers yield min/median/max across blocks."""
 
 
 
 
1786
  if clock_ghz is None:
1787
  clock_ghz = measure_clock(stamps.device)
1788
 
 
1818
  return {"phases": phases, "clock_ghz": round(clock_ghz, 3),
1819
  "blocks_reporting": int(live.sum().item()),
1820
  "blocks_total": int(live.numel())}
1821
+
1822
+
1823
+ _SCHEMAS = {
1824
+ "probe_device": {
1825
+ "device": "name, sm_arch, sms, l2_bytes, vram_bytes, torch, "
1826
+ "cuda_runtime, ecc, driver, power_limit_w",
1827
+ "clock": "max/min/final GHz under sustained FMA load plus sag_pct",
1828
+ "dram_bw": "GB/s: triad_4B/8B/16B, read_only, write_only, "
1829
+ "random_gather_16B",
1830
+ "l2_bw": "GB/s over an L2-resident working set",
1831
+ "latency": "cycles: smem, l1, l2, dram (min of 3 chases) plus "
1832
+ "ns_per_cycle",
1833
+ "fma_f32 / fma_f64": "TFLOP/s of the register-resident pipes",
1834
+ "fma_ilp": "TFLOP/s at 1/2/4 chains plus ratio_4_1; near 4 means "
1835
+ "latency-bound",
1836
+ "mma": "TFLOP/s per dtype; sparse carries executed and "
1837
+ "dense_equivalent; verified_k_lanes carries "
1838
+ "executed_k_per_iter, vs_nominal, calibration_linear; "
1839
+ "fp8_accum carries the accumulate-precision stall",
1840
+ "mma_fed": "TFLOP/s of smem-staged fp16 tiles (staging exposed)",
1841
+ "smem_bw": "GB/s aggregate, conflict-light pattern",
1842
+ "smem_conflict_penalty": "time ratio vs stride 1 per lane stride",
1843
+ "transfer_bw": "GB/s: h2d/d2h pinned and pageable, d2d",
1844
+ "atomics_f32": "Gop/s curve over distinct addresses plus "
1845
+ "shared_same_addr",
1846
+ "launch_overhead": "us: torch_queued, torch_round_trip, "
1847
+ "raw_queued, raw_round_trip",
1848
+ "grid_barrier": "us per grid.sync or 'unsupported'",
1849
+ "occupancy_blocks_per_sm": "API table by (threads, dyn smem)",
1850
+ "occupancy_knees_fma_tflops": "timed dependent-chain table",
1851
+ },
1852
+ "bench": {
1853
+ "median_ms": "median over accepted samples",
1854
+ "sustained_ms": "median over all samples",
1855
+ "median_ci95_ms / p05_ms / p95_ms / min_ms / mean_ms":
1856
+ "distribution of accepted samples",
1857
+ "samples / rejected_throttled / rejection_basis":
1858
+ "counts and whether rejection used sm_clock or "
1859
+ "bandwidth_consistency",
1860
+ "no_clean_window": "present when every window ran throttled; the "
1861
+ "median is then a throttled-regime number",
1862
+ "clock_ghz": "best and worst SM clock probes around the run",
1863
+ "achieved_gbs / achieved_tflops / arithmetic_intensity":
1864
+ "rates from declared traffic",
1865
+ "roofline": "bound, ridge_flops_per_byte, pct_of_roof, peak_dtype",
1866
+ "graph_captured / graph_fallback_reason": "CUDA-graph state",
1867
+ "alloc_growth_bytes_per_call": "present when fn leaks allocations",
1868
+ "host_overhead_suspected": "present when fn syncs or does host "
1869
+ "work inside; ops under ~50 us are "
1870
+ "below the detection floor",
1871
+ "verdict": "one-sentence summary",
1872
+ },
1873
+ "compare": {
1874
+ "pass": "final verdict incl. the one-sided bias gate",
1875
+ "verdict / criterion / hints": "summary, acceptance rule, and "
1876
+ "failure-pattern suggestions",
1877
+ "worst_trial": "max_ulp, mean_ulp, first_divergent_index(+_nd), "
1878
+ "NaN/Inf census, order_band_ulp, band_score, "
1879
+ "frac_above_band, bias_one_sided_frac, "
1880
+ "worst_elements (index, index_nd, ref, cand, ulp)",
1881
+ "max_abs / max_rel / ulp_hist_log2": "aggregate evidence",
1882
+ "floor": "the banded route cannot reject errors under "
1883
+ "band_margin*(band+2) ULP at order-stable elements",
1884
+ },
1885
+ "fuzz_shapes": {
1886
+ "cases": "per-case shape, variant, dtype, status, repro recipe",
1887
+ "status values": "ok | mismatch | exception | context_killed | "
1888
+ "context_lost | crashed_isolated | skipped_vram "
1889
+ "| skipped_empty",
1890
+ "failed / total / context_lost / verdict / hints": "aggregates",
1891
+ },
1892
+ "sweep": {
1893
+ "rows": "grid point plus median_ms, rejected_throttled, check?, "
1894
+ "achieved rates, status in pruned_race | not_run",
1895
+ "verdict": "on the fastest row",
1896
+ },
1897
+ "dossier_drift": {
1898
+ "entries": "path, current, pinned, rel per intersecting leaf",
1899
+ "worst_rel / over_tol / compared / pass / verdict": "aggregates",
1900
+ },
1901
+ "judge": {
1902
+ "pass / verdict / hints": "composite over compare, fuzz, bench",
1903
+ "compare / fuzz / bench": "the full sub-reports",
1904
+ },
1905
+ "read_stamps": {
1906
+ "phases": "ticks and us per phase; 2-D input adds min/median/max "
1907
+ "across blocks",
1908
+ "note": "stamps record issue; DRAIN variants include the store "
1909
+ "drain to the coherence point",
1910
+ },
1911
+ }
1912
+
1913
+
1914
+ def schema(name=None):
1915
+ """Field layout of every report, so an agent can look up what a number means."""
1916
+ if name is None:
1917
+ return _SCHEMAS
1918
+ if name not in _SCHEMAS:
1919
+ raise KeyError(f"no schema for {name!r}; have "
1920
+ f"{sorted(_SCHEMAS)}")
1921
+ return _SCHEMAS[name]
build/torch213-cxx11-cu126-x86_64-linux/{_metakernel_cuda_b62a1ce.abi3.so → _metakernel_cuda_0b9de42.abi3.so} RENAMED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:2abef05bfc6a31613189774aeb6133563c7c563475a19128863a55db715d93a0
3
- size 743672
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e88c26a134c137ab36fc4580d338ee0a75ac9306710d4681f34b6ac43b6e60c6
3
+ size 900400
build/torch213-cxx11-cu126-x86_64-linux/_ops.py CHANGED
@@ -1,9 +1,9 @@
1
  import torch
2
- from . import _metakernel_cuda_b62a1ce
3
- ops = torch.ops._metakernel_cuda_b62a1ce
4
 
5
  def add_op_namespace_prefix(op_name: str):
6
  """
7
  Prefix op by namespace.
8
  """
9
- return f"_metakernel_cuda_b62a1ce::{op_name}"
 
1
  import torch
2
+ from . import _metakernel_cuda_0b9de42
3
+ ops = torch.ops._metakernel_cuda_0b9de42
4
 
5
  def add_op_namespace_prefix(op_name: str):
6
  """
7
  Prefix op by namespace.
8
  """
9
+ return f"_metakernel_cuda_0b9de42::{op_name}"
build/torch213-cxx11-cu126-x86_64-linux/_pinned.py CHANGED
@@ -1,8 +1,4 @@
1
- """Release-pinned device dossiers, one per certified architecture.
2
-
3
- Generated by dev/pin_dossier.py from full probe_device() runs on idle,
4
- single-tenant cards; consumed by metakernel.dossier_drift.
5
- """
6
 
7
  PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
8
  'confidence': 'high',
@@ -295,7 +291,7 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
295
  'd2h_pinned': 10.8,
296
  'h2d_pageable': 10.3,
297
  'h2d_pinned': 11.8}}},
298
- 'sm89_nvidiartx6000adagenerati': {'atomics_f32': {'clock_ghz': 2.706,
299
  'confidence': 'high',
300
  'method': 'atomicAdd '
301
  'hammer; curve '
@@ -308,21 +304,21 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
308
  'contention) to '
309
  'one per thread',
310
  'units': 'Gop/s',
311
- 'value': {'curve_slots': {'1': 0.72,
312
- '145408': 483.54,
313
- '4096': 209.84,
314
- '512': 55.6,
315
- '64': 13.82,
316
- '8': 5.77},
317
- 'shared_same_addr': 12.88}},
318
  'clock': {'confidence': 'high',
319
- 'final_ghz': 2.699,
320
- 'max_ghz': 2.706,
321
  'method': '24 back-to-back FMA '
322
  'spin windows of 30M '
323
  'ticks; wall/ticks '
324
  'per window',
325
- 'min_ghz': 2.501,
326
  'sag_pct': 0.3,
327
  'units': 'GHz'},
328
  'device': {'cuda_runtime': '12.6',
@@ -336,7 +332,7 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
336
  'sms': 142,
337
  'torch': '2.10.0+cu126',
338
  'vram_bytes': 48305274880},
339
- 'dram_bw': {'clock_ghz': 2.706,
340
  'confidence': 'high',
341
  'method': 'streaming triad '
342
  'c=a+s*b per access '
@@ -356,26 +352,41 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
356
  'line-efficiency '
357
  'claim',
358
  'units': 'GB/s',
359
- 'value': {'random_gather_16B': 218.5,
360
- 'read_only': 771.0,
361
- 'triad_16B': 663.1,
362
- 'triad_4B': 638.3,
363
- 'triad_8B': 705.3,
364
  'write_only': 766.5}},
365
- 'fma_f32': {'clock_ghz': 2.706,
366
  'confidence': 'high',
367
  'method': 'register-resident '
368
  'FMA chains',
369
  'units': 'TFLOP/s',
370
- 'value': 90.85},
371
- 'fma_f64': {'clock_ghz': 2.706,
372
  'confidence': 'high',
373
  'method': 'register-resident '
374
  'double FMA chains '
375
  '(the DFMA pipe)',
376
  'units': 'TFLOP/s',
377
- 'value': 1.282},
378
- 'grid_barrier': {'clock_ghz': 2.706,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
379
  'confidence': 'high',
380
  'method': 'cooperative '
381
  'kernel, 142 '
@@ -383,8 +394,8 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
383
  'threads',
384
  'units': 'us per '
385
  'grid.sync',
386
- 'value': 0.54},
387
- 'l2_bw': {'clock_ghz': 2.706,
388
  'confidence': 'medium',
389
  'method': 'in-kernel repeated '
390
  'triad passes over 72 '
@@ -394,14 +405,15 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
394
  'from HBM; amortized '
395
  'over 64 passes',
396
  'units': 'GB/s',
397
- 'value': 9325.3},
398
- 'latency': {'clock_ghz': 2.687,
399
  'confidence': 'high',
400
  'method': 'Sattolo-cycle '
401
- 'pointer chase; 16 '
402
- 'KB ring for L1, 2 '
403
- 'MB TLB-resident '
404
- 'ring for L2, '
 
405
  'self-evicting '
406
  'chain (2x L2 in '
407
  'touched lines) for '
@@ -413,40 +425,58 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
413
  'footprints',
414
  'units': 'cycles (single-lane '
415
  'dependent loads)',
416
- 'value': {'dram_cycles': 649.4,
417
  'l1_cycles': 45.8,
418
- 'l2_cycles': 291.5,
419
  'ns_per_cycle': 0.372,
420
  'smem_cycles': 30.0}},
421
- 'launch_overhead': {'clock_ghz': 2.706,
422
- 'confidence': 'high',
423
  'method': 'empty-kernel '
424
- 'trains, '
425
- 'queued vs '
426
- 'launch+sync',
427
- 'note': 'host-dependent: '
428
- 'includes '
429
- 'torch op '
430
- 'dispatch',
 
 
 
 
 
 
 
 
 
 
 
 
431
  'units': 'us',
432
- 'value': {'queued_us': 5.55,
433
- 'round_trip_us': 14.1}},
434
- 'mma': {'clock_ghz': 2.706,
 
 
435
  'confidence': 'medium',
 
 
 
 
436
  'method': 'register-fragment '
437
  'mma_sync loops (wmma; '
438
  'fp8 and 2:4-sparse '
439
  'fp16 via inline PTX '
440
- 'm16n8k32; sparse '
441
- 'counted '
442
- 'dense-equivalent, the '
443
- 'vendor convention); '
444
- 'flops derive from the '
445
- 'mean accumulator '
446
- 'read-back with every '
447
- 'fragment element '
448
- 'observed, never the '
449
- 'declared loop shape',
450
  'note': 'dense-tile rate without '
451
  'memory traffic; real '
452
  'kernels see this only '
@@ -454,25 +484,54 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
454
  'behind it',
455
  'units': 'TFLOP/s (TOP/s for '
456
  'int8)',
457
- 'value': {'bf16': 365.5,
458
- 'fp16': 375.9,
459
- 'fp16_sparse_2_4': 713.4,
460
- 'fp8_e4m3': 193.7,
461
- 'int8': 770.4,
462
- 'tf32': 177.1},
463
- 'verified_k_lanes': {'bf16': {'executed_k_per_iter': 64.0,
 
 
 
464
  'vs_nominal': 1.0},
465
- 'fp16': {'executed_k_per_iter': 64.0,
 
466
  'vs_nominal': 1.0},
467
- 'fp16_sparse_2_4': {'executed_k_per_iter': 128.0,
 
468
  'vs_nominal': 1.0},
469
- 'fp8_e4m3': {'executed_k_per_iter': 64.0,
470
- 'vs_nominal': 0.25},
471
- 'int8': {'executed_k_per_iter': 64.0,
 
 
 
 
 
472
  'vs_nominal': 1.0},
473
- 'tf32': {'executed_k_per_iter': 32.0,
 
474
  'vs_nominal': 1.0}}},
475
- 'occupancy_blocks_per_sm': {'clock_ghz': 2.706,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
476
  'confidence': 'high',
477
  'method': 'occupancy '
478
  'API '
@@ -501,7 +560,7 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
501
  '16KB': 3,
502
  '32KB': 3,
503
  '48KB': 2}}},
504
- 'occupancy_knees_fma_tflops': {'clock_ghz': 2.706,
505
  'confidence': 'high',
506
  'method': 'a '
507
  'single '
@@ -521,17 +580,6 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
521
  'fixed '
522
  'total '
523
  'work; '
524
- 'throughput '
525
- 'is '
526
- 'proportional '
527
- 'to '
528
- 'resident '
529
- 'threads '
530
- 'until '
531
- 'the '
532
- 'pipe '
533
- 'saturates, '
534
- 'so '
535
  'the '
536
  'cliff '
537
  'marks '
@@ -549,15 +597,15 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
549
  'above',
550
  'units': 'TFLOP/s',
551
  'value': {'t1024': {'0KB': 75.5,
552
- '32KB': 73.66,
553
- '48KB': 74.95},
554
- 't128': {'0KB': 79.86,
555
  '32KB': 60.31,
556
- '48KB': 44.57},
557
- 't256': {'0KB': 81.69,
558
- '32KB': 79.48,
559
- '48KB': 71.47}}},
560
- 'smem_bw': {'clock_ghz': 2.706,
561
  'confidence': 'medium',
562
  'method': 'block-resident '
563
  'read+write '
@@ -566,12 +614,56 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
566
  'aggregated across '
567
  'SMs',
568
  'note': 'bank-conflict-light '
569
- 'access pattern; a '
570
- 'conflicted pattern '
571
- 'can be far lower',
 
572
  'units': 'GB/s',
573
- 'value': 21250.0},
574
- 'transfer_bw': {'clock_ghz': 2.706,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
575
  'confidence': 'high',
576
  'method': '256 MB torch '
577
  'copies, '
@@ -583,8 +675,8 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
583
  'direction of '
584
  'traffic',
585
  'units': 'GB/s',
586
- 'value': {'d2d': 330.7,
587
- 'd2h_pageable': 15.6,
588
- 'd2h_pinned': 26.2,
589
- 'h2d_pageable': 16.6,
590
  'h2d_pinned': 24.0}}}}
 
1
+ '''Release-pinned device dossiers, one per certified architecture; generated by dev/pin_dossier.py from idle single-tenant runs, consumed by dossier_drift.'''
 
 
 
 
2
 
3
  PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
4
  'confidence': 'high',
 
291
  'd2h_pinned': 10.8,
292
  'h2d_pageable': 10.3,
293
  'h2d_pinned': 11.8}}},
294
+ 'sm89_nvidiartx6000adagenerati': {'atomics_f32': {'clock_ghz': 2.705,
295
  'confidence': 'high',
296
  'method': 'atomicAdd '
297
  'hammer; curve '
 
304
  'contention) to '
305
  'one per thread',
306
  'units': 'Gop/s',
307
+ 'value': {'curve_slots': {'1': 0.71,
308
+ '145408': 413.99,
309
+ '4096': 184.9,
310
+ '512': 49.84,
311
+ '64': 13.25,
312
+ '8': 5.74},
313
+ 'shared_same_addr': 12.23}},
314
  'clock': {'confidence': 'high',
315
+ 'final_ghz': 2.698,
316
+ 'max_ghz': 2.705,
317
  'method': '24 back-to-back FMA '
318
  'spin windows of 30M '
319
  'ticks; wall/ticks '
320
  'per window',
321
+ 'min_ghz': 2.5,
322
  'sag_pct': 0.3,
323
  'units': 'GHz'},
324
  'device': {'cuda_runtime': '12.6',
 
332
  'sms': 142,
333
  'torch': '2.10.0+cu126',
334
  'vram_bytes': 48305274880},
335
+ 'dram_bw': {'clock_ghz': 2.705,
336
  'confidence': 'high',
337
  'method': 'streaming triad '
338
  'c=a+s*b per access '
 
352
  'line-efficiency '
353
  'claim',
354
  'units': 'GB/s',
355
+ 'value': {'random_gather_16B': 218.1,
356
+ 'read_only': 766.5,
357
+ 'triad_16B': 682.1,
358
+ 'triad_4B': 654.8,
359
+ 'triad_8B': 670.9,
360
  'write_only': 766.5}},
361
+ 'fma_f32': {'clock_ghz': 2.705,
362
  'confidence': 'high',
363
  'method': 'register-resident '
364
  'FMA chains',
365
  'units': 'TFLOP/s',
366
+ 'value': 86.58},
367
+ 'fma_f64': {'clock_ghz': 2.705,
368
  'confidence': 'high',
369
  'method': 'register-resident '
370
  'double FMA chains '
371
  '(the DFMA pipe)',
372
  'units': 'TFLOP/s',
373
+ 'value': 1.257},
374
+ 'fma_ilp': {'clock_ghz': 2.705,
375
+ 'confidence': 'high',
376
+ 'method': '1/2/4 independent '
377
+ 'dependent FMA '
378
+ 'chains per thread '
379
+ 'at one block of 64 '
380
+ 'threads per SM; a '
381
+ 'ratio near 4 means '
382
+ 'latency-bound at '
383
+ 'that occupancy',
384
+ 'units': 'TFLOP/s',
385
+ 'value': {'ilp1': 11.75,
386
+ 'ilp2': 23.37,
387
+ 'ilp4': 43.03,
388
+ 'ratio_4_1': 3.66}},
389
+ 'grid_barrier': {'clock_ghz': 2.705,
390
  'confidence': 'high',
391
  'method': 'cooperative '
392
  'kernel, 142 '
 
394
  'threads',
395
  'units': 'us per '
396
  'grid.sync',
397
+ 'value': 0.55},
398
+ 'l2_bw': {'clock_ghz': 2.705,
399
  'confidence': 'medium',
400
  'method': 'in-kernel repeated '
401
  'triad passes over 72 '
 
405
  'from HBM; amortized '
406
  'over 64 passes',
407
  'units': 'GB/s',
408
+ 'value': 9590.6},
409
+ 'latency': {'clock_ghz': 2.691,
410
  'confidence': 'high',
411
  'method': 'Sattolo-cycle '
412
+ 'pointer chase, min '
413
+ 'of 3 runs; 16 KB '
414
+ 'ring for L1, 2 MB '
415
+ 'TLB-resident ring '
416
+ 'for L2, '
417
  'self-evicting '
418
  'chain (2x L2 in '
419
  'touched lines) for '
 
425
  'footprints',
426
  'units': 'cycles (single-lane '
427
  'dependent loads)',
428
+ 'value': {'dram_cycles': 656.9,
429
  'l1_cycles': 45.8,
430
+ 'l2_cycles': 292.0,
431
  'ns_per_cycle': 0.372,
432
  'smem_cycles': 30.0}},
433
+ 'launch_overhead': {'clock_ghz': 2.705,
434
+ 'confidence': 'medium',
435
  'method': 'empty-kernel '
436
+ 'trains; '
437
+ 'torch path '
438
+ 'per-launch '
439
+ 'through op '
440
+ 'dispatch, '
441
+ 'raw path '
442
+ 'looped '
443
+ 'inside one '
444
+ 'binding '
445
+ 'call',
446
+ 'note': 'host-state-dependent; '
447
+ 'the '
448
+ 'torch-minus-raw '
449
+ 'gap is the '
450
+ 'dispatch '
451
+ 'cost a '
452
+ 'torch-launched '
453
+ 'candidate '
454
+ 'pays',
455
  'units': 'us',
456
+ 'value': {'raw_queued_us': 6.75,
457
+ 'raw_round_trip_us': 13.87,
458
+ 'torch_queued_us': 7.09,
459
+ 'torch_round_trip_us': 18.53}},
460
+ 'mma': {'clock_ghz': 2.705,
461
  'confidence': 'medium',
462
+ 'fp8_accum': {'fp8_e4m3': {'effective_accum_mantissa_bits': 8,
463
+ 'stall_element_value': 16384.0},
464
+ 'fp8_e5m2': {'effective_accum_mantissa_bits': 8,
465
+ 'stall_element_value': 16384.0}},
466
  'method': 'register-fragment '
467
  'mma_sync loops (wmma; '
468
  'fp8 and 2:4-sparse '
469
  'fp16 via inline PTX '
470
+ 'm16n8k32); flops '
471
+ 'derive from a '
472
+ 'sub-saturation '
473
+ 'accumulator read-back '
474
+ 'with every fragment '
475
+ 'element observed, '
476
+ 'never the declared '
477
+ 'loop shape; sparse '
478
+ 'reports executed and '
479
+ 'dense-equivalent flops',
480
  'note': 'dense-tile rate without '
481
  'memory traffic; real '
482
  'kernels see this only '
 
484
  'behind it',
485
  'units': 'TFLOP/s (TOP/s for '
486
  'int8)',
487
+ 'value': {'bf16': 369.6,
488
+ 'fp16': 365.3,
489
+ 'fp16_sparse_2_4': {'dense_equivalent': 711.1,
490
+ 'executed': 355.6},
491
+ 'fp8_e4m3': 772.3,
492
+ 'fp8_e5m2': 771.2,
493
+ 'int8': 750.0,
494
+ 'tf32': 178.3},
495
+ 'verified_k_lanes': {'bf16': {'calibration_linear': True,
496
+ 'executed_k_per_iter': 64.0,
497
  'vs_nominal': 1.0},
498
+ 'fp16': {'calibration_linear': True,
499
+ 'executed_k_per_iter': 64.0,
500
  'vs_nominal': 1.0},
501
+ 'fp16_sparse_2_4': {'calibration_linear': True,
502
+ 'executed_k_per_iter': 128.0,
503
  'vs_nominal': 1.0},
504
+ 'fp8_e4m3': {'calibration_linear': True,
505
+ 'executed_k_per_iter': 256.0,
506
+ 'vs_nominal': 1.0},
507
+ 'fp8_e5m2': {'calibration_linear': True,
508
+ 'executed_k_per_iter': 256.0,
509
+ 'vs_nominal': 1.0},
510
+ 'int8': {'calibration_linear': True,
511
+ 'executed_k_per_iter': 64.0,
512
  'vs_nominal': 1.0},
513
+ 'tf32': {'calibration_linear': True,
514
+ 'executed_k_per_iter': 32.0,
515
  'vs_nominal': 1.0}}},
516
+ 'mma_fed': {'clock_ghz': 2.705,
517
+ 'confidence': 'high',
518
+ 'method': 'm16n16k16 fp16 '
519
+ 'tiles fed from an '
520
+ 'L2-resident buffer '
521
+ 'through cp.async '
522
+ 'double-buffered '
523
+ 'shared memory; '
524
+ 'accumulator-verified',
525
+ 'note': 'the achieved '
526
+ 'counterpart to the '
527
+ 'register peak: '
528
+ 'staging exposed, one '
529
+ 'dependent '
530
+ 'accumulator chain '
531
+ 'per warp',
532
+ 'units': 'TFLOP/s',
533
+ 'value': 85.2},
534
+ 'occupancy_blocks_per_sm': {'clock_ghz': 2.705,
535
  'confidence': 'high',
536
  'method': 'occupancy '
537
  'API '
 
560
  '16KB': 3,
561
  '32KB': 3,
562
  '48KB': 2}}},
563
+ 'occupancy_knees_fma_tflops': {'clock_ghz': 2.705,
564
  'confidence': 'high',
565
  'method': 'a '
566
  'single '
 
580
  'fixed '
581
  'total '
582
  'work; '
 
 
 
 
 
 
 
 
 
 
 
583
  'the '
584
  'cliff '
585
  'marks '
 
597
  'above',
598
  'units': 'TFLOP/s',
599
  'value': {'t1024': {'0KB': 75.5,
600
+ '32KB': 73.71,
601
+ '48KB': 72.69},
602
+ 't128': {'0KB': 79.72,
603
  '32KB': 60.31,
604
+ '48KB': 45.81},
605
+ 't256': {'0KB': 78.79,
606
+ '32KB': 78.85,
607
+ '48KB': 72.97}}},
608
+ 'smem_bw': {'clock_ghz': 2.705,
609
  'confidence': 'medium',
610
  'method': 'block-resident '
611
  'read+write '
 
614
  'aggregated across '
615
  'SMs',
616
  'note': 'bank-conflict-light '
617
+ 'access pattern; the '
618
+ 'stride sweep below '
619
+ 'prices the '
620
+ 'conflicted case',
621
  'units': 'GB/s',
622
+ 'value': 22244.0},
623
+ 'smem_conflict_penalty': {'clock_ghz': 2.705,
624
+ 'confidence': 'high',
625
+ 'method': 'dependent '
626
+ 'read-FMA-write '
627
+ 'per '
628
+ 'thread '
629
+ 'at '
630
+ 'warp-lane '
631
+ 'bank '
632
+ 'distance '
633
+ '`stride`; '
634
+ 'stride '
635
+ '32 '
636
+ 'puts '
637
+ 'every '
638
+ 'lane '
639
+ 'in '
640
+ 'one '
641
+ 'bank',
642
+ 'note': 'the '
643
+ 'direct '
644
+ 'conflict '
645
+ 'measurement '
646
+ 'that '
647
+ 'disambiguates '
648
+ 'a '
649
+ 'bank-conflict '
650
+ 'signature '
651
+ 'from '
652
+ 'an ILP '
653
+ 'stall',
654
+ 'units': 'x '
655
+ '(time '
656
+ 'ratio '
657
+ 'vs '
658
+ 'stride '
659
+ '1)',
660
+ 'value': {'stride_1': 1.0,
661
+ 'stride_16': 7.13,
662
+ 'stride_2': 1.07,
663
+ 'stride_32': 14.08,
664
+ 'stride_4': 2.04,
665
+ 'stride_8': 3.91}},
666
+ 'transfer_bw': {'clock_ghz': 2.705,
667
  'confidence': 'high',
668
  'method': '256 MB torch '
669
  'copies, '
 
675
  'direction of '
676
  'traffic',
677
  'units': 'GB/s',
678
+ 'value': {'d2d': 324.0,
679
+ 'd2h_pageable': 7.8,
680
+ 'd2h_pinned': 26.3,
681
+ 'h2d_pageable': 7.6,
682
  'h2d_pinned': 24.0}}}}
build/torch213-cxx11-cu126-x86_64-linux/metadata.json CHANGED
@@ -1,9 +1,10 @@
1
  {
2
  "name": "metakernel",
3
- "id": "_metakernel_cuda_b62a1ce",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "python-depends": [],
 
7
  "backend": {
8
  "type": "cuda",
9
  "archs": [
@@ -16,21 +17,20 @@
16
  "digest": {
17
  "algorithm": "sha256",
18
  "files": {
19
- "__init__.py": "e/P3LIoxZqzuhgyc26YJRkdLj4OilextyRiU8YbAvIA=",
20
- "_metakernel_cuda_b62a1ce.abi3.so": "Kr7wW/xqMWExiXdK62EzVjx8VjR1oZEohjpV23Fdk6A=",
21
- "_ops.py": "QokZXDYGh+rLEKO/wNDRcMkJPtPWK568RiTlCHopsHM=",
22
- "_pinned.py": "4i1FvV3kWanEdFLd9+rGOjHcHDAyOjKnjLt5LUzgASs=",
23
- "metakernel/__init__.py": "DFYPlrhXwYjEqCl/8n0SmWGZV8NFml5DPhMjKfv98GY="
24
  }
25
  },
26
  "provenance": {
27
  "kernel-builder": {
28
  "version": "0.17.0-dev0",
29
- "sha": "19aaa6421e674e9fecc352bbae6eab81d19a6bf4",
30
  "dirty": false
31
  },
32
  "kernel": {
33
- "sha": "b62a1cef64b6e3b2cabf774c82ea7ce61dcac6c0",
34
  "dirty": false
35
  }
36
  }
 
1
  {
2
  "name": "metakernel",
3
+ "id": "_metakernel_cuda_0b9de42",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "python-depends": [],
7
+ "kernel-depends": [],
8
  "backend": {
9
  "type": "cuda",
10
  "archs": [
 
17
  "digest": {
18
  "algorithm": "sha256",
19
  "files": {
20
+ "__init__.py": "lDQ+fJ0JnI3sQGR3UMnDovBbG4ciYBsaFjmgYdKDTKI=",
21
+ "_metakernel_cuda_0b9de42.abi3.so": "6IwmoTTBN6s2/EWA0zjuCnWskwZxDUaB80tqxDtuYMY=",
22
+ "_ops.py": "x88Bkm1nxRli0nIu2u4ztaJopS1IAZWdN9LZPfspJdo=",
23
+ "_pinned.py": "UyzMniAjfVMIrIbbrSq92KKo5dX5l/GwV3pMVA+eNtM="
 
24
  }
25
  },
26
  "provenance": {
27
  "kernel-builder": {
28
  "version": "0.17.0-dev0",
29
+ "sha": "2c40e10d3da1392245ee81bd05b55fb3103b2b34",
30
  "dirty": false
31
  },
32
  "kernel": {
33
+ "sha": "0b9de42fee8a1e52593f2f1f58460c0acc93d7e8",
34
  "dirty": false
35
  }
36
  }
build/torch213-cxx11-cu126-x86_64-linux/metakernel/__init__.py DELETED
@@ -1,26 +0,0 @@
1
- import ctypes
2
- import importlib.util
3
- import sys
4
- from pathlib import Path
5
- from types import ModuleType
6
-
7
-
8
- def _import_from_path(file_path: Path) -> ModuleType:
9
- # We cannot use the module name as-is, after adding it to `sys.modules`,
10
- # it would also be used for other imports. So, we make a module name that
11
- # depends on the path for it to be unique using the hex-encoded hash of
12
- # the path.
13
- path_hash = "{:x}".format(ctypes.c_size_t(hash(file_path.absolute())).value)
14
- module_name = path_hash
15
- spec = importlib.util.spec_from_file_location(module_name, file_path)
16
- if spec is None:
17
- raise ImportError(f"Cannot load spec for {module_name} from {file_path}")
18
- module = importlib.util.module_from_spec(spec)
19
- if module is None:
20
- raise ImportError(f"Cannot load module {module_name} from spec")
21
- sys.modules[module_name] = module
22
- spec.loader.exec_module(module) # type: ignore
23
- return module
24
-
25
-
26
- globals().update(vars(_import_from_path(Path(__file__).parent.parent / "__init__.py")))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
build/torch213-cxx11-cu130-x86_64-linux/__init__.py CHANGED
@@ -1,20 +1,4 @@
1
- """Measuring instrument for the kernel-design loop: timing, adversarial
2
- correctness comparison, and measured device facts, as loadable ops.
3
-
4
- from kernels import get_kernel
5
- mk = get_kernel("phanerozoic/metakernel", version=1,
6
- trust_remote_code=True)
7
-
8
- dossier = mk.probe_device() # measured facts
9
- report = mk.bench(lambda: op(x), iters=200) # throttle-rejected
10
- verdict = mk.compare(ref, cand, gen) # ULP evidence
11
- table = mk.sweep(factory, {"BLOCK": [64, 128]}) # config search
12
-
13
- Every op returns plain dicts and tensors. Every dossier entry is
14
- measured by a dedicated probe kernel, carries its SM clock, and labels
15
- throttled samples; no hardware counters, so entries carry a confidence
16
- field.
17
- """
18
  import math
19
  import os
20
  import time
@@ -24,9 +8,7 @@ import torch
24
  try:
25
  from ._ops import ops
26
  except ImportError: # pragma: no cover - spawned child in a source tree
27
- # Subprocess-isolation children re-import this package; in a source
28
- # checkout there is no generated _ops module, so rebuild the op table
29
- # from the (cached) JIT extension that load_local advertised.
30
  _root = os.environ.get("METAKERNEL_DEV_ROOT")
31
  if not _root:
32
  raise
@@ -48,24 +30,25 @@ try:
48
  except ImportError: # pragma: no cover - fresh tree before first pin
49
  _PINNED = {}
50
 
51
- __all__ = ["probe_device", "bench", "compare", "order_shuffles",
52
- "fuzz_shapes", "sweep", "alloc_stamps", "read_stamps",
53
- "stamped_demo", "measure_clock", "ulp_diff", "dossier_drift",
54
- "pinned_dossiers", "ops"]
55
-
56
- # name -> (kind, M*N of the C tile, nominal K, work multiplier, nominal
57
- # K-lanes per unrolled iteration). The accumulator read-back yields
58
- # executed K-lanes per op (column sums of B equal K), so flops =
59
- # 2 x M*N x gain x multiplier with nothing declared trusted; multiplier
60
- # 2.0 is the sparse dense-equivalent convention. Lanes/iter: wmma K x 4
61
- # chained ops; PTX kinds x 8 over two chains.
62
  _MMA_KINDS = {
63
- "fp16": (0, 16 * 16, 16, 1.0, 64.0),
64
- "bf16": (1, 16 * 16, 16, 1.0, 64.0),
65
- "tf32": (2, 16 * 16, 8, 1.0, 32.0),
66
- "int8": (3, 16 * 16, 16, 1.0, 64.0),
67
- "fp8_e4m3": (4, 16 * 8, 32, 1.0, 256.0),
68
- "fp16_sparse_2_4": (5, 16 * 8, 32, 2.0, 128.0), # dense-equivalent
 
69
  }
70
 
71
 
@@ -74,7 +57,7 @@ def _sync():
74
 
75
 
76
  def _events(fn, reps):
77
- """Time reps calls of fn with one event pair each; returns ms list."""
78
  starts = [torch.cuda.Event(enable_timing=True) for _ in range(reps)]
79
  stops = [torch.cuda.Event(enable_timing=True) for _ in range(reps)]
80
  for s, e in zip(starts, stops):
@@ -86,8 +69,7 @@ def _events(fn, reps):
86
 
87
 
88
  def measure_clock(device="cuda", ticks=4_000_000, blocks=None):
89
- """Effective SM clock in GHz under an FMA spin of `ticks` clock64 ticks.
90
- Wall time over elapsed ticks is the frequency the ticks ran at."""
91
  props = torch.cuda.get_device_properties(device)
92
  if blocks is None:
93
  blocks = props.multi_processor_count
@@ -120,8 +102,7 @@ def _entry(value, units, method, clock_ghz, confidence, note=""):
120
 
121
 
122
  def _sattolo_ring(n, device, generator=None):
123
- """A random single-cycle permutation as an int32 next-pointer table, so
124
- a pointer chase visits every slot before repeating."""
125
  perm = torch.randperm(n, generator=generator)
126
  ring = torch.empty(n, dtype=torch.int32)
127
  ring[perm] = torch.roll(perm, -1).to(torch.int32)
@@ -129,8 +110,7 @@ def _sattolo_ring(n, device, generator=None):
129
 
130
 
131
  def _smi_settings(index):
132
- """ECC mode, driver, and power limit via nvidia-smi; None fields
133
- when the tool is unavailable."""
134
  import subprocess
135
  try:
136
  out = subprocess.run(
@@ -150,10 +130,19 @@ def _smi_settings(index):
150
  return {"ecc": None, "driver": None, "power_limit_w": None}
151
 
152
 
 
 
 
 
 
 
 
 
 
 
 
153
  def mma_tile_check(device="cuda", trials=8):
154
- """Random small-integer m16n16k16 tiles through the wmma fragment
155
- path, compared exactly to the reference GEMM; pins M, N, K, and
156
- operand mapping (integer operands make fp32 accumulation exact)."""
157
  worst = 0.0
158
  for t in range(trials):
159
  g = torch.Generator(device=device).manual_seed(1000 + t)
@@ -171,10 +160,83 @@ def mma_tile_check(device="cuda", trials=8):
171
  "trials": trials}
172
 
173
 
174
- def probe_device(device="cuda", quick=False):
175
- """Empirical device dossier. Each entry is measured by a dedicated
176
- probe kernel and carries units, method, the SM clock it ran under, and
177
- a confidence field."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
  props = torch.cuda.get_device_properties(device)
179
  sm = props.multi_processor_count
180
  l2 = props.L2_cache_size
@@ -187,8 +249,7 @@ def probe_device(device="cuda", quick=False):
187
  "vram_bytes": props.total_memory,
188
  "torch": torch.__version__,
189
  "cuda_runtime": torch.version.cuda,
190
- # instrument settings: readings depend on them, so the
191
- # dossier records them (None when nvidia-smi is unavailable)
192
  **_smi_settings(torch.device(device).index or 0),
193
  }
194
  }
@@ -213,9 +274,8 @@ def probe_device(device="cuda", quick=False):
213
  }
214
  clk = max(traj)
215
 
216
- # ---- device-memory bandwidth: triad per access width, plus read-only,
217
- # write-only, and random-gather
218
- n4 = 16 << 20 # 16M float4 = 256 MB per buffer
219
  if props.total_memory < 4 << 30:
220
  n4 = 4 << 20
221
  a = torch.rand(n4 * 4, device=device)
@@ -239,7 +299,6 @@ def probe_device(device="cuda", quick=False):
239
  dtype=torch.int32).to(device)
240
  ops.mk_gather(a, idx, c, 256, 0)
241
  ms = _median(_events(lambda: ops.mk_gather(a, idx, c, 256, 0), reps))
242
- # traffic accounting: idx reads + 16B random loads + streamed stores
243
  hbm["random_gather_16B"] = round(
244
  (idx.numel() * 4 + 2 * n4 * 16) / (ms * 1e-3) / 1e9, 1)
245
  d["dram_bw"] = _entry(hbm, "GB/s", "streaming triad c=a+s*b per access "
@@ -250,9 +309,9 @@ def probe_device(device="cuda", quick=False):
250
  "so its nominal number is the achievable rate for "
251
  "random access, not a line-efficiency claim")
252
 
253
- # ---- L2 bandwidth (working set 3 buffers inside L2)
254
  ws = max(l2 // 4, 1 << 20)
255
- nl2 = ws // 16 # float4 elems per buffer
256
  al = a[:nl2 * 4].contiguous()
257
  bl = b[:nl2 * 4].contiguous()
258
  cl = c[:nl2 * 4].contiguous()
@@ -267,62 +326,61 @@ def probe_device(device="cuda", quick=False):
267
  "medium", "first pass streams from HBM; amortized "
268
  f"over {passes} passes")
269
 
270
- # ---- latencies: pointer chase per level. The 2 MB L2 ring exceeds
271
- # L1, fits L2 everywhere, and stays TLB-resident after a warm walk;
272
- # the DRAM chain touches 2x L2 in lines, so it evicts itself.
273
  lat = {}
274
  idle_clk = measure_clock(device, ticks=2_000_000, blocks=1)
275
- ring_s = _sattolo_ring(8192, device)
276
- ops.mk_chase_shared(ring_s, 1 << 14, out64)
277
- _sync()
278
- hops = 1 << 16
279
- ops.mk_chase_shared(ring_s, hops, out64)
280
- _sync()
281
- lat["smem_cycles"] = round(int(out64[0].item()) / hops, 1)
282
 
283
- l1_ring_n = (16 << 10) // 4 # 16 KB: L1-resident after a warm walk
284
- ring_l1 = _sattolo_ring(l1_ring_n, device)
285
- ops.mk_chase_global(ring_l1, l1_ring_n, out64)
286
- _sync()
287
- hops_l1 = 1 << 15
288
- ops.mk_chase_global(ring_l1, hops_l1, out64)
289
- _sync()
290
- lat["l1_cycles"] = round(int(out64[0].item()) / hops_l1, 1)
291
-
292
- l2_ring_n = (2 << 20) // 4
293
- ring_l2 = _sattolo_ring(l2_ring_n, device)
294
- ops.mk_chase_global(ring_l2, l2_ring_n, out64) # full-cycle warm walk
295
- _sync()
296
- hops_l2 = 1 << 14
297
- ops.mk_chase_global(ring_l2, hops_l2, out64)
298
- _sync()
299
- lat["l2_cycles"] = round(int(out64[0].item()) / hops_l2, 1)
300
 
 
 
 
 
 
 
 
 
 
 
 
 
301
  hbm_bytes = min(max(4 * l2, 64 << 20), props.total_memory // 8)
302
  ring_h = _sattolo_ring(int(hbm_bytes) // 4, device)
303
  hops_h = max((2 * l2) // 128, 1 << 17)
304
- ops.mk_chase_global(ring_h, 1 << 14, out64) # fault pages / warm TLB
305
- _sync()
306
- ops.mk_chase_global(ring_h, hops_h, out64)
307
- _sync()
308
- lat["dram_cycles"] = round(int(out64[0].item()) / hops_h, 1)
309
  lat["ns_per_cycle"] = round(1.0 / idle_clk, 3)
310
  d["latency"] = _entry(lat, "cycles (single-lane dependent loads)",
311
- "Sattolo-cycle pointer chase; 16 KB ring for L1, "
312
- "2 MB TLB-resident ring for L2, self-evicting "
313
- "chain (2x L2 in touched lines) for HBM",
314
- idle_clk, "high",
315
  "smem includes index arithmetic; HBM includes "
316
  "residual TLB effects at large footprints")
317
  del ring_h, ring_l2, ring_l1, ring_s
318
 
319
- # ---- fp32 FMA and tensor-core throughput
320
  blocks = sm * 4
321
  iters = 20_000 if quick else 60_000
322
  ops.mk_fma_f32(blocks, 256, iters, sink)
323
  ms = _median(_events(lambda: ops.mk_fma_f32(blocks, 256, iters, sink),
324
  reps))
325
- flops = blocks * 256 * iters * 16 * 2 * 2 # 2 chains x 2 flops x unroll
326
  d["fma_f32"] = _entry(round(flops / (ms * 1e-3) / 1e12, 2), "TFLOP/s",
327
  "register-resident FMA chains", clk, "high")
328
  sink64 = torch.zeros(2, dtype=torch.float64, device=device)
@@ -334,43 +392,88 @@ def probe_device(device="cuda", quick=False):
334
  d["fma_f64"] = _entry(round(flops64 / (ms * 1e-3) / 1e12, 3), "TFLOP/s",
335
  "register-resident double FMA chains (the DFMA "
336
  "pipe)", clk, "high")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
337
  mma = {}
338
  mma_klanes = {}
339
- for name, (kind, mn, k_nom, mult, nom_lanes) in _MMA_KINDS.items():
 
340
  it = 30_000 if quick else 100_000
341
  r = ops.mk_mma(kind, blocks, 8, 512, sink)
342
  if r != 0:
343
  mma[name] = "unsupported"
344
  continue
345
  _sync()
346
- # verification pass: with unit operands the accumulator gain IS
347
- # the executed K-lane count (exact at these magnitudes)
348
- cal_iters = 512
349
- sink[1] = 0.0
350
- ops.mk_mma(kind, 1, 1, cal_iters, sink)
351
- _sync()
352
- gain_per_iter = float(sink[1].item()) / cal_iters
353
  mma_klanes[name] = {
354
- "executed_k_per_iter": round(gain_per_iter, 2),
355
- "vs_nominal": round(gain_per_iter / nom_lanes, 3),
 
356
  }
 
 
357
  ms = _median(_events(
358
  lambda k=kind, i=it: ops.mk_mma(k, blocks, 8, i, sink), reps))
359
- flops = blocks * 8 * it * 2.0 * mn * gain_per_iter * mult
360
- mma[name] = round(flops / (ms * 1e-3) / 1e12, 1)
 
 
 
 
361
  d["mma"] = _entry(mma, "TFLOP/s (TOP/s for int8)",
362
  "register-fragment mma_sync loops (wmma; fp8 and "
363
- "2:4-sparse fp16 via inline PTX m16n8k32; sparse "
364
- "counted dense-equivalent, the vendor convention); "
365
- "flops derive from the mean accumulator read-back "
366
  "with every fragment element observed, never the "
367
- "declared loop shape",
 
368
  clk, "medium",
369
  "dense-tile rate without memory traffic; real "
370
  "kernels see this only when staging hides behind it")
371
  d["mma"]["verified_k_lanes"] = mma_klanes
 
 
372
 
373
- # ---- shared-memory bandwidth
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
374
  it_sm = 30_000 if quick else 100_000
375
  ops.mk_smem_bw(blocks, 256, it_sm, sink)
376
  ms = _median(_events(lambda: ops.mk_smem_bw(blocks, 256, it_sm, sink),
@@ -380,8 +483,23 @@ def probe_device(device="cuda", quick=False):
380
  "block-resident read+write streaming (8 B per "
381
  "thread-iteration), aggregated across SMs", clk,
382
  "medium",
383
- "bank-conflict-light access pattern; a conflicted "
384
- "pattern can be far lower")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
385
 
386
  # ---- PCIe / host transfer bandwidth
387
  nbytes = 256 << 20
@@ -412,9 +530,7 @@ def probe_device(device="cuda", quick=False):
412
  "d2d counts one direction of traffic")
413
  del host_pin, host_pag, dev_t, dev_u
414
 
415
- # ---- atomic contention curve: throughput vs number of distinct
416
- # addresses the whole grid hammers (slots = 1 is full contention;
417
- # slots = threads is contention-free), plus the shared-memory point
418
  n_thr = blocks * 256
419
  buf = torch.zeros(n_thr, dtype=torch.float32, device=device)
420
  at = {"curve_slots": {}}
@@ -424,7 +540,7 @@ def probe_device(device="cuda", quick=False):
424
  buf.zero_()
425
  ops.mk_atomics(1, blocks, 256, it_at, buf, slots)
426
  ms = _median(_events(
427
- lambda s=slots: ops.mk_atomics(1, blocks, 256, it_at, buf, s),
428
  reps))
429
  at["curve_slots"][str(slots)] = round(
430
  n_thr * it_at / (ms * 1e-3) / 1e9, 2)
@@ -437,7 +553,7 @@ def probe_device(device="cuda", quick=False):
437
  "of distinct global addresses from 1 (full "
438
  "contention) to one per thread", clk, "high")
439
 
440
- # ---- launch overhead
441
  anchor = sink
442
  n_launch = 500 if quick else 2000
443
  _sync()
@@ -452,10 +568,19 @@ def probe_device(device="cuda", quick=False):
452
  ops.mk_empty(anchor)
453
  _sync()
454
  rt = (time.perf_counter() - t0) / n_rt * 1e6
 
 
455
  d["launch_overhead"] = _entry(
456
- {"queued_us": round(queued, 2), "round_trip_us": round(rt, 2)},
457
- "us", "empty-kernel trains, queued vs launch+sync", clk, "high",
458
- "host-dependent: includes torch op dispatch")
 
 
 
 
 
 
 
459
 
460
  # ---- grid barrier
461
  bar_iters = 500 if quick else 2000
@@ -471,9 +596,7 @@ def probe_device(device="cuda", quick=False):
471
  "unavailable or grid too large", clk,
472
  "high")
473
 
474
- # ---- occupancy table (API) plus timed knees: the same random gather
475
- # at every (threads, dyn_smem) point; when the forced blocks/SM stop
476
- # covering memory latency, achieved bandwidth falls off the cliff
477
  occ = {}
478
  for threads in (128, 256, 512, 1024):
479
  row = {}
@@ -494,8 +617,8 @@ def probe_device(device="cuda", quick=False):
494
  ops.mk_fma_dep(knee_blocks, threads, smem_kb * 1024,
495
  knee_iters, sink)
496
  ms = _median(_events(
497
- lambda t=threads, s=smem_kb: ops.mk_fma_dep(
498
- knee_blocks, t, s * 1024, knee_iters, sink), reps))
499
  fl = knee_blocks * threads * knee_iters * 16 * 2
500
  row[f"{smem_kb}KB"] = round(fl / (ms * 1e-3) / 1e12, 2)
501
  knees[f"t{threads}"] = row
@@ -504,27 +627,160 @@ def probe_device(device="cuda", quick=False):
504
  "at forced (threads, dyn smem) occupancies over fixed total work; "
505
  "the cliff marks where occupancy stops covering latency",
506
  clk, "high", "timing-derived counterpart to the API table above")
 
 
 
507
  return d
508
 
509
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
510
  def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
511
  dtype="fp16", clock_guard=0.97, clock_stride=8, graph=False,
512
- device="cuda"):
513
- """Median-of-N cudaEvent timing with throttle rejection.
514
-
515
- The SM clock is probed every `clock_stride` samples; samples in a
516
- window below `clock_guard` x the best observed clock are rejected
517
- and counted. Declared `bytes`/`flops` yield achieved GB/s / TFLOP/s;
518
- a `dossier` adds roofline placement. `graph=True` times CUDA-graph
519
- replays, stripping Python and torch dispatch (fn must be graph-safe:
520
- no sync, no allocation growth, static shapes; a failed capture falls
521
- back to eager with the reason reported). `clock_stride=1` probes
522
- around every sample, ~0.3 ms each."""
523
  for _ in range(warmup):
524
  fn()
525
  _sync()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
526
  graph_state = "off"
527
  graph_reason = ""
 
 
 
 
 
 
528
  if graph:
529
  pre_rng = torch.cuda.get_rng_state(device)
530
  try:
@@ -539,10 +795,7 @@ def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
539
  except Exception as e: # noqa: BLE001 - fall back, say why
540
  graph_state = "fallback_eager"
541
  graph_reason = f"{type(e).__name__}: {str(e)[:120]}"
542
- # drain the invalidated stream so eager timing can proceed.
543
- # On some torch versions a failed capture also wedges the
544
- # device RNG with no in-process recovery; timing uses no RNG,
545
- # but the caller's later randn may raise.
546
  rng_ok = True
547
  for _attempt in range(5):
548
  try:
@@ -572,25 +825,51 @@ def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
572
  samples.append((_events(fn, n), len(clocks) - 1))
573
  clocks.append(measure_clock(device, ticks=probe_ticks))
574
  best_clk = max(clocks)
575
- kept, rejected = [], 0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
576
  flat = []
577
- for group, ci in samples:
578
- window_ok = (clocks[ci] >= clock_guard * best_clk and
579
- clocks[min(ci + 1, len(clocks) - 1)] >=
580
- clock_guard * best_clk)
581
- for ms in group:
582
- flat.append(ms)
583
- if window_ok:
 
 
 
 
 
 
 
 
 
 
 
 
584
  kept.append(ms)
585
  else:
586
  rejected += 1
587
- if not kept:
 
588
  kept = flat
589
  med = _median(kept)
590
  ks = sorted(kept)
591
  n_k = len(ks)
592
- # order-statistic 95% CI on the median (normal approximation to the
593
- # binomial ranks); degenerate at tiny n, where it spans the sample
594
  half = 1.959964 * (n_k ** 0.5) / 2.0
595
  lo_i = max(0, int(n_k / 2 - half) - 1)
596
  hi_i = min(n_k - 1, int(n_k / 2 + half))
@@ -605,12 +884,25 @@ def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
605
  "mean_ms": round(sum(kept) / n_k, 4),
606
  "samples": len(flat),
607
  "rejected_throttled": rejected,
 
608
  "graph_captured": graph_state == "captured",
609
  "clock_ghz": {"best": round(best_clk, 3),
610
  "worst": round(min(clocks), 3)},
611
  }
 
 
 
 
 
 
 
612
  if graph_reason:
613
  rep["graph_fallback_reason"] = graph_reason
 
 
 
 
 
614
  if bytes is not None:
615
  rep["achieved_gbs"] = round(bytes / (med * 1e-3) / 1e9, 1)
616
  if flops is not None:
@@ -620,6 +912,8 @@ def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
620
  if dossier is not None and bytes and flops:
621
  bw = max(dossier["dram_bw"]["value"].values()) * 1e9
622
  mma_v = dossier["mma"]["value"].get(dtype)
 
 
623
  peak = (mma_v if isinstance(mma_v, (int, float))
624
  else dossier["fma_f32"]["value"]) * 1e12
625
  ai = flops / bytes
@@ -630,13 +924,12 @@ def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
630
  "pct_of_roof": round(100 * (flops / (med * 1e-3)) / roof, 1),
631
  "peak_dtype": dtype,
632
  }
 
633
  return rep
634
 
635
 
636
  def _to_ordered_int(t):
637
- """Map float bit patterns to integers whose difference is the ULP
638
- distance: positive floats keep their bits, negative floats mirror
639
- below zero, so the mapping is monotone in the float value."""
640
  if t.dtype == torch.float32:
641
  u = t.view(torch.int32).to(torch.int64) & 0xFFFFFFFF
642
  half = 1 << 31
@@ -652,11 +945,12 @@ def _to_ordered_int(t):
652
 
653
 
654
  def ulp_diff(a, b):
655
- """Elementwise ULP distance between same-dtype tensors. For floats,
656
- NaNs in the same position count as 0 and a one-sided NaN counts as
657
- 2**62; for integer and bool tensors the distance is the absolute
658
- numeric difference (an exact op must be exactly equal)."""
659
- assert a.dtype == b.dtype and a.shape == b.shape
 
660
  if not a.is_floating_point():
661
  return (a.to(torch.int64) - b.to(torch.int64)).abs()
662
  ia, ib = _to_ordered_int(a), _to_ordered_int(b)
@@ -673,9 +967,7 @@ class order_shuffles:
673
 
674
  @staticmethod
675
  def last_dim():
676
- """Permute the last dim of every tensor arg identically: the
677
- accumulation order of a last-dim reduction changes, the value
678
- does not."""
679
  def shuffle(args, seed):
680
  g = torch.Generator(device=args[0].device).manual_seed(seed)
681
  p = torch.randperm(args[0].shape[-1], device=args[0].device,
@@ -685,8 +977,7 @@ class order_shuffles:
685
 
686
  @staticmethod
687
  def matmul_k():
688
- """Permute K of (A [.., M, K], B [.., K, N]) consistently: the
689
- product is unchanged, the accumulation order is not."""
690
  def shuffle(args, seed):
691
  a_m, b_m = args[0], args[1]
692
  g = torch.Generator(device=a_m.device).manual_seed(seed)
@@ -700,37 +991,70 @@ def _as_outputs(x):
700
  return list(x) if isinstance(x, (tuple, list)) else [x]
701
 
702
 
703
- def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
704
- ref_variants=None, ulp_tol=None, atol=None, rtol=None,
705
- band_margin=4.0, zero_atol=None, worst_k=8, seed=0):
706
- """Adversarial correctness comparison with ULP evidence.
707
-
708
- `gen(seed) -> args`; `ref(*args)` and `cand(*args)` return a tensor
709
- or tuple (worst output governs). With `order_shuffle(args, seed)`
710
- reordering contractual-noise dimensions (see `order_shuffles`), each
711
- element is judged against its own measured POINTWISE reduction-order
712
- band: pass is max(ulp / (own_band + 2)) <= band_margin; without a
713
- band, max ULP <= ulp_tol (default 2); `atol`/`rtol` are an
714
- alternative acceptance route. `zero_atol`: elements with
715
- |ref| < zero_atol are judged by absolute difference (ULP explodes
716
- across the denormal range). The report carries the top-`worst_k`
717
- offenders (index, ref, cand, ULP). Order shuffles cover the
718
- reference's own topology; a candidate with a different legitimate
719
- topology (split-K) needs `ref_variants`, callables `f(args) -> out`
720
- whose outputs widen the pointwise band."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
721
  worst = {"max_ulp": -1}
722
  total_hist = {}
723
  per_trial = []
724
  max_abs_seen = 0.0
725
  max_rel_seen = 0.0
 
726
  for t in range(trials):
727
  args = gen(seed + t)
 
 
 
 
728
  r_outs = _as_outputs(ref(*args))
729
  c_outs = _as_outputs(cand(*args))
730
- assert len(r_outs) == len(c_outs), "output arity mismatch"
731
- # pointwise band: each element is judged against its own measured
732
- # order sensitivity (cancellation makes near-zero outputs
733
- # legitimately order-sensitive while neighbors are stable)
 
 
 
 
 
 
 
 
 
 
 
734
  bands = None
735
  band = 0
736
  band_score = 0.0
@@ -754,8 +1078,11 @@ def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
754
  max_ulp = 0
755
  mean_ulp = 0.0
756
  first_div = -1
 
757
  n_el = 0
758
  n_above_band = 0
 
 
759
  nan_r = nan_c = inf_r = inf_c = 0
760
  worst_elements = []
761
  for oi, (r, c) in enumerate(zip(r_outs, c_outs)):
@@ -773,8 +1100,11 @@ def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
773
  mean_ulp += float(d.double().sum().item())
774
  n_el += d.numel()
775
  nz = (d > 0)
 
 
776
  if first_div < 0 and nz.any():
777
  first_div = int(nz.flatten().nonzero()[0].item())
 
778
  if nz.any():
779
  buckets = torch.clamp(
780
  torch.log2(d[nz].double()).floor(), 0, 62
@@ -791,6 +1121,7 @@ def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
791
  if val > 0:
792
  worst_elements.append({
793
  "output": oi, "index": int(ix),
 
794
  "ref": float(rf[ix].item()),
795
  "cand": float(cf[ix].item()),
796
  "ulp": int(val)})
@@ -810,23 +1141,27 @@ def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
810
  inf_r += int(torch.isinf(r).sum().item())
811
  inf_c += int(torch.isinf(c).sum().item())
812
  worst_elements.sort(key=lambda w: -w["ulp"])
 
813
  trial_rep = {
814
  "max_ulp": max_ulp,
815
  "mean_ulp": round(mean_ulp / max(n_el, 1), 3),
816
  "first_divergent_index": first_div,
 
817
  "nan_ref": nan_r, "nan_cand": nan_c,
818
  "inf_ref": inf_r, "inf_cand": inf_c,
819
  "order_band_ulp": band,
820
  "band_score": round(band_score, 2),
821
  "frac_above_band": round(n_above_band / max(n_el, 1), 6),
 
 
822
  "worst_elements": worst_elements[:worst_k],
823
  }
824
  per_trial.append(trial_rep)
825
  if max_ulp > worst["max_ulp"]:
826
  worst = trial_rep
827
- band_max = max(t["order_band_ulp"] for t in per_trial)
828
  max_seen = max(t["max_ulp"] for t in per_trial)
829
- if order_shuffle is not None or ref_variants:
 
830
  score = max(t["band_score"] for t in per_trial)
831
  ulp_ok = score <= band_margin
832
  criterion = (f"pointwise max(ulp / (order_band + 2)) = "
@@ -841,8 +1176,34 @@ def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
841
  max_rel_seen <= (rtol or 0) or max_abs_seen <= (atol or 0)
842
  criterion += (f"; OR max_abs {max_abs_seen:.3e} <= atol {atol} / "
843
  f"max_rel {max_rel_seen:.3e} <= rtol {rtol}")
844
- return {
845
- "pass": bool(ulp_ok or tol_ok),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
846
  "criterion": criterion,
847
  "worst_trial": worst,
848
  "max_abs": max_abs_seen,
@@ -850,6 +1211,9 @@ def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
850
  "trials": per_trial,
851
  "ulp_hist_log2": dict(sorted(total_hist.items())),
852
  }
 
 
 
853
 
854
 
855
  _FUZZ_VARIANTS = ("contig", "transposed", "strided_rows", "offset_slice",
@@ -865,6 +1229,30 @@ def _context_alive(device):
865
  return False
866
 
867
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
868
  def _fuzz_build(make, shape, dtype):
869
  try:
870
  out = make(tuple(shape), dtype)
@@ -881,7 +1269,7 @@ def _fuzz_variant(x, v):
881
  if v == "strided_rows":
882
  return x[::2] if x.dim() >= 1 and x.shape[0] >= 2 else x
883
  if v == "offset_slice":
884
- # a storage-offset-1 view: misaligned for vectorized loads
885
  return x.flatten()[1:] if x.numel() > 1 else x
886
  if v == "overflow_expand":
887
  if x.numel() == 0:
@@ -895,8 +1283,7 @@ def _fuzz_variant(x, v):
895
 
896
  def _fuzz_group(op, ref, make, shape, dtype, variants, ulp_tol, device,
897
  stop_on_context_loss=True):
898
- """All variant cases for one (shape, dtype) group; returns (rows,
899
- context_lost)."""
900
  rows = []
901
  bases = _fuzz_build(make, shape, dtype)
902
  lost = False
@@ -933,8 +1320,7 @@ def _fuzz_group(op, ref, make, shape, dtype, variants, ulp_tol, device,
933
  got = op(*xs)
934
  want = ref(*xs)
935
  if v == "overflow_expand":
936
- # bounded comparison: the head plus the high-index tail,
937
- # which is where int32 flat indexing wraps
938
  w = 1 << 20
939
  gf, wf = got.flatten(), want.flatten()
940
  pairs = ((gf[:w], wf[:w]), (gf[-w:], wf[-w:]))
@@ -959,20 +1345,30 @@ def _fuzz_group(op, ref, make, shape, dtype, variants, ulp_tol, device,
959
  return rows, lost
960
 
961
 
962
- # isolate child: a plain subprocess that bootstraps its own metakernel
963
- # (get_kernel, else the dev-root JIT fallback) and unpickles op/ref/make
964
- # by module reference, so it works under get_kernel's synthetic names
965
- _REPO_ID = "phanerozoic/metakernel"
966
- _REPO_VERSION = 1
967
-
968
- _ISOLATE_BOOTSTRAP = r"""
969
- import json
970
- import os
971
- import pickle
972
- import sys
973
-
 
 
 
 
 
 
 
 
 
 
974
  meta = json.load(open(sys.argv[1], "r"))
975
- sys.path[:0] = meta["sys_path"] # BEFORE unpickling module-ref functions
976
  try:
977
  import kernels
978
  _mk = kernels.get_kernel(meta["repo"], version=meta["version"],
@@ -985,85 +1381,165 @@ except Exception:
985
  import load_local
986
  _mk = load_local.load()
987
  import torch
988
- op, ref, make = pickle.load(open(sys.argv[2], "rb"))
989
- rows, _lost = _mk._fuzz_group(
990
- op, ref, make, tuple(meta["shape"]), getattr(torch, meta["dtype"]),
991
- tuple(meta["variants"]), meta["ulp_tol"], meta["device"],
992
- stop_on_context_loss=False)
993
- pickle.dump(rows, open(sys.argv[3], "wb"))
 
 
 
 
 
 
 
 
 
 
 
 
994
  """
995
 
996
 
997
- def fuzz_shapes(op, ref, make, shapes=None, variants=_FUZZ_VARIANTS,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
998
  dtypes=(torch.float32,), ulp_tol=64, isolate=False,
999
- isolate_timeout_s=180, device="cuda"):
1000
- """Run op vs ref across edge shapes, dtypes, and layout mutations;
1001
- report ok / mismatch / exception per case.
1002
-
1003
- `make(shape, dtype)` (or `make(shape)`) builds a tensor or tuple;
1004
- layout variants derive non-contiguous and misaligned views whose
1005
- content equals a contiguous copy. `overflow_expand` stride-0-expands
1006
- past 2**31 logical elements to catch int32 indexing (`skipped_vram`
1007
- when it would not fit). Every case carries a `repro` recipe.
1008
- In-process, the CUDA context is health-checked after exceptions and
1009
- remaining cases marked `context_lost` if killed; `isolate=True`
1010
- spawns each (shape, dtype) group in a subprocess, so a
1011
- context-killing candidate costs one group (`crashed_isolated`);
1012
- op/ref/make must be picklable."""
1013
  if shapes is None:
1014
  shapes = [(), (7,), (0, 8), (1, 1), (1, 17), (3, 63), (17, 129),
1015
  (128, 128), (1023, 5), (2, 4096), (2, 3, 5)]
 
 
1016
 
1017
  rows = []
1018
  lost = False
1019
  if isolate:
1020
  import json
1021
  import pickle
1022
- import subprocess
1023
  import sys as _sys
1024
  import tempfile
1025
  tmpdir = tempfile.mkdtemp(prefix="mk_fuzz_")
1026
  fns = os.path.join(tmpdir, "fns.pkl")
 
 
1027
  with open(fns, "wb") as f:
1028
- pickle.dump((op, ref, make), f)
1029
- for dtype in dtypes:
1030
- for shape in shapes:
1031
- meta = os.path.join(tmpdir, "meta.json")
1032
- out = os.path.join(tmpdir, "rows.pkl")
1033
- if os.path.exists(out):
1034
- os.remove(out)
1035
- with open(meta, "w", encoding="utf-8") as f:
1036
- json.dump({
1037
- "sys_path": [p for p in _sys.path
1038
- if isinstance(p, str)],
1039
- "repo": _REPO_ID, "version": _REPO_VERSION,
 
 
 
 
1040
  "shape": list(shape),
1041
  "dtype": str(dtype).replace("torch.", ""),
1042
  "variants": list(variants),
1043
  "ulp_tol": ulp_tol, "device": device,
1044
- }, f)
1045
- group = None
1046
- stderr_tail = ""
1047
- try:
1048
- r = subprocess.run(
1049
- [_sys.executable, "-c", _ISOLATE_BOOTSTRAP, meta,
1050
- fns, out], timeout=isolate_timeout_s,
1051
- capture_output=True)
1052
- if r.returncode == 0 and os.path.exists(out):
1053
  with open(out, "rb") as f:
1054
- group = pickle.load(f)
1055
- exitcode = r.returncode
1056
- stderr_tail = r.stderr.decode(
1057
- errors="replace")[-400:]
1058
- except subprocess.TimeoutExpired:
1059
- exitcode = "timeout"
1060
- if group is None:
1061
- group = [{"shape": tuple(shape), "variant": "*",
1062
- "dtype": str(dtype).replace("torch.", ""),
1063
- "status": "crashed_isolated",
1064
- "exitcode": exitcode,
1065
- "stderr_tail": stderr_tail}]
1066
- rows.extend(group)
1067
  else:
1068
  pending = [(dt, sh) for dt in dtypes for sh in shapes]
1069
  for gi, (dtype, shape) in enumerate(pending):
@@ -1080,21 +1556,27 @@ def fuzz_shapes(op, ref, make, shapes=None, variants=_FUZZ_VARIANTS,
1080
  n_bad = sum(1 for r in rows
1081
  if r["status"] not in ("ok", "skipped_vram",
1082
  "skipped_empty"))
1083
- return {"cases": rows, "failed": n_bad, "total": len(rows),
1084
- "context_lost": lost}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1085
 
1086
 
1087
  def sweep(factory, grid, iters=30, warmup=5, bytes=None, flops=None,
1088
  check=None, budget_s=None, race=False):
1089
- """Launch-configuration search: `factory(**point) -> callable`; one
1090
- bench row per grid point, sorted fastest first. A point whose factory
1091
- or run raises is reported, not skipped silently. `check(fn) -> bool`
1092
- is an optional correctness gate recorded per row (a fast wrong config
1093
- must not win a sweep). `budget_s` caps total sweep time; points not
1094
- reached are reported as `not_run` rather than silently dropped.
1095
- `race=True` runs successive halving: a quick pass over every point at
1096
- iters/4, then the full budget only on the fastest quartile; pruned
1097
- points keep their quick measurement and status `pruned_race`."""
1098
  keys = list(grid.keys())
1099
 
1100
  def expand(i, acc):
@@ -1159,13 +1641,71 @@ def sweep(factory, grid, iters=30, warmup=5, bytes=None, flops=None,
1159
  continue
1160
  rows.append(run_point(p, iters))
1161
  rows.sort(key=lambda r: (not r.get("check", True), r["median_ms"]))
 
 
 
 
 
 
 
 
 
 
 
1162
  return rows
1163
 
1164
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1165
  def alloc_stamps(n, blocks=None, device="cuda"):
1166
- """Device buffer for the MK_STAMP convention (metakernel_cuda/
1167
- stamps.cuh): the candidate kernel writes clock64() into slot i. With
1168
- `blocks`, allocates the [blocks, n] layout for MK_STAMP_BLOCK."""
1169
  if blocks is not None:
1170
  return torch.zeros(blocks, n, dtype=torch.int64, device=device)
1171
  return torch.zeros(n, dtype=torch.int64, device=device)
@@ -1175,8 +1715,7 @@ _DRIFT_SKIP = ("device", "clock", "launch_overhead", "transfer_bw")
1175
 
1176
 
1177
  def pinned_dossiers():
1178
- """Names of the dossiers pinned in this release (one per certified
1179
- architecture)."""
1180
  return sorted(_PINNED.keys())
1181
 
1182
 
@@ -1195,11 +1734,7 @@ def _numeric_leaves(d, prefix=""):
1195
 
1196
 
1197
  def dossier_drift(current, pinned=None, rel_tol=0.35, skip=_DRIFT_SKIP):
1198
- """Compare a fresh dossier against the release-pinned one for the
1199
- same architecture: per-leaf relative drift, worst drift, entries
1200
- over `rel_tol`. Host-dependent entries and raw clocks are skipped by
1201
- default (`skip=()` compares everything); `pinned=None` auto-selects
1202
- by sm_arch and raises if no pin exists."""
1203
  if pinned is None:
1204
  arch = current["device"]["sm_arch"]
1205
  matches = [k for k, v in _PINNED.items()
@@ -1222,16 +1757,19 @@ def dossier_drift(current, pinned=None, rel_tol=0.35, skip=_DRIFT_SKIP):
1222
  "rel": round(rel, 4)})
1223
  worst = max((e["rel"] for e in entries), default=0.0)
1224
  over = [e for e in entries if e["rel"] > rel_tol]
1225
- return {"entries": entries, "worst_rel": worst, "over_tol": over,
1226
- "rel_tol": rel_tol, "compared": len(entries),
1227
- "pass": len(over) == 0}
 
 
 
 
 
1228
 
1229
 
1230
  def stamped_demo(n=1 << 22, fma_iters=4000, blocks=None, threads=256,
1231
  device="cuda"):
1232
- """Run the shipped MK_STAMP_BLOCK example kernel (global load, FMA
1233
- compute, global store) and decode its stamps; a template for
1234
- instrumenting a candidate."""
1235
  props = torch.cuda.get_device_properties(device)
1236
  if blocks is None:
1237
  blocks = props.multi_processor_count
@@ -1244,11 +1782,7 @@ def stamped_demo(n=1 << 22, fma_iters=4000, blocks=None, threads=256,
1244
 
1245
 
1246
  def read_stamps(stamps, labels=None, clock_ghz=None):
1247
- """Decode MK_STAMP phases: consecutive deltas in ticks and in
1248
- microseconds under the measured (or provided) SM clock. A 1-D buffer
1249
- (MK_STAMP: block 0 only) yields one delta per phase; a 2-D [B, n]
1250
- buffer (MK_STAMP_BLOCK) yields min / median / max across blocks per
1251
- phase, with blocks whose stamps stayed zero excluded."""
1252
  if clock_ghz is None:
1253
  clock_ghz = measure_clock(stamps.device)
1254
 
@@ -1284,3 +1818,104 @@ def read_stamps(stamps, labels=None, clock_ghz=None):
1284
  return {"phases": phases, "clock_ghz": round(clock_ghz, 3),
1285
  "blocks_reporting": int(live.sum().item()),
1286
  "blocks_total": int(live.numel())}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Measuring instrument for the kernel-design loop: measured device facts, throttle-aware timing, adversarial correctness comparison, all as loadable ops returning plain data."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  import math
3
  import os
4
  import time
 
8
  try:
9
  from ._ops import ops
10
  except ImportError: # pragma: no cover - spawned child in a source tree
11
+ # children re-import the package; a source checkout rebuilds the op table from the cached JIT extension
 
 
12
  _root = os.environ.get("METAKERNEL_DEV_ROOT")
13
  if not _root:
14
  raise
 
30
  except ImportError: # pragma: no cover - fresh tree before first pin
31
  _PINNED = {}
32
 
33
+ __all__ = ["probe_device", "probe_all_devices", "bench", "compare", "judge",
34
+ "order_shuffles", "fuzz_shapes", "sweep", "alloc_stamps",
35
+ "read_stamps", "stamped_demo", "measure_clock", "ulp_diff",
36
+ "dossier_drift", "pinned_dossiers", "mma_tile_check", "schema",
37
+ "probe_capturable", "ops"]
38
+
39
+ _REPO_ID = "phanerozoic/metakernel"
40
+ _REPO_VERSION = 1
41
+ _PROBE_SCHEMA = 2
42
+
43
+ # kind id, C-tile M*N, nominal K, nominal K-lanes per unrolled loop iteration
44
  _MMA_KINDS = {
45
+ "fp16": (0, 16 * 16, 16, 64.0),
46
+ "bf16": (1, 16 * 16, 16, 64.0),
47
+ "tf32": (2, 16 * 16, 8, 32.0),
48
+ "int8": (3, 16 * 16, 16, 64.0),
49
+ "fp8_e4m3": (4, 16 * 8, 32, 256.0),
50
+ "fp16_sparse_2_4": (5, 16 * 8, 32, 128.0),
51
+ "fp8_e5m2": (6, 16 * 8, 32, 256.0),
52
  }
53
 
54
 
 
57
 
58
 
59
  def _events(fn, reps):
60
+ """Time reps calls of fn with one cudaEvent pair each; returns a ms list."""
61
  starts = [torch.cuda.Event(enable_timing=True) for _ in range(reps)]
62
  stops = [torch.cuda.Event(enable_timing=True) for _ in range(reps)]
63
  for s, e in zip(starts, stops):
 
69
 
70
 
71
  def measure_clock(device="cuda", ticks=4_000_000, blocks=None):
72
+ """Effective SM clock in GHz from clock64 ticks over wall time under an FMA spin."""
 
73
  props = torch.cuda.get_device_properties(device)
74
  if blocks is None:
75
  blocks = props.multi_processor_count
 
102
 
103
 
104
  def _sattolo_ring(n, device, generator=None):
105
+ """Random single-cycle permutation as an int32 next-pointer table."""
 
106
  perm = torch.randperm(n, generator=generator)
107
  ring = torch.empty(n, dtype=torch.int32)
108
  ring[perm] = torch.roll(perm, -1).to(torch.int32)
 
110
 
111
 
112
  def _smi_settings(index):
113
+ """ECC mode, driver, power limit via nvidia-smi; None fields when unavailable."""
 
114
  import subprocess
115
  try:
116
  out = subprocess.run(
 
130
  return {"ecc": None, "driver": None, "power_limit_w": None}
131
 
132
 
133
+ def _unravel(flat, shape):
134
+ """Multi-dimensional index for a flat index into shape."""
135
+ if not shape:
136
+ return []
137
+ out = []
138
+ for s in reversed(list(shape)):
139
+ out.append(int(flat % s))
140
+ flat //= s
141
+ return list(reversed(out))
142
+
143
+
144
  def mma_tile_check(device="cuda", trials=8):
145
+ """Random small-integer m16n16k16 tiles through the wmma path, compared exactly to the reference GEMM."""
 
 
146
  worst = 0.0
147
  for t in range(trials):
148
  g = torch.Generator(device=device).manual_seed(1000 + t)
 
160
  "trials": trials}
161
 
162
 
163
+ def _tenancy_canary(device, rel_tol=0.10, attempts=3):
164
+ """Fixed-cost triad timed in paired rounds; busy only when every attempt disagrees."""
165
+ n4 = 4 << 20
166
+ a = torch.rand(n4 * 4, device=device)
167
+ b = torch.rand(n4 * 4, device=device)
168
+ c = torch.empty(n4 * 4, device=device)
169
+ ops.mk_triad(a, b, c, 4, 1.5)
170
+ rels = []
171
+ for _ in range(attempts):
172
+ r1 = _median(_events(lambda: ops.mk_triad(a, b, c, 4, 1.5), 3))
173
+ r2 = _median(_events(lambda: ops.mk_triad(a, b, c, 4, 1.5), 3))
174
+ rel = abs(r1 - r2) / min(r1, r2)
175
+ rels.append(round(rel, 4))
176
+ if rel <= rel_tol:
177
+ return False, {"rel_diffs": rels, "rel_tol": rel_tol}
178
+ return True, {"rel_diffs": rels, "rel_tol": rel_tol}
179
+
180
+
181
+ def _cache_dir():
182
+ base = os.environ.get("LOCALAPPDATA") or os.path.expanduser("~/.cache")
183
+ d = os.path.join(base, "metakernel")
184
+ os.makedirs(d, exist_ok=True)
185
+ return d
186
+
187
+
188
+ def _cache_path(device, quick):
189
+ import re
190
+ props = torch.cuda.get_device_properties(device)
191
+ smi = _smi_settings(torch.device(device).index or 0)
192
+ slug = re.sub(r"[^a-z0-9]+", "", props.name.lower())[:24]
193
+ key = "sm%d%d_%s_drv%s_cu%s_q%d_s%d" % (
194
+ props.major, props.minor, slug, smi["driver"] or "na",
195
+ torch.version.cuda, int(quick), _PROBE_SCHEMA)
196
+ return os.path.join(_cache_dir(), key + ".json")
197
+
198
+
199
+ def _mma_calibrate(kind_id, sink):
200
+ """Executed K-lanes per loop iteration from a sub-saturation accumulator read-back, with a linearity check."""
201
+ gains = []
202
+ for cal in (4, 32):
203
+ sink[1] = 0.0
204
+ ops.mk_mma(kind_id, 1, 1, cal, sink)
205
+ _sync()
206
+ gains.append(float(sink[1].item()) / cal)
207
+ linear = gains[0] > 0 and abs(gains[1] - gains[0]) / gains[0] < 0.02
208
+ return gains[0], linear
209
+
210
+
211
+ def _fp8_accum_stall(kind_id, sink, k_exec):
212
+ """Growth curve of the fp8 accumulator; a stall reveals the effective accumulate mantissa width."""
213
+ vals = []
214
+ for it in (32, 64, 128, 256, 512):
215
+ sink[1] = 0.0
216
+ ops.mk_mma(kind_id, 1, 1, it, sink)
217
+ _sync()
218
+ vals.append(float(sink[1].item()) / 2.0)
219
+ if vals[-1] == vals[-2] and vals[-1] < vals[0] * 16 * 0.99:
220
+ bits = int(round(math.log2(vals[-1] / (2.0 * k_exec))))
221
+ return {"stall_element_value": vals[-1],
222
+ "effective_accum_mantissa_bits": bits}
223
+ return {"stall_element_value": None,
224
+ "effective_accum_mantissa_bits": None}
225
+
226
+
227
+ def probe_device(device="cuda", quick=False, cached=True,
228
+ tenancy_check=True):
229
+ """Empirical device dossier; every entry measured by a dedicated probe kernel and stamped with its SM clock."""
230
+ cpath = _cache_path(device, quick)
231
+ if cached and os.path.exists(cpath):
232
+ import json
233
+ return json.load(open(cpath, encoding="utf-8"))
234
+ if tenancy_check:
235
+ busy, ev = _tenancy_canary(device)
236
+ if busy:
237
+ raise RuntimeError("device_busy: canary rounds disagree %s; "
238
+ "idle the device or pass "
239
+ "tenancy_check=False" % ev)
240
  props = torch.cuda.get_device_properties(device)
241
  sm = props.multi_processor_count
242
  l2 = props.L2_cache_size
 
249
  "vram_bytes": props.total_memory,
250
  "torch": torch.__version__,
251
  "cuda_runtime": torch.version.cuda,
252
+ # instrument settings recorded because readings depend on them
 
253
  **_smi_settings(torch.device(device).index or 0),
254
  }
255
  }
 
274
  }
275
  clk = max(traj)
276
 
277
+ # ---- device-memory bandwidth per access width plus read/write/gather
278
+ n4 = 16 << 20
 
279
  if props.total_memory < 4 << 30:
280
  n4 = 4 << 20
281
  a = torch.rand(n4 * 4, device=device)
 
299
  dtype=torch.int32).to(device)
300
  ops.mk_gather(a, idx, c, 256, 0)
301
  ms = _median(_events(lambda: ops.mk_gather(a, idx, c, 256, 0), reps))
 
302
  hbm["random_gather_16B"] = round(
303
  (idx.numel() * 4 + 2 * n4 * 16) / (ms * 1e-3) / 1e9, 1)
304
  d["dram_bw"] = _entry(hbm, "GB/s", "streaming triad c=a+s*b per access "
 
309
  "so its nominal number is the achievable rate for "
310
  "random access, not a line-efficiency claim")
311
 
312
+ # ---- L2 bandwidth over an L2-resident working set
313
  ws = max(l2 // 4, 1 << 20)
314
+ nl2 = ws // 16
315
  al = a[:nl2 * 4].contiguous()
316
  bl = b[:nl2 * 4].contiguous()
317
  cl = c[:nl2 * 4].contiguous()
 
326
  "medium", "first pass streams from HBM; amortized "
327
  f"over {passes} passes")
328
 
329
+ # ---- latencies: min of 3 chases per level so a co-tenant burst cannot land in the dossier
 
 
330
  lat = {}
331
  idle_clk = measure_clock(device, ticks=2_000_000, blocks=1)
 
 
 
 
 
 
 
332
 
333
+ def chase(op, ring, warm_hops, hops, extra=None):
334
+ best = None
335
+ for _ in range(3):
336
+ if extra is None:
337
+ op(ring, warm_hops, out64)
338
+ _sync()
339
+ op(ring, hops, out64)
340
+ else:
341
+ op(ring, extra, warm_hops, out64)
342
+ _sync()
343
+ op(ring, extra, hops, out64)
344
+ _sync()
345
+ t = int(out64[0].item()) / hops
346
+ best = t if best is None else min(best, t)
347
+ return best
 
 
348
 
349
+ ring_s = _sattolo_ring(8192, device)
350
+ lat["smem_cycles"] = round(
351
+ chase(lambda r, h, o: ops.mk_chase_shared(r, h, o), ring_s,
352
+ 1 << 14, 1 << 16), 1)
353
+ ring_l1 = _sattolo_ring((16 << 10) // 4, device)
354
+ lat["l1_cycles"] = round(
355
+ chase(lambda r, h, o: ops.mk_chase_global(r, h, o), ring_l1,
356
+ ring_l1.numel(), 1 << 15), 1)
357
+ ring_l2 = _sattolo_ring((2 << 20) // 4, device)
358
+ lat["l2_cycles"] = round(
359
+ chase(lambda r, h, o: ops.mk_chase_global(r, h, o), ring_l2,
360
+ ring_l2.numel(), 1 << 14), 1)
361
  hbm_bytes = min(max(4 * l2, 64 << 20), props.total_memory // 8)
362
  ring_h = _sattolo_ring(int(hbm_bytes) // 4, device)
363
  hops_h = max((2 * l2) // 128, 1 << 17)
364
+ lat["dram_cycles"] = round(
365
+ chase(lambda r, h, o: ops.mk_chase_global(r, h, o), ring_h,
366
+ 1 << 14, hops_h), 1)
 
 
367
  lat["ns_per_cycle"] = round(1.0 / idle_clk, 3)
368
  d["latency"] = _entry(lat, "cycles (single-lane dependent loads)",
369
+ "Sattolo-cycle pointer chase, min of 3 runs; 16 "
370
+ "KB ring for L1, 2 MB TLB-resident ring for L2, "
371
+ "self-evicting chain (2x L2 in touched lines) "
372
+ "for HBM", idle_clk, "high",
373
  "smem includes index arithmetic; HBM includes "
374
  "residual TLB effects at large footprints")
375
  del ring_h, ring_l2, ring_l1, ring_s
376
 
377
+ # ---- fp32/fp64 FMA and tensor-core throughput
378
  blocks = sm * 4
379
  iters = 20_000 if quick else 60_000
380
  ops.mk_fma_f32(blocks, 256, iters, sink)
381
  ms = _median(_events(lambda: ops.mk_fma_f32(blocks, 256, iters, sink),
382
  reps))
383
+ flops = blocks * 256 * iters * 16 * 2 * 2
384
  d["fma_f32"] = _entry(round(flops / (ms * 1e-3) / 1e12, 2), "TFLOP/s",
385
  "register-resident FMA chains", clk, "high")
386
  sink64 = torch.zeros(2, dtype=torch.float64, device=device)
 
392
  d["fma_f64"] = _entry(round(flops64 / (ms * 1e-3) / 1e12, 3), "TFLOP/s",
393
  "register-resident double FMA chains (the DFMA "
394
  "pipe)", clk, "high")
395
+
396
+ # ---- ILP ladder: 1/2/4 chains at low occupancy separates latency-bound from pipe-bound
397
+ ilp = {}
398
+ it_ilp = 20_000 if quick else 60_000
399
+ for ch in (1, 2, 4):
400
+ ops.mk_fma_ilp(ch, sm, 64, it_ilp, sink)
401
+ ms = _median(_events(
402
+ lambda c2=ch: ops.mk_fma_ilp(c2, sm, 64, it_ilp, sink), reps))
403
+ ilp[f"ilp{ch}"] = round(
404
+ sm * 64 * it_ilp * 16 * 2 * ch / (ms * 1e-3) / 1e12, 2)
405
+ ilp["ratio_4_1"] = round(ilp["ilp4"] / ilp["ilp1"], 2)
406
+ d["fma_ilp"] = _entry(ilp, "TFLOP/s",
407
+ "1/2/4 independent dependent FMA chains per "
408
+ "thread at one block of 64 threads per SM; a "
409
+ "ratio near 4 means latency-bound at that "
410
+ "occupancy", clk, "high")
411
+
412
  mma = {}
413
  mma_klanes = {}
414
+ fp8_accum = {}
415
+ for name, (kind, mn, k_nom, nom_lanes) in _MMA_KINDS.items():
416
  it = 30_000 if quick else 100_000
417
  r = ops.mk_mma(kind, blocks, 8, 512, sink)
418
  if r != 0:
419
  mma[name] = "unsupported"
420
  continue
421
  _sync()
422
+ gain, linear = _mma_calibrate(kind, sink)
 
 
 
 
 
 
423
  mma_klanes[name] = {
424
+ "executed_k_per_iter": round(gain, 2),
425
+ "vs_nominal": round(gain / nom_lanes, 3),
426
+ "calibration_linear": linear,
427
  }
428
+ if name.startswith("fp8"):
429
+ fp8_accum[name] = _fp8_accum_stall(kind, sink, k_nom)
430
  ms = _median(_events(
431
  lambda k=kind, i=it: ops.mk_mma(k, blocks, 8, i, sink), reps))
432
+ tflops = blocks * 8 * it * 2.0 * mn * gain / (ms * 1e-3) / 1e12
433
+ if name == "fp16_sparse_2_4":
434
+ mma[name] = {"executed": round(tflops, 1),
435
+ "dense_equivalent": round(2 * tflops, 1)}
436
+ else:
437
+ mma[name] = round(tflops, 1)
438
  d["mma"] = _entry(mma, "TFLOP/s (TOP/s for int8)",
439
  "register-fragment mma_sync loops (wmma; fp8 and "
440
+ "2:4-sparse fp16 via inline PTX m16n8k32); flops "
441
+ "derive from a sub-saturation accumulator read-back "
 
442
  "with every fragment element observed, never the "
443
+ "declared loop shape; sparse reports executed and "
444
+ "dense-equivalent flops",
445
  clk, "medium",
446
  "dense-tile rate without memory traffic; real "
447
  "kernels see this only when staging hides behind it")
448
  d["mma"]["verified_k_lanes"] = mma_klanes
449
+ if fp8_accum:
450
+ d["mma"]["fp8_accum"] = fp8_accum
451
 
452
+ # ---- fed MMA: smem-staged fp16 tiles under cp.async double buffering
453
+ if props.major >= 8:
454
+ gbuf = torch.ones(16 << 20, dtype=torch.float16, device=device)
455
+ sink[1] = 0.0
456
+ r = ops.mk_mma_fed(gbuf, 1, 1, 64, sink)
457
+ if r == 0:
458
+ _sync()
459
+ fed_gain = float(sink[1].item()) / 64
460
+ it_fed = 6_000 if quick else 20_000
461
+ ops.mk_mma_fed(gbuf, sm * 2, 8, it_fed, sink)
462
+ ms = _median(_events(
463
+ lambda: ops.mk_mma_fed(gbuf, sm * 2, 8, it_fed, sink),
464
+ reps))
465
+ fed = sm * 2 * 8 * it_fed * 2.0 * 256 * fed_gain / \
466
+ (ms * 1e-3) / 1e12
467
+ d["mma_fed"] = _entry(
468
+ round(fed, 1), "TFLOP/s",
469
+ "m16n16k16 fp16 tiles fed from an L2-resident buffer "
470
+ "through cp.async double-buffered shared memory; "
471
+ "accumulator-verified", clk, "high",
472
+ "the achieved counterpart to the register peak: staging "
473
+ "exposed, one dependent accumulator chain per warp")
474
+ del gbuf
475
+
476
+ # ---- shared-memory bandwidth and the bank-conflict stride sweep
477
  it_sm = 30_000 if quick else 100_000
478
  ops.mk_smem_bw(blocks, 256, it_sm, sink)
479
  ms = _median(_events(lambda: ops.mk_smem_bw(blocks, 256, it_sm, sink),
 
483
  "block-resident read+write streaming (8 B per "
484
  "thread-iteration), aggregated across SMs", clk,
485
  "medium",
486
+ "bank-conflict-light access pattern; the stride "
487
+ "sweep below prices the conflicted case")
488
+ strides = (1, 8, 32) if quick else (1, 2, 4, 8, 16, 32)
489
+ it_st = 8_000 if quick else 20_000
490
+ st_ms = {}
491
+ for s2 in strides:
492
+ ops.mk_smem_stride(s2, blocks, 256, it_st, sink)
493
+ st_ms[s2] = _median(_events(
494
+ lambda s3=s2: ops.mk_smem_stride(s3, blocks, 256, it_st, sink),
495
+ reps))
496
+ d["smem_conflict_penalty"] = _entry(
497
+ {f"stride_{s2}": round(st_ms[s2] / st_ms[1], 2) for s2 in strides},
498
+ "x (time ratio vs stride 1)",
499
+ "dependent read-FMA-write per thread at warp-lane bank distance "
500
+ "`stride`; stride 32 puts every lane in one bank", clk, "high",
501
+ "the direct conflict measurement that disambiguates a "
502
+ "bank-conflict signature from an ILP stall")
503
 
504
  # ---- PCIe / host transfer bandwidth
505
  nbytes = 256 << 20
 
530
  "d2d counts one direction of traffic")
531
  del host_pin, host_pag, dev_t, dev_u
532
 
533
+ # ---- atomic contention curve
 
 
534
  n_thr = blocks * 256
535
  buf = torch.zeros(n_thr, dtype=torch.float32, device=device)
536
  at = {"curve_slots": {}}
 
540
  buf.zero_()
541
  ops.mk_atomics(1, blocks, 256, it_at, buf, slots)
542
  ms = _median(_events(
543
+ lambda s4=slots: ops.mk_atomics(1, blocks, 256, it_at, buf, s4),
544
  reps))
545
  at["curve_slots"][str(slots)] = round(
546
  n_thr * it_at / (ms * 1e-3) / 1e9, 2)
 
553
  "of distinct global addresses from 1 (full "
554
  "contention) to one per thread", clk, "high")
555
 
556
+ # ---- launch overhead: torch-dispatch path and the raw C++ path
557
  anchor = sink
558
  n_launch = 500 if quick else 2000
559
  _sync()
 
568
  ops.mk_empty(anchor)
569
  _sync()
570
  rt = (time.perf_counter() - t0) / n_rt * 1e6
571
+ raw_q = float(ops.mk_launch_raw(n_launch, 0, anchor))
572
+ raw_rt = float(ops.mk_launch_raw(n_rt, 1, anchor))
573
  d["launch_overhead"] = _entry(
574
+ {"torch_queued_us": round(queued, 2),
575
+ "torch_round_trip_us": round(rt, 2),
576
+ "raw_queued_us": round(raw_q / n_launch * 1e6, 2)
577
+ if raw_q >= 0 else None,
578
+ "raw_round_trip_us": round(raw_rt / n_rt * 1e6, 2)
579
+ if raw_rt >= 0 else None},
580
+ "us", "empty-kernel trains; torch path per-launch through op "
581
+ "dispatch, raw path looped inside one binding call", clk,
582
+ "medium", "host-state-dependent; the torch-minus-raw gap is the "
583
+ "dispatch cost a torch-launched candidate pays")
584
 
585
  # ---- grid barrier
586
  bar_iters = 500 if quick else 2000
 
596
  "unavailable or grid too large", clk,
597
  "high")
598
 
599
+ # ---- occupancy API table plus timed knees
 
 
600
  occ = {}
601
  for threads in (128, 256, 512, 1024):
602
  row = {}
 
617
  ops.mk_fma_dep(knee_blocks, threads, smem_kb * 1024,
618
  knee_iters, sink)
619
  ms = _median(_events(
620
+ lambda t=threads, s5=smem_kb: ops.mk_fma_dep(
621
+ knee_blocks, t, s5 * 1024, knee_iters, sink), reps))
622
  fl = knee_blocks * threads * knee_iters * 16 * 2
623
  row[f"{smem_kb}KB"] = round(fl / (ms * 1e-3) / 1e12, 2)
624
  knees[f"t{threads}"] = row
 
627
  "at forced (threads, dyn smem) occupancies over fixed total work; "
628
  "the cliff marks where occupancy stops covering latency",
629
  clk, "high", "timing-derived counterpart to the API table above")
630
+ if cached:
631
+ import json
632
+ json.dump(d, open(cpath, "w", encoding="utf-8"))
633
  return d
634
 
635
 
636
+ def probe_all_devices(quick=True, cached=True, tenancy_check=True):
637
+ """One dossier per visible device plus a pairwise P2P bandwidth matrix."""
638
+ n = torch.cuda.device_count()
639
+ devices = {}
640
+ for i in range(n):
641
+ devices[i] = probe_device(f"cuda:{i}", quick=quick, cached=cached,
642
+ tenancy_check=tenancy_check)
643
+ p2p = {}
644
+ if n > 1:
645
+ nbytes = 64 << 20
646
+ for i in range(n):
647
+ for j in range(n):
648
+ if i == j:
649
+ continue
650
+ src = torch.rand(nbytes // 4, device=f"cuda:{i}")
651
+ dst = torch.empty(nbytes // 4, device=f"cuda:{j}")
652
+ dst.copy_(src)
653
+ torch.cuda.synchronize(i)
654
+ torch.cuda.synchronize(j)
655
+ t0 = time.perf_counter()
656
+ for _ in range(5):
657
+ dst.copy_(src)
658
+ torch.cuda.synchronize(i)
659
+ torch.cuda.synchronize(j)
660
+ ms = (time.perf_counter() - t0) / 5 * 1e3
661
+ p2p[f"{i}->{j}"] = {
662
+ "gbs": round(nbytes / (ms * 1e-3) / 1e9, 1),
663
+ "peer_access": bool(
664
+ torch.cuda.can_device_access_peer(i, j)),
665
+ }
666
+ del src, dst
667
+ return {"devices": devices, "p2p": p2p}
668
+
669
+
670
+ _CAPTURE_PROBE = r"""
671
+ import pickle, sys, torch
672
+ fn = pickle.load(open(sys.argv[1], "rb"))
673
+ for _ in range(3):
674
+ fn()
675
+ torch.cuda.synchronize()
676
+ try:
677
+ g = torch.cuda.CUDAGraph()
678
+ with torch.cuda.graph(g):
679
+ fn()
680
+ print("capturable", flush=True)
681
+ except Exception as e:
682
+ print("uncapturable: %s: %s" % (type(e).__name__, str(e)[:120]),
683
+ flush=True)
684
+ """
685
+
686
+
687
+ def probe_capturable(fn, timeout_s=180):
688
+ """Test CUDA-graph capturability of fn in a child process, so a capture-wedged RNG never reaches the caller."""
689
+ import pickle
690
+ import subprocess
691
+ import sys as _sys
692
+ import tempfile
693
+ try:
694
+ blob = pickle.dumps(fn)
695
+ except Exception: # noqa: BLE001
696
+ return {"capturable": None,
697
+ "reason": "fn is not picklable; probe skipped (define fn "
698
+ "at module top level to enable it)"}
699
+ path = os.path.join(tempfile.mkdtemp(prefix="mk_cap_"), "fn.pkl")
700
+ with open(path, "wb") as f:
701
+ f.write(blob)
702
+ try:
703
+ r = subprocess.run([_sys.executable, "-c", _CAPTURE_PROBE, path],
704
+ capture_output=True, text=True,
705
+ timeout=timeout_s)
706
+ except Exception as e: # noqa: BLE001
707
+ return {"capturable": None,
708
+ "reason": f"probe child failed: {type(e).__name__}"}
709
+ out = (r.stdout or "").strip()
710
+ if out == "capturable":
711
+ return {"capturable": True, "reason": ""}
712
+ if out.startswith("uncapturable"):
713
+ return {"capturable": False, "reason": out}
714
+ return {"capturable": None,
715
+ "reason": "probe child exit %s: %s" % (
716
+ r.returncode, (r.stderr or "")[-200:])}
717
+
718
+
719
+ def _bench_verdict(rep):
720
+ parts = ["median %.4g ms" % rep["median_ms"]]
721
+ if "achieved_tflops" in rep:
722
+ parts.append("%.4g TFLOP/s" % rep["achieved_tflops"])
723
+ elif "achieved_gbs" in rep:
724
+ parts.append("%.4g GB/s" % rep["achieved_gbs"])
725
+ if "roofline" in rep:
726
+ parts.append("%.0f%% of %s roof" % (rep["roofline"]["pct_of_roof"],
727
+ rep["roofline"]["bound"]))
728
+ if rep["rejected_throttled"]:
729
+ parts.append("%d/%d rejected (%s)" % (
730
+ rep["rejected_throttled"], rep["samples"],
731
+ rep["rejection_basis"]))
732
+ if rep.get("no_clean_window"):
733
+ parts.append("no clean window: the whole run was throttled")
734
+ if rep.get("alloc_growth_bytes_per_call"):
735
+ parts.append("fn allocates %d B/call" %
736
+ rep["alloc_growth_bytes_per_call"])
737
+ if rep.get("host_overhead_suspected"):
738
+ parts.append("fn appears to sync or do host work inside")
739
+ return "; ".join(parts)
740
+
741
+
742
  def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
743
  dtype="fp16", clock_guard=0.97, clock_stride=8, graph=False,
744
+ graph_probe_isolated=False, device="cuda"):
745
+ """Median-of-N cudaEvent timing with throttle rejection, roofline placement, and footgun detection."""
746
+ for name, v in (("iters", iters), ("warmup", warmup)):
747
+ if not isinstance(v, int) or v < (1 if name == "iters" else 0):
748
+ raise ValueError(f"{name} must be a non-negative int; "
749
+ f"got {v!r}")
750
+ for name, v in (("bytes", bytes), ("flops", flops)):
751
+ if v is not None and (not isinstance(v, (int, float)) or v <= 0):
752
+ raise ValueError(f"{name} must be a positive number counting "
753
+ f"{name} per call of fn; got {v!r}")
 
754
  for _ in range(warmup):
755
  fn()
756
  _sync()
757
+ # footgun probes: per-call allocation growth and hidden host work
758
+ alloc0 = torch.cuda.memory_allocated(device)
759
+ fn()
760
+ fn()
761
+ _sync()
762
+ alloc_growth = max(0, (torch.cuda.memory_allocated(device) - alloc0)
763
+ // 2)
764
+ # a burst of enqueues that leaves the stream idle means fn drains it; only trustworthy above the launch-overhead floor
765
+ for _ in range(16):
766
+ fn()
767
+ stream_idle = bool(torch.cuda.current_stream(device).query())
768
+ _sync()
769
+ t0 = time.perf_counter()
770
+ pre = _events(fn, 4)
771
+ wall_ms = (time.perf_counter() - t0) * 1e3
772
+ ev_ms = sum(pre)
773
+ host_suspect = (stream_idle and ev_ms / 4 > 0.05) or \
774
+ wall_ms > ev_ms * 1.5 + 2.0
775
+
776
  graph_state = "off"
777
  graph_reason = ""
778
+ if graph and graph_probe_isolated:
779
+ cap = probe_capturable(fn)
780
+ if cap["capturable"] is False:
781
+ graph = False
782
+ graph_state = "fallback_eager"
783
+ graph_reason = "isolated probe: " + cap["reason"]
784
  if graph:
785
  pre_rng = torch.cuda.get_rng_state(device)
786
  try:
 
795
  except Exception as e: # noqa: BLE001 - fall back, say why
796
  graph_state = "fallback_eager"
797
  graph_reason = f"{type(e).__name__}: {str(e)[:120]}"
798
+ # drain the invalidated stream; a failed capture can wedge the device RNG on some torch versions
 
 
 
799
  rng_ok = True
800
  for _attempt in range(5):
801
  try:
 
825
  samples.append((_events(fn, n), len(clocks) - 1))
826
  clocks.append(measure_clock(device, ticks=probe_ticks))
827
  best_clk = max(clocks)
828
+
829
+ # rejection basis: memory-bound work is judged on its own rate, not the SM clock
830
+ ridge_memory = False
831
+ if dossier is not None and bytes and flops:
832
+ bw0 = max(dossier["dram_bw"]["value"].values()) * 1e9
833
+ mma_v0 = dossier["mma"]["value"].get(dtype)
834
+ if isinstance(mma_v0, dict):
835
+ mma_v0 = mma_v0.get("dense_equivalent")
836
+ peak0 = (mma_v0 if isinstance(mma_v0, (int, float))
837
+ else dossier["fma_f32"]["value"]) * 1e12
838
+ ridge_memory = (flops / bytes) * bw0 < peak0
839
+ basis = "bandwidth_consistency" if (
840
+ bytes is not None and (flops is None or ridge_memory)) \
841
+ else "sm_clock"
842
+
843
  flat = []
844
+ kept = []
845
+ rejected = 0
846
+ if basis == "sm_clock":
847
+ for group, ci in samples:
848
+ window_ok = (clocks[ci] >= clock_guard * best_clk and
849
+ clocks[min(ci + 1, len(clocks) - 1)] >=
850
+ clock_guard * best_clk)
851
+ for ms in group:
852
+ flat.append(ms)
853
+ if window_ok:
854
+ kept.append(ms)
855
+ else:
856
+ rejected += 1
857
+ else:
858
+ for group, _ci in samples:
859
+ flat.extend(group)
860
+ floor = sorted(flat)[max(0, int(0.05 * len(flat)) - 1)]
861
+ for ms in flat:
862
+ if ms <= floor * 1.15:
863
  kept.append(ms)
864
  else:
865
  rejected += 1
866
+ all_rejected = not kept
867
+ if all_rejected:
868
  kept = flat
869
  med = _median(kept)
870
  ks = sorted(kept)
871
  n_k = len(ks)
872
+ # order-statistic 95% CI on the median; degenerate at tiny n
 
873
  half = 1.959964 * (n_k ** 0.5) / 2.0
874
  lo_i = max(0, int(n_k / 2 - half) - 1)
875
  hi_i = min(n_k - 1, int(n_k / 2 + half))
 
884
  "mean_ms": round(sum(kept) / n_k, 4),
885
  "samples": len(flat),
886
  "rejected_throttled": rejected,
887
+ "rejection_basis": basis,
888
  "graph_captured": graph_state == "captured",
889
  "clock_ghz": {"best": round(best_clk, 3),
890
  "worst": round(min(clocks), 3)},
891
  }
892
+ no_clean = all_rejected
893
+ if basis == "sm_clock" and dossier is not None:
894
+ ref_clk = dossier["clock"]["max_ghz"]
895
+ if best_clk < clock_guard * ref_clk:
896
+ no_clean = True
897
+ if no_clean:
898
+ rep["no_clean_window"] = True
899
  if graph_reason:
900
  rep["graph_fallback_reason"] = graph_reason
901
+ if alloc_growth:
902
+ rep["alloc_growth_bytes_per_call"] = int(alloc_growth)
903
+ if host_suspect:
904
+ rep["host_overhead_suspected"] = True
905
+ rep["host_wall_over_event"] = round(wall_ms / max(ev_ms, 1e-9), 2)
906
  if bytes is not None:
907
  rep["achieved_gbs"] = round(bytes / (med * 1e-3) / 1e9, 1)
908
  if flops is not None:
 
912
  if dossier is not None and bytes and flops:
913
  bw = max(dossier["dram_bw"]["value"].values()) * 1e9
914
  mma_v = dossier["mma"]["value"].get(dtype)
915
+ if isinstance(mma_v, dict):
916
+ mma_v = mma_v.get("dense_equivalent")
917
  peak = (mma_v if isinstance(mma_v, (int, float))
918
  else dossier["fma_f32"]["value"]) * 1e12
919
  ai = flops / bytes
 
924
  "pct_of_roof": round(100 * (flops / (med * 1e-3)) / roof, 1),
925
  "peak_dtype": dtype,
926
  }
927
+ rep["verdict"] = _bench_verdict(rep)
928
  return rep
929
 
930
 
931
  def _to_ordered_int(t):
932
+ """Monotone integer mapping of float bit patterns; differences are ULP distances."""
 
 
933
  if t.dtype == torch.float32:
934
  u = t.view(torch.int32).to(torch.int64) & 0xFFFFFFFF
935
  half = 1 << 31
 
945
 
946
 
947
  def ulp_diff(a, b):
948
+ """Elementwise ULP distance; both-NaN counts 0, one-sided NaN 2**62, integers by absolute difference."""
949
+ if a.dtype != b.dtype or a.shape != b.shape:
950
+ raise ValueError("ulp_diff needs matching dtype and shape; got "
951
+ f"{a.dtype}{tuple(a.shape)} vs "
952
+ f"{b.dtype}{tuple(b.shape)}; cast or reshape the "
953
+ "candidate to the reference layout")
954
  if not a.is_floating_point():
955
  return (a.to(torch.int64) - b.to(torch.int64)).abs()
956
  ia, ib = _to_ordered_int(a), _to_ordered_int(b)
 
967
 
968
  @staticmethod
969
  def last_dim():
970
+ """Identical permutation of every arg's last dim: reduction order changes, value does not."""
 
 
971
  def shuffle(args, seed):
972
  g = torch.Generator(device=args[0].device).manual_seed(seed)
973
  p = torch.randperm(args[0].shape[-1], device=args[0].device,
 
977
 
978
  @staticmethod
979
  def matmul_k():
980
+ """Consistent K permutation of (A [.., M, K], B [.., K, N]): product unchanged, accumulation order not."""
 
981
  def shuffle(args, seed):
982
  a_m, b_m = args[0], args[1]
983
  g = torch.Generator(device=a_m.device).manual_seed(seed)
 
991
  return list(x) if isinstance(x, (tuple, list)) else [x]
992
 
993
 
994
+ def _compare_hints(trial):
995
+ hints = []
996
+ if trial.get("bias_one_sided_frac", 0) >= 0.9 and \
997
+ trial.get("frac_above_band", 0) > 0.5:
998
+ hints.append("systematic one-sided bias: check scale factors or "
999
+ "the accumulator dtype")
1000
+ if trial["nan_cand"] > trial["nan_ref"]:
1001
+ hints.append("candidate introduces NaN: check uninitialized "
1002
+ "memory or masking")
1003
+ nd = trial.get("first_divergent_nd")
1004
+ if nd and nd[-1] > 0 and any(nd[-1] % t == 0 for t in (16, 32, 64,
1005
+ 128)):
1006
+ hints.append("divergence starts on a tile-aligned index: check "
1007
+ "boundary indexing")
1008
+ return hints
1009
+
1010
+
1011
+ def compare(ref, cand, gen=None, trials=8, order_shuffle=None,
1012
+ order_trials=8, ref_variants=None, ulp_tol=None, atol=None,
1013
+ rtol=None, band_margin=4.0, zero_atol=None, worst_k=8, seed=0,
1014
+ inputs=None, frac_above_tol=0.5):
1015
+ """Adversarial correctness comparison with pointwise order bands, ULP evidence, and a one-sided-bias gate; the banded route cannot reject errors under band_margin*(band+2) ULP at order-stable elements."""
1016
+ if (gen is None) == (inputs is None):
1017
+ raise ValueError("pass exactly one of gen (a seed -> args "
1018
+ "callable) or inputs (a tuple of concrete "
1019
+ "tensors)")
1020
+ if inputs is not None:
1021
+ if isinstance(inputs, torch.Tensor):
1022
+ inputs = (inputs,)
1023
+ if not all(isinstance(x, torch.Tensor) for x in inputs):
1024
+ raise ValueError("inputs must be tensors; wrap scalars with "
1025
+ "torch.tensor first")
1026
+ fixed = tuple(inputs)
1027
+ gen = lambda seed: fixed # noqa: E731
1028
+ trials = 1
1029
  worst = {"max_ulp": -1}
1030
  total_hist = {}
1031
  per_trial = []
1032
  max_abs_seen = 0.0
1033
  max_rel_seen = 0.0
1034
+ out_shapes = []
1035
  for t in range(trials):
1036
  args = gen(seed + t)
1037
+ if not isinstance(args, (tuple, list)):
1038
+ raise ValueError("gen(seed) must return a tuple of args; got "
1039
+ f"{type(args).__name__}; return (x,) for a "
1040
+ "single-argument op")
1041
  r_outs = _as_outputs(ref(*args))
1042
  c_outs = _as_outputs(cand(*args))
1043
+ if len(r_outs) != len(c_outs):
1044
+ raise ValueError(f"reference returned {len(r_outs)} outputs, "
1045
+ f"candidate {len(c_outs)}; make the arities "
1046
+ "match")
1047
+ for oi, (r, c) in enumerate(zip(r_outs, c_outs)):
1048
+ if r.dtype != c.dtype:
1049
+ raise ValueError(f"output {oi} dtype mismatch: ref "
1050
+ f"{r.dtype}, cand {c.dtype}; cast the "
1051
+ "candidate to the reference dtype")
1052
+ if r.shape != c.shape:
1053
+ raise ValueError(f"output {oi} shape mismatch: ref "
1054
+ f"{tuple(r.shape)}, cand "
1055
+ f"{tuple(c.shape)}")
1056
+ out_shapes = [tuple(r.shape) for r in r_outs]
1057
+ # pointwise band: each element judged against its own measured order sensitivity
1058
  bands = None
1059
  band = 0
1060
  band_score = 0.0
 
1078
  max_ulp = 0
1079
  mean_ulp = 0.0
1080
  first_div = -1
1081
+ first_div_nd = None
1082
  n_el = 0
1083
  n_above_band = 0
1084
+ n_diverging = 0
1085
+ n_cand_high = 0
1086
  nan_r = nan_c = inf_r = inf_c = 0
1087
  worst_elements = []
1088
  for oi, (r, c) in enumerate(zip(r_outs, c_outs)):
 
1100
  mean_ulp += float(d.double().sum().item())
1101
  n_el += d.numel()
1102
  nz = (d > 0)
1103
+ n_diverging += int(nz.sum().item())
1104
+ n_cand_high += int(((c > r) & nz).sum().item())
1105
  if first_div < 0 and nz.any():
1106
  first_div = int(nz.flatten().nonzero()[0].item())
1107
+ first_div_nd = _unravel(first_div, r.shape)
1108
  if nz.any():
1109
  buckets = torch.clamp(
1110
  torch.log2(d[nz].double()).floor(), 0, 62
 
1121
  if val > 0:
1122
  worst_elements.append({
1123
  "output": oi, "index": int(ix),
1124
+ "index_nd": _unravel(int(ix), r.shape),
1125
  "ref": float(rf[ix].item()),
1126
  "cand": float(cf[ix].item()),
1127
  "ulp": int(val)})
 
1141
  inf_r += int(torch.isinf(r).sum().item())
1142
  inf_c += int(torch.isinf(c).sum().item())
1143
  worst_elements.sort(key=lambda w: -w["ulp"])
1144
+ frac_pos = n_cand_high / max(n_diverging, 1)
1145
  trial_rep = {
1146
  "max_ulp": max_ulp,
1147
  "mean_ulp": round(mean_ulp / max(n_el, 1), 3),
1148
  "first_divergent_index": first_div,
1149
+ "first_divergent_nd": first_div_nd,
1150
  "nan_ref": nan_r, "nan_cand": nan_c,
1151
  "inf_ref": inf_r, "inf_cand": inf_c,
1152
  "order_band_ulp": band,
1153
  "band_score": round(band_score, 2),
1154
  "frac_above_band": round(n_above_band / max(n_el, 1), 6),
1155
+ "bias_one_sided_frac": round(max(frac_pos, 1 - frac_pos), 4)
1156
+ if n_diverging else 0.0,
1157
  "worst_elements": worst_elements[:worst_k],
1158
  }
1159
  per_trial.append(trial_rep)
1160
  if max_ulp > worst["max_ulp"]:
1161
  worst = trial_rep
 
1162
  max_seen = max(t["max_ulp"] for t in per_trial)
1163
+ banded = order_shuffle is not None or ref_variants
1164
+ if banded:
1165
  score = max(t["band_score"] for t in per_trial)
1166
  ulp_ok = score <= band_margin
1167
  criterion = (f"pointwise max(ulp / (order_band + 2)) = "
 
1176
  max_rel_seen <= (rtol or 0) or max_abs_seen <= (atol or 0)
1177
  criterion += (f"; OR max_abs {max_abs_seen:.3e} <= atol {atol} / "
1178
  f"max_rel {max_rel_seen:.3e} <= rtol {rtol}")
1179
+ # one-sided bias gate: a uniform small error is above-band nearly everywhere in one direction
1180
+ bias_fail = False
1181
+ if banded and (ulp_ok or tol_ok):
1182
+ w_frac = max(t["frac_above_band"] for t in per_trial)
1183
+ w_side = max(t["bias_one_sided_frac"] for t in per_trial)
1184
+ if w_frac > frac_above_tol and w_side >= 0.9:
1185
+ bias_fail = True
1186
+ criterion += ("; FAILED bias gate: frac_above_band %.3f > %s "
1187
+ "with one-sided fraction %.2f" %
1188
+ (w_frac, frac_above_tol, w_side))
1189
+ passed = bool((ulp_ok or tol_ok) and not bias_fail)
1190
+ hints = [] if passed else _compare_hints(worst)
1191
+ if passed:
1192
+ verdict = "pass: " + (
1193
+ f"max {max_seen} ULP within the order band" if banded
1194
+ else f"max {max_seen} ULP")
1195
+ elif bias_fail:
1196
+ verdict = ("fail: uniform bias, %.0f%% of elements above their "
1197
+ "order band in one direction" %
1198
+ (100 * max(t["frac_above_band"] for t in per_trial)))
1199
+ else:
1200
+ w0 = worst["worst_elements"][0] if worst["worst_elements"] else None
1201
+ verdict = "fail: max %d ULP" % max_seen + (
1202
+ " at index %s (ref %.6g, cand %.6g)" %
1203
+ (w0["index_nd"], w0["ref"], w0["cand"]) if w0 else "")
1204
+ rep = {
1205
+ "pass": passed,
1206
+ "verdict": verdict,
1207
  "criterion": criterion,
1208
  "worst_trial": worst,
1209
  "max_abs": max_abs_seen,
 
1211
  "trials": per_trial,
1212
  "ulp_hist_log2": dict(sorted(total_hist.items())),
1213
  }
1214
+ if hints:
1215
+ rep["hints"] = hints
1216
+ return rep
1217
 
1218
 
1219
  _FUZZ_VARIANTS = ("contig", "transposed", "strided_rows", "offset_slice",
 
1229
  return False
1230
 
1231
 
1232
+ class _RandnMaker:
1233
+ """Picklable default maker: `arity` same-shape randn tensors (zeros for empty shapes)."""
1234
+
1235
+ def __init__(self, arity=1):
1236
+ self.arity = arity
1237
+
1238
+ def __call__(self, shape, dtype=torch.float32):
1239
+ n = math.prod(shape)
1240
+ if n:
1241
+ out = tuple(torch.randn(tuple(shape), device="cuda",
1242
+ dtype=dtype)
1243
+ for _ in range(self.arity))
1244
+ else:
1245
+ out = tuple(torch.zeros(tuple(shape), device="cuda",
1246
+ dtype=dtype)
1247
+ for _ in range(self.arity))
1248
+ return out if self.arity > 1 else out[0]
1249
+
1250
+
1251
+ def _default_make(arity=1):
1252
+ """Default maker instance; a class so isolate=True can transport it."""
1253
+ return _RandnMaker(arity)
1254
+
1255
+
1256
  def _fuzz_build(make, shape, dtype):
1257
  try:
1258
  out = make(tuple(shape), dtype)
 
1269
  if v == "strided_rows":
1270
  return x[::2] if x.dim() >= 1 and x.shape[0] >= 2 else x
1271
  if v == "offset_slice":
1272
+ # storage-offset-1 view: misaligned for vectorized loads
1273
  return x.flatten()[1:] if x.numel() > 1 else x
1274
  if v == "overflow_expand":
1275
  if x.numel() == 0:
 
1283
 
1284
  def _fuzz_group(op, ref, make, shape, dtype, variants, ulp_tol, device,
1285
  stop_on_context_loss=True):
1286
+ """All variant cases for one (shape, dtype) group; returns (rows, context_lost)."""
 
1287
  rows = []
1288
  bases = _fuzz_build(make, shape, dtype)
1289
  lost = False
 
1320
  got = op(*xs)
1321
  want = ref(*xs)
1322
  if v == "overflow_expand":
1323
+ # bounded comparison: head plus the high-index tail where int32 indexing wraps
 
1324
  w = 1 << 20
1325
  gf, wf = got.flatten(), want.flatten()
1326
  pairs = ((gf[:w], wf[:w]), (gf[-w:], wf[-w:]))
 
1345
  return rows, lost
1346
 
1347
 
1348
+ def _fn_spec(fn, name):
1349
+ """Picklable transport spec for a callable, with a source fallback for local defs."""
1350
+ import pickle
1351
+ try:
1352
+ return ("pickle", pickle.dumps(fn), None)
1353
+ except Exception: # noqa: BLE001
1354
+ import inspect
1355
+ import textwrap
1356
+ try:
1357
+ src = textwrap.dedent(inspect.getsource(fn))
1358
+ except (OSError, TypeError):
1359
+ src = ""
1360
+ if not src.startswith("def "):
1361
+ raise ValueError(
1362
+ f"{name} is not picklable and not a plain def; define "
1363
+ "op/ref/make as top-level functions (not lambdas or "
1364
+ "closures) to use isolate=True") from None
1365
+ return ("source", src, fn.__name__)
1366
+
1367
+
1368
+ _POOL_BOOTSTRAP = r"""
1369
+ import json, os, pickle, sys
1370
  meta = json.load(open(sys.argv[1], "r"))
1371
+ sys.path[:0] = meta["sys_path"]
1372
  try:
1373
  import kernels
1374
  _mk = kernels.get_kernel(meta["repo"], version=meta["version"],
 
1381
  import load_local
1382
  _mk = load_local.load()
1383
  import torch
1384
+ _ns = {"torch": torch}
1385
+ def _resolve(spec):
1386
+ kind, payload, name = spec
1387
+ if kind == "pickle":
1388
+ return pickle.loads(payload)
1389
+ exec(payload, _ns)
1390
+ return _ns[name]
1391
+ _op, _ref, _make = (_resolve(s) for s in
1392
+ pickle.load(open(meta["fns"], "rb")))
1393
+ print("ready", flush=True)
1394
+ for line in sys.stdin:
1395
+ job = json.loads(line)
1396
+ rows, _l = _mk._fuzz_group(
1397
+ _op, _ref, _make, tuple(job["shape"]),
1398
+ getattr(torch, job["dtype"]), tuple(job["variants"]),
1399
+ job["ulp_tol"], job["device"], stop_on_context_loss=False)
1400
+ pickle.dump(rows, open(job["out"], "wb"))
1401
+ print("done", flush=True)
1402
  """
1403
 
1404
 
1405
+ class _FuzzPool:
1406
+ """One persistent isolation worker fed groups over stdin, respawned only after a crash."""
1407
+
1408
+ def __init__(self, meta_path, stderr_path, timeout_s):
1409
+ self.meta_path = meta_path
1410
+ self.stderr_path = stderr_path
1411
+ self.timeout_s = timeout_s
1412
+ self.proc = None
1413
+ self.q = None
1414
+
1415
+ def _spawn(self):
1416
+ import queue
1417
+ import subprocess
1418
+ import sys as _sys
1419
+ import threading
1420
+ self.errf = open(self.stderr_path, "ab")
1421
+ self.proc = subprocess.Popen(
1422
+ [_sys.executable, "-c", _POOL_BOOTSTRAP, self.meta_path],
1423
+ stdin=subprocess.PIPE, stdout=subprocess.PIPE,
1424
+ stderr=self.errf, text=True)
1425
+ self.q = queue.Queue()
1426
+
1427
+ def reader(p, q):
1428
+ for line in p.stdout:
1429
+ q.put(line.strip())
1430
+ threading.Thread(target=reader, args=(self.proc, self.q),
1431
+ daemon=True).start()
1432
+ try:
1433
+ first = self.q.get(timeout=self.timeout_s)
1434
+ except Exception: # noqa: BLE001
1435
+ first = None
1436
+ return first == "ready"
1437
+
1438
+ def run(self, job_line):
1439
+ """Returns True when the worker reports done, False on crash or timeout."""
1440
+ import queue
1441
+ if self.proc is None or self.proc.poll() is not None:
1442
+ if not self._spawn():
1443
+ return False
1444
+ try:
1445
+ self.proc.stdin.write(job_line + "\n")
1446
+ self.proc.stdin.flush()
1447
+ except OSError:
1448
+ return False
1449
+ try:
1450
+ return self.q.get(timeout=self.timeout_s) == "done"
1451
+ except queue.Empty:
1452
+ if self.proc.poll() is None:
1453
+ self.proc.kill()
1454
+ return False
1455
+
1456
+ def stderr_tail(self):
1457
+ try:
1458
+ self.errf.flush()
1459
+ with open(self.stderr_path, "rb") as f:
1460
+ return f.read()[-400:].decode(errors="replace")
1461
+ except OSError:
1462
+ return ""
1463
+
1464
+ def close(self):
1465
+ if self.proc is not None and self.proc.poll() is None:
1466
+ self.proc.kill()
1467
+ try:
1468
+ self.errf.close()
1469
+ except Exception: # noqa: BLE001
1470
+ pass
1471
+
1472
+
1473
+ def _fuzz_hints(rows):
1474
+ hints = []
1475
+ bad = [c for c in rows if c["status"] == "mismatch"]
1476
+ if bad and all(c["variant"] in ("transposed", "strided_rows",
1477
+ "offset_slice") for c in bad):
1478
+ hints.append("mismatches only on non-contiguous variants: the op "
1479
+ "assumes contiguity; call .contiguous() or honor "
1480
+ "strides")
1481
+ if any(c["variant"] == "overflow_expand" and
1482
+ c["status"] == "mismatch" for c in rows):
1483
+ hints.append("overflow_expand mismatch: int32 indexing wraps past "
1484
+ "2^31 elements; use 64-bit index arithmetic")
1485
+ return hints
1486
+
1487
+
1488
+ def fuzz_shapes(op, ref, make=None, shapes=None, variants=_FUZZ_VARIANTS,
1489
  dtypes=(torch.float32,), ulp_tol=64, isolate=False,
1490
+ isolate_timeout_s=180, device="cuda", arity=1):
1491
+ """Edge shapes, dtypes, and layout mutations with per-case repro recipes; isolate=True survives context-killing candidates via a pooled worker."""
 
 
 
 
 
 
 
 
 
 
 
 
1492
  if shapes is None:
1493
  shapes = [(), (7,), (0, 8), (1, 1), (1, 17), (3, 63), (17, 129),
1494
  (128, 128), (1023, 5), (2, 4096), (2, 3, 5)]
1495
+ if make is None:
1496
+ make = _default_make(arity)
1497
 
1498
  rows = []
1499
  lost = False
1500
  if isolate:
1501
  import json
1502
  import pickle
 
1503
  import sys as _sys
1504
  import tempfile
1505
  tmpdir = tempfile.mkdtemp(prefix="mk_fuzz_")
1506
  fns = os.path.join(tmpdir, "fns.pkl")
1507
+ specs = [_fn_spec(f, n) for f, n in ((op, "op"), (ref, "ref"),
1508
+ (make, "make"))]
1509
  with open(fns, "wb") as f:
1510
+ pickle.dump(specs, f)
1511
+ meta_path = os.path.join(tmpdir, "meta.json")
1512
+ with open(meta_path, "w", encoding="utf-8") as f:
1513
+ json.dump({"sys_path": [p for p in _sys.path
1514
+ if isinstance(p, str)],
1515
+ "repo": _REPO_ID, "version": _REPO_VERSION,
1516
+ "fns": fns}, f)
1517
+ pool = _FuzzPool(meta_path, os.path.join(tmpdir, "stderr.log"),
1518
+ isolate_timeout_s)
1519
+ try:
1520
+ for dtype in dtypes:
1521
+ for shape in shapes:
1522
+ out = os.path.join(tmpdir, "rows.pkl")
1523
+ if os.path.exists(out):
1524
+ os.remove(out)
1525
+ job = json.dumps({
1526
  "shape": list(shape),
1527
  "dtype": str(dtype).replace("torch.", ""),
1528
  "variants": list(variants),
1529
  "ulp_tol": ulp_tol, "device": device,
1530
+ "out": out})
1531
+ ok = pool.run(job)
1532
+ if ok and os.path.exists(out):
 
 
 
 
 
 
1533
  with open(out, "rb") as f:
1534
+ rows.extend(pickle.load(f))
1535
+ else:
1536
+ rows.append({
1537
+ "shape": tuple(shape), "variant": "*",
1538
+ "dtype": str(dtype).replace("torch.", ""),
1539
+ "status": "crashed_isolated",
1540
+ "stderr_tail": pool.stderr_tail()})
1541
+ finally:
1542
+ pool.close()
 
 
 
 
1543
  else:
1544
  pending = [(dt, sh) for dt in dtypes for sh in shapes]
1545
  for gi, (dtype, shape) in enumerate(pending):
 
1556
  n_bad = sum(1 for r in rows
1557
  if r["status"] not in ("ok", "skipped_vram",
1558
  "skipped_empty"))
1559
+ rep = {"cases": rows, "failed": n_bad, "total": len(rows),
1560
+ "context_lost": lost}
1561
+ hints = _fuzz_hints(rows)
1562
+ if hints:
1563
+ rep["hints"] = hints
1564
+ if n_bad == 0:
1565
+ rep["verdict"] = "pass: %d/%d cases ok" % (len(rows) - n_bad,
1566
+ len(rows))
1567
+ else:
1568
+ worst_case = next(c for c in rows
1569
+ if c["status"] not in ("ok", "skipped_vram",
1570
+ "skipped_empty"))
1571
+ rep["verdict"] = "fail: %d/%d cases (%s at %s %s)" % (
1572
+ n_bad, len(rows), worst_case["status"], worst_case["shape"],
1573
+ worst_case["variant"])
1574
+ return rep
1575
 
1576
 
1577
  def sweep(factory, grid, iters=30, warmup=5, bytes=None, flops=None,
1578
  check=None, budget_s=None, race=False):
1579
+ """Launch-configuration search, fastest first, with a correctness gate per row and no silently dropped points."""
 
 
 
 
 
 
 
 
1580
  keys = list(grid.keys())
1581
 
1582
  def expand(i, acc):
 
1641
  continue
1642
  rows.append(run_point(p, iters))
1643
  rows.sort(key=lambda r: (not r.get("check", True), r["median_ms"]))
1644
+ if rows:
1645
+ n_pruned = sum(1 for r in rows if r.get("status") == "pruned_race")
1646
+ n_skip = sum(1 for r in rows if r.get("status") == "not_run")
1647
+ best = rows[0]
1648
+ rows_verdict = "fastest %s at %.4g ms" % (
1649
+ {k: best[k] for k in keys}, best["median_ms"])
1650
+ if n_pruned:
1651
+ rows_verdict += "; %d pruned" % n_pruned
1652
+ if n_skip:
1653
+ rows_verdict += "; %d not run" % n_skip
1654
+ best["verdict"] = rows_verdict
1655
  return rows
1656
 
1657
 
1658
+ def judge(cand, ref, inputs=None, gen=None, bytes=None, flops=None,
1659
+ dtype=None, order_shuffle=None, make=None, quick_fuzz=True,
1660
+ device="cuda"):
1661
+ """One-call verdict: compare, a quick fuzz, and a roofline bench against the cached dossier."""
1662
+ if (gen is None) == (inputs is None):
1663
+ raise ValueError("pass exactly one of inputs (concrete tensors) "
1664
+ "or gen (seed -> args)")
1665
+ if inputs is not None and isinstance(inputs, torch.Tensor):
1666
+ inputs = (inputs,)
1667
+ args0 = tuple(inputs) if inputs is not None else gen(0)
1668
+ arity = len(args0)
1669
+ if dtype is None:
1670
+ dt0 = args0[0].dtype
1671
+ dtype = {torch.float16: "fp16", torch.bfloat16: "bf16"}.get(
1672
+ dt0, "f32")
1673
+ dossier = probe_device(device, quick=True, cached=True,
1674
+ tenancy_check=False)
1675
+ cmp_rep = compare(ref, cand, gen=gen, inputs=inputs, trials=4,
1676
+ order_shuffle=order_shuffle)
1677
+ fuzz_rep = None
1678
+ if quick_fuzz:
1679
+ fuzz_rep = fuzz_shapes(
1680
+ cand, ref, make=make, arity=arity,
1681
+ shapes=[(1, 17), (3, 63), (128, 128)],
1682
+ variants=("contig", "transposed", "offset_slice"),
1683
+ dtypes=(args0[0].dtype,))
1684
+ bench_rep = None
1685
+ if inputs is not None:
1686
+ bench_rep = bench(lambda: cand(*inputs), iters=32, warmup=6,
1687
+ bytes=bytes, flops=flops, dossier=dossier,
1688
+ dtype=dtype, device=device)
1689
+ passed = cmp_rep["pass"] and (fuzz_rep is None or
1690
+ fuzz_rep["failed"] == 0)
1691
+ parts = [cmp_rep["verdict"]]
1692
+ if fuzz_rep is not None:
1693
+ parts.append(fuzz_rep["verdict"])
1694
+ if bench_rep is not None:
1695
+ parts.append(bench_rep["verdict"])
1696
+ rep = {"pass": passed, "verdict" : " | ".join(parts),
1697
+ "compare": cmp_rep, "fuzz": fuzz_rep, "bench": bench_rep,
1698
+ "device": dossier["device"]["name"]}
1699
+ hints = list(cmp_rep.get("hints", []))
1700
+ if fuzz_rep is not None:
1701
+ hints += fuzz_rep.get("hints", [])
1702
+ if hints:
1703
+ rep["hints"] = hints
1704
+ return rep
1705
+
1706
+
1707
  def alloc_stamps(n, blocks=None, device="cuda"):
1708
+ """Device buffer for MK_STAMP (1-D) or MK_STAMP_BLOCK ([blocks, n]) stamps."""
 
 
1709
  if blocks is not None:
1710
  return torch.zeros(blocks, n, dtype=torch.int64, device=device)
1711
  return torch.zeros(n, dtype=torch.int64, device=device)
 
1715
 
1716
 
1717
  def pinned_dossiers():
1718
+ """Names of the release-pinned dossiers, one per certified architecture."""
 
1719
  return sorted(_PINNED.keys())
1720
 
1721
 
 
1734
 
1735
 
1736
  def dossier_drift(current, pinned=None, rel_tol=0.35, skip=_DRIFT_SKIP):
1737
+ """Per-leaf relative drift of a fresh dossier against the release pin for the same architecture."""
 
 
 
 
1738
  if pinned is None:
1739
  arch = current["device"]["sm_arch"]
1740
  matches = [k for k, v in _PINNED.items()
 
1757
  "rel": round(rel, 4)})
1758
  worst = max((e["rel"] for e in entries), default=0.0)
1759
  over = [e for e in entries if e["rel"] > rel_tol]
1760
+ rep = {"entries": entries, "worst_rel": worst, "over_tol": over,
1761
+ "rel_tol": rel_tol, "compared": len(entries),
1762
+ "pass": len(over) == 0}
1763
+ rep["verdict"] = ("pass: worst drift %.3f over %d entries" %
1764
+ (worst, len(entries))) if rep["pass"] else \
1765
+ ("fail: %d entries over %.2f, worst %s at %.3f" %
1766
+ (len(over), rel_tol, over[0]["path"], worst))
1767
+ return rep
1768
 
1769
 
1770
  def stamped_demo(n=1 << 22, fma_iters=4000, blocks=None, threads=256,
1771
  device="cuda"):
1772
+ """Run the shipped MK_STAMP_BLOCK example (load, compute, drained store) and decode its stamps."""
 
 
1773
  props = torch.cuda.get_device_properties(device)
1774
  if blocks is None:
1775
  blocks = props.multi_processor_count
 
1782
 
1783
 
1784
  def read_stamps(stamps, labels=None, clock_ghz=None):
1785
+ """Decode MK_STAMP deltas to ticks and microseconds; 2-D buffers yield min/median/max across blocks."""
 
 
 
 
1786
  if clock_ghz is None:
1787
  clock_ghz = measure_clock(stamps.device)
1788
 
 
1818
  return {"phases": phases, "clock_ghz": round(clock_ghz, 3),
1819
  "blocks_reporting": int(live.sum().item()),
1820
  "blocks_total": int(live.numel())}
1821
+
1822
+
1823
+ _SCHEMAS = {
1824
+ "probe_device": {
1825
+ "device": "name, sm_arch, sms, l2_bytes, vram_bytes, torch, "
1826
+ "cuda_runtime, ecc, driver, power_limit_w",
1827
+ "clock": "max/min/final GHz under sustained FMA load plus sag_pct",
1828
+ "dram_bw": "GB/s: triad_4B/8B/16B, read_only, write_only, "
1829
+ "random_gather_16B",
1830
+ "l2_bw": "GB/s over an L2-resident working set",
1831
+ "latency": "cycles: smem, l1, l2, dram (min of 3 chases) plus "
1832
+ "ns_per_cycle",
1833
+ "fma_f32 / fma_f64": "TFLOP/s of the register-resident pipes",
1834
+ "fma_ilp": "TFLOP/s at 1/2/4 chains plus ratio_4_1; near 4 means "
1835
+ "latency-bound",
1836
+ "mma": "TFLOP/s per dtype; sparse carries executed and "
1837
+ "dense_equivalent; verified_k_lanes carries "
1838
+ "executed_k_per_iter, vs_nominal, calibration_linear; "
1839
+ "fp8_accum carries the accumulate-precision stall",
1840
+ "mma_fed": "TFLOP/s of smem-staged fp16 tiles (staging exposed)",
1841
+ "smem_bw": "GB/s aggregate, conflict-light pattern",
1842
+ "smem_conflict_penalty": "time ratio vs stride 1 per lane stride",
1843
+ "transfer_bw": "GB/s: h2d/d2h pinned and pageable, d2d",
1844
+ "atomics_f32": "Gop/s curve over distinct addresses plus "
1845
+ "shared_same_addr",
1846
+ "launch_overhead": "us: torch_queued, torch_round_trip, "
1847
+ "raw_queued, raw_round_trip",
1848
+ "grid_barrier": "us per grid.sync or 'unsupported'",
1849
+ "occupancy_blocks_per_sm": "API table by (threads, dyn smem)",
1850
+ "occupancy_knees_fma_tflops": "timed dependent-chain table",
1851
+ },
1852
+ "bench": {
1853
+ "median_ms": "median over accepted samples",
1854
+ "sustained_ms": "median over all samples",
1855
+ "median_ci95_ms / p05_ms / p95_ms / min_ms / mean_ms":
1856
+ "distribution of accepted samples",
1857
+ "samples / rejected_throttled / rejection_basis":
1858
+ "counts and whether rejection used sm_clock or "
1859
+ "bandwidth_consistency",
1860
+ "no_clean_window": "present when every window ran throttled; the "
1861
+ "median is then a throttled-regime number",
1862
+ "clock_ghz": "best and worst SM clock probes around the run",
1863
+ "achieved_gbs / achieved_tflops / arithmetic_intensity":
1864
+ "rates from declared traffic",
1865
+ "roofline": "bound, ridge_flops_per_byte, pct_of_roof, peak_dtype",
1866
+ "graph_captured / graph_fallback_reason": "CUDA-graph state",
1867
+ "alloc_growth_bytes_per_call": "present when fn leaks allocations",
1868
+ "host_overhead_suspected": "present when fn syncs or does host "
1869
+ "work inside; ops under ~50 us are "
1870
+ "below the detection floor",
1871
+ "verdict": "one-sentence summary",
1872
+ },
1873
+ "compare": {
1874
+ "pass": "final verdict incl. the one-sided bias gate",
1875
+ "verdict / criterion / hints": "summary, acceptance rule, and "
1876
+ "failure-pattern suggestions",
1877
+ "worst_trial": "max_ulp, mean_ulp, first_divergent_index(+_nd), "
1878
+ "NaN/Inf census, order_band_ulp, band_score, "
1879
+ "frac_above_band, bias_one_sided_frac, "
1880
+ "worst_elements (index, index_nd, ref, cand, ulp)",
1881
+ "max_abs / max_rel / ulp_hist_log2": "aggregate evidence",
1882
+ "floor": "the banded route cannot reject errors under "
1883
+ "band_margin*(band+2) ULP at order-stable elements",
1884
+ },
1885
+ "fuzz_shapes": {
1886
+ "cases": "per-case shape, variant, dtype, status, repro recipe",
1887
+ "status values": "ok | mismatch | exception | context_killed | "
1888
+ "context_lost | crashed_isolated | skipped_vram "
1889
+ "| skipped_empty",
1890
+ "failed / total / context_lost / verdict / hints": "aggregates",
1891
+ },
1892
+ "sweep": {
1893
+ "rows": "grid point plus median_ms, rejected_throttled, check?, "
1894
+ "achieved rates, status in pruned_race | not_run",
1895
+ "verdict": "on the fastest row",
1896
+ },
1897
+ "dossier_drift": {
1898
+ "entries": "path, current, pinned, rel per intersecting leaf",
1899
+ "worst_rel / over_tol / compared / pass / verdict": "aggregates",
1900
+ },
1901
+ "judge": {
1902
+ "pass / verdict / hints": "composite over compare, fuzz, bench",
1903
+ "compare / fuzz / bench": "the full sub-reports",
1904
+ },
1905
+ "read_stamps": {
1906
+ "phases": "ticks and us per phase; 2-D input adds min/median/max "
1907
+ "across blocks",
1908
+ "note": "stamps record issue; DRAIN variants include the store "
1909
+ "drain to the coherence point",
1910
+ },
1911
+ }
1912
+
1913
+
1914
+ def schema(name=None):
1915
+ """Field layout of every report, so an agent can look up what a number means."""
1916
+ if name is None:
1917
+ return _SCHEMAS
1918
+ if name not in _SCHEMAS:
1919
+ raise KeyError(f"no schema for {name!r}; have "
1920
+ f"{sorted(_SCHEMAS)}")
1921
+ return _SCHEMAS[name]
build/torch213-cxx11-cu130-x86_64-linux/_metakernel_cuda_0b9de42.abi3.so ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:8120f29036953f429c2640dbe6dcb057d44ff3ac89944c849d07f5abe6616c8d
3
+ size 1426104
build/torch213-cxx11-cu130-x86_64-linux/_metakernel_cuda_b62a1ce.abi3.so DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:88438dc3c8221430094a0874eb102dc4eadcdfda40ba1c176ba9ff9ab3c21fef
3
- size 1179960
 
 
 
 
build/torch213-cxx11-cu130-x86_64-linux/_ops.py CHANGED
@@ -1,9 +1,9 @@
1
  import torch
2
- from . import _metakernel_cuda_b62a1ce
3
- ops = torch.ops._metakernel_cuda_b62a1ce
4
 
5
  def add_op_namespace_prefix(op_name: str):
6
  """
7
  Prefix op by namespace.
8
  """
9
- return f"_metakernel_cuda_b62a1ce::{op_name}"
 
1
  import torch
2
+ from . import _metakernel_cuda_0b9de42
3
+ ops = torch.ops._metakernel_cuda_0b9de42
4
 
5
  def add_op_namespace_prefix(op_name: str):
6
  """
7
  Prefix op by namespace.
8
  """
9
+ return f"_metakernel_cuda_0b9de42::{op_name}"
build/torch213-cxx11-cu130-x86_64-linux/_pinned.py CHANGED
@@ -1,8 +1,4 @@
1
- """Release-pinned device dossiers, one per certified architecture.
2
-
3
- Generated by dev/pin_dossier.py from full probe_device() runs on idle,
4
- single-tenant cards; consumed by metakernel.dossier_drift.
5
- """
6
 
7
  PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
8
  'confidence': 'high',
@@ -295,7 +291,7 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
295
  'd2h_pinned': 10.8,
296
  'h2d_pageable': 10.3,
297
  'h2d_pinned': 11.8}}},
298
- 'sm89_nvidiartx6000adagenerati': {'atomics_f32': {'clock_ghz': 2.706,
299
  'confidence': 'high',
300
  'method': 'atomicAdd '
301
  'hammer; curve '
@@ -308,21 +304,21 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
308
  'contention) to '
309
  'one per thread',
310
  'units': 'Gop/s',
311
- 'value': {'curve_slots': {'1': 0.72,
312
- '145408': 483.54,
313
- '4096': 209.84,
314
- '512': 55.6,
315
- '64': 13.82,
316
- '8': 5.77},
317
- 'shared_same_addr': 12.88}},
318
  'clock': {'confidence': 'high',
319
- 'final_ghz': 2.699,
320
- 'max_ghz': 2.706,
321
  'method': '24 back-to-back FMA '
322
  'spin windows of 30M '
323
  'ticks; wall/ticks '
324
  'per window',
325
- 'min_ghz': 2.501,
326
  'sag_pct': 0.3,
327
  'units': 'GHz'},
328
  'device': {'cuda_runtime': '12.6',
@@ -336,7 +332,7 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
336
  'sms': 142,
337
  'torch': '2.10.0+cu126',
338
  'vram_bytes': 48305274880},
339
- 'dram_bw': {'clock_ghz': 2.706,
340
  'confidence': 'high',
341
  'method': 'streaming triad '
342
  'c=a+s*b per access '
@@ -356,26 +352,41 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
356
  'line-efficiency '
357
  'claim',
358
  'units': 'GB/s',
359
- 'value': {'random_gather_16B': 218.5,
360
- 'read_only': 771.0,
361
- 'triad_16B': 663.1,
362
- 'triad_4B': 638.3,
363
- 'triad_8B': 705.3,
364
  'write_only': 766.5}},
365
- 'fma_f32': {'clock_ghz': 2.706,
366
  'confidence': 'high',
367
  'method': 'register-resident '
368
  'FMA chains',
369
  'units': 'TFLOP/s',
370
- 'value': 90.85},
371
- 'fma_f64': {'clock_ghz': 2.706,
372
  'confidence': 'high',
373
  'method': 'register-resident '
374
  'double FMA chains '
375
  '(the DFMA pipe)',
376
  'units': 'TFLOP/s',
377
- 'value': 1.282},
378
- 'grid_barrier': {'clock_ghz': 2.706,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
379
  'confidence': 'high',
380
  'method': 'cooperative '
381
  'kernel, 142 '
@@ -383,8 +394,8 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
383
  'threads',
384
  'units': 'us per '
385
  'grid.sync',
386
- 'value': 0.54},
387
- 'l2_bw': {'clock_ghz': 2.706,
388
  'confidence': 'medium',
389
  'method': 'in-kernel repeated '
390
  'triad passes over 72 '
@@ -394,14 +405,15 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
394
  'from HBM; amortized '
395
  'over 64 passes',
396
  'units': 'GB/s',
397
- 'value': 9325.3},
398
- 'latency': {'clock_ghz': 2.687,
399
  'confidence': 'high',
400
  'method': 'Sattolo-cycle '
401
- 'pointer chase; 16 '
402
- 'KB ring for L1, 2 '
403
- 'MB TLB-resident '
404
- 'ring for L2, '
 
405
  'self-evicting '
406
  'chain (2x L2 in '
407
  'touched lines) for '
@@ -413,40 +425,58 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
413
  'footprints',
414
  'units': 'cycles (single-lane '
415
  'dependent loads)',
416
- 'value': {'dram_cycles': 649.4,
417
  'l1_cycles': 45.8,
418
- 'l2_cycles': 291.5,
419
  'ns_per_cycle': 0.372,
420
  'smem_cycles': 30.0}},
421
- 'launch_overhead': {'clock_ghz': 2.706,
422
- 'confidence': 'high',
423
  'method': 'empty-kernel '
424
- 'trains, '
425
- 'queued vs '
426
- 'launch+sync',
427
- 'note': 'host-dependent: '
428
- 'includes '
429
- 'torch op '
430
- 'dispatch',
 
 
 
 
 
 
 
 
 
 
 
 
431
  'units': 'us',
432
- 'value': {'queued_us': 5.55,
433
- 'round_trip_us': 14.1}},
434
- 'mma': {'clock_ghz': 2.706,
 
 
435
  'confidence': 'medium',
 
 
 
 
436
  'method': 'register-fragment '
437
  'mma_sync loops (wmma; '
438
  'fp8 and 2:4-sparse '
439
  'fp16 via inline PTX '
440
- 'm16n8k32; sparse '
441
- 'counted '
442
- 'dense-equivalent, the '
443
- 'vendor convention); '
444
- 'flops derive from the '
445
- 'mean accumulator '
446
- 'read-back with every '
447
- 'fragment element '
448
- 'observed, never the '
449
- 'declared loop shape',
450
  'note': 'dense-tile rate without '
451
  'memory traffic; real '
452
  'kernels see this only '
@@ -454,25 +484,54 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
454
  'behind it',
455
  'units': 'TFLOP/s (TOP/s for '
456
  'int8)',
457
- 'value': {'bf16': 365.5,
458
- 'fp16': 375.9,
459
- 'fp16_sparse_2_4': 713.4,
460
- 'fp8_e4m3': 193.7,
461
- 'int8': 770.4,
462
- 'tf32': 177.1},
463
- 'verified_k_lanes': {'bf16': {'executed_k_per_iter': 64.0,
 
 
 
464
  'vs_nominal': 1.0},
465
- 'fp16': {'executed_k_per_iter': 64.0,
 
466
  'vs_nominal': 1.0},
467
- 'fp16_sparse_2_4': {'executed_k_per_iter': 128.0,
 
468
  'vs_nominal': 1.0},
469
- 'fp8_e4m3': {'executed_k_per_iter': 64.0,
470
- 'vs_nominal': 0.25},
471
- 'int8': {'executed_k_per_iter': 64.0,
 
 
 
 
 
472
  'vs_nominal': 1.0},
473
- 'tf32': {'executed_k_per_iter': 32.0,
 
474
  'vs_nominal': 1.0}}},
475
- 'occupancy_blocks_per_sm': {'clock_ghz': 2.706,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
476
  'confidence': 'high',
477
  'method': 'occupancy '
478
  'API '
@@ -501,7 +560,7 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
501
  '16KB': 3,
502
  '32KB': 3,
503
  '48KB': 2}}},
504
- 'occupancy_knees_fma_tflops': {'clock_ghz': 2.706,
505
  'confidence': 'high',
506
  'method': 'a '
507
  'single '
@@ -521,17 +580,6 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
521
  'fixed '
522
  'total '
523
  'work; '
524
- 'throughput '
525
- 'is '
526
- 'proportional '
527
- 'to '
528
- 'resident '
529
- 'threads '
530
- 'until '
531
- 'the '
532
- 'pipe '
533
- 'saturates, '
534
- 'so '
535
  'the '
536
  'cliff '
537
  'marks '
@@ -549,15 +597,15 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
549
  'above',
550
  'units': 'TFLOP/s',
551
  'value': {'t1024': {'0KB': 75.5,
552
- '32KB': 73.66,
553
- '48KB': 74.95},
554
- 't128': {'0KB': 79.86,
555
  '32KB': 60.31,
556
- '48KB': 44.57},
557
- 't256': {'0KB': 81.69,
558
- '32KB': 79.48,
559
- '48KB': 71.47}}},
560
- 'smem_bw': {'clock_ghz': 2.706,
561
  'confidence': 'medium',
562
  'method': 'block-resident '
563
  'read+write '
@@ -566,12 +614,56 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
566
  'aggregated across '
567
  'SMs',
568
  'note': 'bank-conflict-light '
569
- 'access pattern; a '
570
- 'conflicted pattern '
571
- 'can be far lower',
 
572
  'units': 'GB/s',
573
- 'value': 21250.0},
574
- 'transfer_bw': {'clock_ghz': 2.706,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
575
  'confidence': 'high',
576
  'method': '256 MB torch '
577
  'copies, '
@@ -583,8 +675,8 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
583
  'direction of '
584
  'traffic',
585
  'units': 'GB/s',
586
- 'value': {'d2d': 330.7,
587
- 'd2h_pageable': 15.6,
588
- 'd2h_pinned': 26.2,
589
- 'h2d_pageable': 16.6,
590
  'h2d_pinned': 24.0}}}}
 
1
+ '''Release-pinned device dossiers, one per certified architecture; generated by dev/pin_dossier.py from idle single-tenant runs, consumed by dossier_drift.'''
 
 
 
 
2
 
3
  PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
4
  'confidence': 'high',
 
291
  'd2h_pinned': 10.8,
292
  'h2d_pageable': 10.3,
293
  'h2d_pinned': 11.8}}},
294
+ 'sm89_nvidiartx6000adagenerati': {'atomics_f32': {'clock_ghz': 2.705,
295
  'confidence': 'high',
296
  'method': 'atomicAdd '
297
  'hammer; curve '
 
304
  'contention) to '
305
  'one per thread',
306
  'units': 'Gop/s',
307
+ 'value': {'curve_slots': {'1': 0.71,
308
+ '145408': 413.99,
309
+ '4096': 184.9,
310
+ '512': 49.84,
311
+ '64': 13.25,
312
+ '8': 5.74},
313
+ 'shared_same_addr': 12.23}},
314
  'clock': {'confidence': 'high',
315
+ 'final_ghz': 2.698,
316
+ 'max_ghz': 2.705,
317
  'method': '24 back-to-back FMA '
318
  'spin windows of 30M '
319
  'ticks; wall/ticks '
320
  'per window',
321
+ 'min_ghz': 2.5,
322
  'sag_pct': 0.3,
323
  'units': 'GHz'},
324
  'device': {'cuda_runtime': '12.6',
 
332
  'sms': 142,
333
  'torch': '2.10.0+cu126',
334
  'vram_bytes': 48305274880},
335
+ 'dram_bw': {'clock_ghz': 2.705,
336
  'confidence': 'high',
337
  'method': 'streaming triad '
338
  'c=a+s*b per access '
 
352
  'line-efficiency '
353
  'claim',
354
  'units': 'GB/s',
355
+ 'value': {'random_gather_16B': 218.1,
356
+ 'read_only': 766.5,
357
+ 'triad_16B': 682.1,
358
+ 'triad_4B': 654.8,
359
+ 'triad_8B': 670.9,
360
  'write_only': 766.5}},
361
+ 'fma_f32': {'clock_ghz': 2.705,
362
  'confidence': 'high',
363
  'method': 'register-resident '
364
  'FMA chains',
365
  'units': 'TFLOP/s',
366
+ 'value': 86.58},
367
+ 'fma_f64': {'clock_ghz': 2.705,
368
  'confidence': 'high',
369
  'method': 'register-resident '
370
  'double FMA chains '
371
  '(the DFMA pipe)',
372
  'units': 'TFLOP/s',
373
+ 'value': 1.257},
374
+ 'fma_ilp': {'clock_ghz': 2.705,
375
+ 'confidence': 'high',
376
+ 'method': '1/2/4 independent '
377
+ 'dependent FMA '
378
+ 'chains per thread '
379
+ 'at one block of 64 '
380
+ 'threads per SM; a '
381
+ 'ratio near 4 means '
382
+ 'latency-bound at '
383
+ 'that occupancy',
384
+ 'units': 'TFLOP/s',
385
+ 'value': {'ilp1': 11.75,
386
+ 'ilp2': 23.37,
387
+ 'ilp4': 43.03,
388
+ 'ratio_4_1': 3.66}},
389
+ 'grid_barrier': {'clock_ghz': 2.705,
390
  'confidence': 'high',
391
  'method': 'cooperative '
392
  'kernel, 142 '
 
394
  'threads',
395
  'units': 'us per '
396
  'grid.sync',
397
+ 'value': 0.55},
398
+ 'l2_bw': {'clock_ghz': 2.705,
399
  'confidence': 'medium',
400
  'method': 'in-kernel repeated '
401
  'triad passes over 72 '
 
405
  'from HBM; amortized '
406
  'over 64 passes',
407
  'units': 'GB/s',
408
+ 'value': 9590.6},
409
+ 'latency': {'clock_ghz': 2.691,
410
  'confidence': 'high',
411
  'method': 'Sattolo-cycle '
412
+ 'pointer chase, min '
413
+ 'of 3 runs; 16 KB '
414
+ 'ring for L1, 2 MB '
415
+ 'TLB-resident ring '
416
+ 'for L2, '
417
  'self-evicting '
418
  'chain (2x L2 in '
419
  'touched lines) for '
 
425
  'footprints',
426
  'units': 'cycles (single-lane '
427
  'dependent loads)',
428
+ 'value': {'dram_cycles': 656.9,
429
  'l1_cycles': 45.8,
430
+ 'l2_cycles': 292.0,
431
  'ns_per_cycle': 0.372,
432
  'smem_cycles': 30.0}},
433
+ 'launch_overhead': {'clock_ghz': 2.705,
434
+ 'confidence': 'medium',
435
  'method': 'empty-kernel '
436
+ 'trains; '
437
+ 'torch path '
438
+ 'per-launch '
439
+ 'through op '
440
+ 'dispatch, '
441
+ 'raw path '
442
+ 'looped '
443
+ 'inside one '
444
+ 'binding '
445
+ 'call',
446
+ 'note': 'host-state-dependent; '
447
+ 'the '
448
+ 'torch-minus-raw '
449
+ 'gap is the '
450
+ 'dispatch '
451
+ 'cost a '
452
+ 'torch-launched '
453
+ 'candidate '
454
+ 'pays',
455
  'units': 'us',
456
+ 'value': {'raw_queued_us': 6.75,
457
+ 'raw_round_trip_us': 13.87,
458
+ 'torch_queued_us': 7.09,
459
+ 'torch_round_trip_us': 18.53}},
460
+ 'mma': {'clock_ghz': 2.705,
461
  'confidence': 'medium',
462
+ 'fp8_accum': {'fp8_e4m3': {'effective_accum_mantissa_bits': 8,
463
+ 'stall_element_value': 16384.0},
464
+ 'fp8_e5m2': {'effective_accum_mantissa_bits': 8,
465
+ 'stall_element_value': 16384.0}},
466
  'method': 'register-fragment '
467
  'mma_sync loops (wmma; '
468
  'fp8 and 2:4-sparse '
469
  'fp16 via inline PTX '
470
+ 'm16n8k32); flops '
471
+ 'derive from a '
472
+ 'sub-saturation '
473
+ 'accumulator read-back '
474
+ 'with every fragment '
475
+ 'element observed, '
476
+ 'never the declared '
477
+ 'loop shape; sparse '
478
+ 'reports executed and '
479
+ 'dense-equivalent flops',
480
  'note': 'dense-tile rate without '
481
  'memory traffic; real '
482
  'kernels see this only '
 
484
  'behind it',
485
  'units': 'TFLOP/s (TOP/s for '
486
  'int8)',
487
+ 'value': {'bf16': 369.6,
488
+ 'fp16': 365.3,
489
+ 'fp16_sparse_2_4': {'dense_equivalent': 711.1,
490
+ 'executed': 355.6},
491
+ 'fp8_e4m3': 772.3,
492
+ 'fp8_e5m2': 771.2,
493
+ 'int8': 750.0,
494
+ 'tf32': 178.3},
495
+ 'verified_k_lanes': {'bf16': {'calibration_linear': True,
496
+ 'executed_k_per_iter': 64.0,
497
  'vs_nominal': 1.0},
498
+ 'fp16': {'calibration_linear': True,
499
+ 'executed_k_per_iter': 64.0,
500
  'vs_nominal': 1.0},
501
+ 'fp16_sparse_2_4': {'calibration_linear': True,
502
+ 'executed_k_per_iter': 128.0,
503
  'vs_nominal': 1.0},
504
+ 'fp8_e4m3': {'calibration_linear': True,
505
+ 'executed_k_per_iter': 256.0,
506
+ 'vs_nominal': 1.0},
507
+ 'fp8_e5m2': {'calibration_linear': True,
508
+ 'executed_k_per_iter': 256.0,
509
+ 'vs_nominal': 1.0},
510
+ 'int8': {'calibration_linear': True,
511
+ 'executed_k_per_iter': 64.0,
512
  'vs_nominal': 1.0},
513
+ 'tf32': {'calibration_linear': True,
514
+ 'executed_k_per_iter': 32.0,
515
  'vs_nominal': 1.0}}},
516
+ 'mma_fed': {'clock_ghz': 2.705,
517
+ 'confidence': 'high',
518
+ 'method': 'm16n16k16 fp16 '
519
+ 'tiles fed from an '
520
+ 'L2-resident buffer '
521
+ 'through cp.async '
522
+ 'double-buffered '
523
+ 'shared memory; '
524
+ 'accumulator-verified',
525
+ 'note': 'the achieved '
526
+ 'counterpart to the '
527
+ 'register peak: '
528
+ 'staging exposed, one '
529
+ 'dependent '
530
+ 'accumulator chain '
531
+ 'per warp',
532
+ 'units': 'TFLOP/s',
533
+ 'value': 85.2},
534
+ 'occupancy_blocks_per_sm': {'clock_ghz': 2.705,
535
  'confidence': 'high',
536
  'method': 'occupancy '
537
  'API '
 
560
  '16KB': 3,
561
  '32KB': 3,
562
  '48KB': 2}}},
563
+ 'occupancy_knees_fma_tflops': {'clock_ghz': 2.705,
564
  'confidence': 'high',
565
  'method': 'a '
566
  'single '
 
580
  'fixed '
581
  'total '
582
  'work; '
 
 
 
 
 
 
 
 
 
 
 
583
  'the '
584
  'cliff '
585
  'marks '
 
597
  'above',
598
  'units': 'TFLOP/s',
599
  'value': {'t1024': {'0KB': 75.5,
600
+ '32KB': 73.71,
601
+ '48KB': 72.69},
602
+ 't128': {'0KB': 79.72,
603
  '32KB': 60.31,
604
+ '48KB': 45.81},
605
+ 't256': {'0KB': 78.79,
606
+ '32KB': 78.85,
607
+ '48KB': 72.97}}},
608
+ 'smem_bw': {'clock_ghz': 2.705,
609
  'confidence': 'medium',
610
  'method': 'block-resident '
611
  'read+write '
 
614
  'aggregated across '
615
  'SMs',
616
  'note': 'bank-conflict-light '
617
+ 'access pattern; the '
618
+ 'stride sweep below '
619
+ 'prices the '
620
+ 'conflicted case',
621
  'units': 'GB/s',
622
+ 'value': 22244.0},
623
+ 'smem_conflict_penalty': {'clock_ghz': 2.705,
624
+ 'confidence': 'high',
625
+ 'method': 'dependent '
626
+ 'read-FMA-write '
627
+ 'per '
628
+ 'thread '
629
+ 'at '
630
+ 'warp-lane '
631
+ 'bank '
632
+ 'distance '
633
+ '`stride`; '
634
+ 'stride '
635
+ '32 '
636
+ 'puts '
637
+ 'every '
638
+ 'lane '
639
+ 'in '
640
+ 'one '
641
+ 'bank',
642
+ 'note': 'the '
643
+ 'direct '
644
+ 'conflict '
645
+ 'measurement '
646
+ 'that '
647
+ 'disambiguates '
648
+ 'a '
649
+ 'bank-conflict '
650
+ 'signature '
651
+ 'from '
652
+ 'an ILP '
653
+ 'stall',
654
+ 'units': 'x '
655
+ '(time '
656
+ 'ratio '
657
+ 'vs '
658
+ 'stride '
659
+ '1)',
660
+ 'value': {'stride_1': 1.0,
661
+ 'stride_16': 7.13,
662
+ 'stride_2': 1.07,
663
+ 'stride_32': 14.08,
664
+ 'stride_4': 2.04,
665
+ 'stride_8': 3.91}},
666
+ 'transfer_bw': {'clock_ghz': 2.705,
667
  'confidence': 'high',
668
  'method': '256 MB torch '
669
  'copies, '
 
675
  'direction of '
676
  'traffic',
677
  'units': 'GB/s',
678
+ 'value': {'d2d': 324.0,
679
+ 'd2h_pageable': 7.8,
680
+ 'd2h_pinned': 26.3,
681
+ 'h2d_pageable': 7.6,
682
  'h2d_pinned': 24.0}}}}
build/torch213-cxx11-cu130-x86_64-linux/metadata.json CHANGED
@@ -1,9 +1,10 @@
1
  {
2
  "name": "metakernel",
3
- "id": "_metakernel_cuda_b62a1ce",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "python-depends": [],
 
7
  "backend": {
8
  "type": "cuda",
9
  "archs": [
@@ -18,21 +19,20 @@
18
  "digest": {
19
  "algorithm": "sha256",
20
  "files": {
21
- "__init__.py": "e/P3LIoxZqzuhgyc26YJRkdLj4OilextyRiU8YbAvIA=",
22
- "_metakernel_cuda_b62a1ce.abi3.so": "iEONw8giFDAJSgh06xAtxOrc39pAuhwXa6n/mrPCH+8=",
23
- "_ops.py": "QokZXDYGh+rLEKO/wNDRcMkJPtPWK568RiTlCHopsHM=",
24
- "_pinned.py": "4i1FvV3kWanEdFLd9+rGOjHcHDAyOjKnjLt5LUzgASs=",
25
- "metakernel/__init__.py": "DFYPlrhXwYjEqCl/8n0SmWGZV8NFml5DPhMjKfv98GY="
26
  }
27
  },
28
  "provenance": {
29
  "kernel-builder": {
30
  "version": "0.17.0-dev0",
31
- "sha": "19aaa6421e674e9fecc352bbae6eab81d19a6bf4",
32
  "dirty": false
33
  },
34
  "kernel": {
35
- "sha": "b62a1cef64b6e3b2cabf774c82ea7ce61dcac6c0",
36
  "dirty": false
37
  }
38
  }
 
1
  {
2
  "name": "metakernel",
3
+ "id": "_metakernel_cuda_0b9de42",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "python-depends": [],
7
+ "kernel-depends": [],
8
  "backend": {
9
  "type": "cuda",
10
  "archs": [
 
19
  "digest": {
20
  "algorithm": "sha256",
21
  "files": {
22
+ "__init__.py": "lDQ+fJ0JnI3sQGR3UMnDovBbG4ciYBsaFjmgYdKDTKI=",
23
+ "_metakernel_cuda_0b9de42.abi3.so": "gSDykDaVP0KcJkDb5tywV9RP86yJlEyEnQf1q+ZhbI0=",
24
+ "_ops.py": "x88Bkm1nxRli0nIu2u4ztaJopS1IAZWdN9LZPfspJdo=",
25
+ "_pinned.py": "UyzMniAjfVMIrIbbrSq92KKo5dX5l/GwV3pMVA+eNtM="
 
26
  }
27
  },
28
  "provenance": {
29
  "kernel-builder": {
30
  "version": "0.17.0-dev0",
31
+ "sha": "2c40e10d3da1392245ee81bd05b55fb3103b2b34",
32
  "dirty": false
33
  },
34
  "kernel": {
35
+ "sha": "0b9de42fee8a1e52593f2f1f58460c0acc93d7e8",
36
  "dirty": false
37
  }
38
  }
build/torch213-cxx11-cu130-x86_64-linux/metakernel/__init__.py DELETED
@@ -1,26 +0,0 @@
1
- import ctypes
2
- import importlib.util
3
- import sys
4
- from pathlib import Path
5
- from types import ModuleType
6
-
7
-
8
- def _import_from_path(file_path: Path) -> ModuleType:
9
- # We cannot use the module name as-is, after adding it to `sys.modules`,
10
- # it would also be used for other imports. So, we make a module name that
11
- # depends on the path for it to be unique using the hex-encoded hash of
12
- # the path.
13
- path_hash = "{:x}".format(ctypes.c_size_t(hash(file_path.absolute())).value)
14
- module_name = path_hash
15
- spec = importlib.util.spec_from_file_location(module_name, file_path)
16
- if spec is None:
17
- raise ImportError(f"Cannot load spec for {module_name} from {file_path}")
18
- module = importlib.util.module_from_spec(spec)
19
- if module is None:
20
- raise ImportError(f"Cannot load module {module_name} from spec")
21
- sys.modules[module_name] = module
22
- spec.loader.exec_module(module) # type: ignore
23
- return module
24
-
25
-
26
- globals().update(vars(_import_from_path(Path(__file__).parent.parent / "__init__.py")))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
build/torch213-cxx11-cu132-x86_64-linux/__init__.py CHANGED
@@ -1,20 +1,4 @@
1
- """Measuring instrument for the kernel-design loop: timing, adversarial
2
- correctness comparison, and measured device facts, as loadable ops.
3
-
4
- from kernels import get_kernel
5
- mk = get_kernel("phanerozoic/metakernel", version=1,
6
- trust_remote_code=True)
7
-
8
- dossier = mk.probe_device() # measured facts
9
- report = mk.bench(lambda: op(x), iters=200) # throttle-rejected
10
- verdict = mk.compare(ref, cand, gen) # ULP evidence
11
- table = mk.sweep(factory, {"BLOCK": [64, 128]}) # config search
12
-
13
- Every op returns plain dicts and tensors. Every dossier entry is
14
- measured by a dedicated probe kernel, carries its SM clock, and labels
15
- throttled samples; no hardware counters, so entries carry a confidence
16
- field.
17
- """
18
  import math
19
  import os
20
  import time
@@ -24,9 +8,7 @@ import torch
24
  try:
25
  from ._ops import ops
26
  except ImportError: # pragma: no cover - spawned child in a source tree
27
- # Subprocess-isolation children re-import this package; in a source
28
- # checkout there is no generated _ops module, so rebuild the op table
29
- # from the (cached) JIT extension that load_local advertised.
30
  _root = os.environ.get("METAKERNEL_DEV_ROOT")
31
  if not _root:
32
  raise
@@ -48,24 +30,25 @@ try:
48
  except ImportError: # pragma: no cover - fresh tree before first pin
49
  _PINNED = {}
50
 
51
- __all__ = ["probe_device", "bench", "compare", "order_shuffles",
52
- "fuzz_shapes", "sweep", "alloc_stamps", "read_stamps",
53
- "stamped_demo", "measure_clock", "ulp_diff", "dossier_drift",
54
- "pinned_dossiers", "ops"]
55
-
56
- # name -> (kind, M*N of the C tile, nominal K, work multiplier, nominal
57
- # K-lanes per unrolled iteration). The accumulator read-back yields
58
- # executed K-lanes per op (column sums of B equal K), so flops =
59
- # 2 x M*N x gain x multiplier with nothing declared trusted; multiplier
60
- # 2.0 is the sparse dense-equivalent convention. Lanes/iter: wmma K x 4
61
- # chained ops; PTX kinds x 8 over two chains.
62
  _MMA_KINDS = {
63
- "fp16": (0, 16 * 16, 16, 1.0, 64.0),
64
- "bf16": (1, 16 * 16, 16, 1.0, 64.0),
65
- "tf32": (2, 16 * 16, 8, 1.0, 32.0),
66
- "int8": (3, 16 * 16, 16, 1.0, 64.0),
67
- "fp8_e4m3": (4, 16 * 8, 32, 1.0, 256.0),
68
- "fp16_sparse_2_4": (5, 16 * 8, 32, 2.0, 128.0), # dense-equivalent
 
69
  }
70
 
71
 
@@ -74,7 +57,7 @@ def _sync():
74
 
75
 
76
  def _events(fn, reps):
77
- """Time reps calls of fn with one event pair each; returns ms list."""
78
  starts = [torch.cuda.Event(enable_timing=True) for _ in range(reps)]
79
  stops = [torch.cuda.Event(enable_timing=True) for _ in range(reps)]
80
  for s, e in zip(starts, stops):
@@ -86,8 +69,7 @@ def _events(fn, reps):
86
 
87
 
88
  def measure_clock(device="cuda", ticks=4_000_000, blocks=None):
89
- """Effective SM clock in GHz under an FMA spin of `ticks` clock64 ticks.
90
- Wall time over elapsed ticks is the frequency the ticks ran at."""
91
  props = torch.cuda.get_device_properties(device)
92
  if blocks is None:
93
  blocks = props.multi_processor_count
@@ -120,8 +102,7 @@ def _entry(value, units, method, clock_ghz, confidence, note=""):
120
 
121
 
122
  def _sattolo_ring(n, device, generator=None):
123
- """A random single-cycle permutation as an int32 next-pointer table, so
124
- a pointer chase visits every slot before repeating."""
125
  perm = torch.randperm(n, generator=generator)
126
  ring = torch.empty(n, dtype=torch.int32)
127
  ring[perm] = torch.roll(perm, -1).to(torch.int32)
@@ -129,8 +110,7 @@ def _sattolo_ring(n, device, generator=None):
129
 
130
 
131
  def _smi_settings(index):
132
- """ECC mode, driver, and power limit via nvidia-smi; None fields
133
- when the tool is unavailable."""
134
  import subprocess
135
  try:
136
  out = subprocess.run(
@@ -150,10 +130,19 @@ def _smi_settings(index):
150
  return {"ecc": None, "driver": None, "power_limit_w": None}
151
 
152
 
 
 
 
 
 
 
 
 
 
 
 
153
  def mma_tile_check(device="cuda", trials=8):
154
- """Random small-integer m16n16k16 tiles through the wmma fragment
155
- path, compared exactly to the reference GEMM; pins M, N, K, and
156
- operand mapping (integer operands make fp32 accumulation exact)."""
157
  worst = 0.0
158
  for t in range(trials):
159
  g = torch.Generator(device=device).manual_seed(1000 + t)
@@ -171,10 +160,83 @@ def mma_tile_check(device="cuda", trials=8):
171
  "trials": trials}
172
 
173
 
174
- def probe_device(device="cuda", quick=False):
175
- """Empirical device dossier. Each entry is measured by a dedicated
176
- probe kernel and carries units, method, the SM clock it ran under, and
177
- a confidence field."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
  props = torch.cuda.get_device_properties(device)
179
  sm = props.multi_processor_count
180
  l2 = props.L2_cache_size
@@ -187,8 +249,7 @@ def probe_device(device="cuda", quick=False):
187
  "vram_bytes": props.total_memory,
188
  "torch": torch.__version__,
189
  "cuda_runtime": torch.version.cuda,
190
- # instrument settings: readings depend on them, so the
191
- # dossier records them (None when nvidia-smi is unavailable)
192
  **_smi_settings(torch.device(device).index or 0),
193
  }
194
  }
@@ -213,9 +274,8 @@ def probe_device(device="cuda", quick=False):
213
  }
214
  clk = max(traj)
215
 
216
- # ---- device-memory bandwidth: triad per access width, plus read-only,
217
- # write-only, and random-gather
218
- n4 = 16 << 20 # 16M float4 = 256 MB per buffer
219
  if props.total_memory < 4 << 30:
220
  n4 = 4 << 20
221
  a = torch.rand(n4 * 4, device=device)
@@ -239,7 +299,6 @@ def probe_device(device="cuda", quick=False):
239
  dtype=torch.int32).to(device)
240
  ops.mk_gather(a, idx, c, 256, 0)
241
  ms = _median(_events(lambda: ops.mk_gather(a, idx, c, 256, 0), reps))
242
- # traffic accounting: idx reads + 16B random loads + streamed stores
243
  hbm["random_gather_16B"] = round(
244
  (idx.numel() * 4 + 2 * n4 * 16) / (ms * 1e-3) / 1e9, 1)
245
  d["dram_bw"] = _entry(hbm, "GB/s", "streaming triad c=a+s*b per access "
@@ -250,9 +309,9 @@ def probe_device(device="cuda", quick=False):
250
  "so its nominal number is the achievable rate for "
251
  "random access, not a line-efficiency claim")
252
 
253
- # ---- L2 bandwidth (working set 3 buffers inside L2)
254
  ws = max(l2 // 4, 1 << 20)
255
- nl2 = ws // 16 # float4 elems per buffer
256
  al = a[:nl2 * 4].contiguous()
257
  bl = b[:nl2 * 4].contiguous()
258
  cl = c[:nl2 * 4].contiguous()
@@ -267,62 +326,61 @@ def probe_device(device="cuda", quick=False):
267
  "medium", "first pass streams from HBM; amortized "
268
  f"over {passes} passes")
269
 
270
- # ---- latencies: pointer chase per level. The 2 MB L2 ring exceeds
271
- # L1, fits L2 everywhere, and stays TLB-resident after a warm walk;
272
- # the DRAM chain touches 2x L2 in lines, so it evicts itself.
273
  lat = {}
274
  idle_clk = measure_clock(device, ticks=2_000_000, blocks=1)
275
- ring_s = _sattolo_ring(8192, device)
276
- ops.mk_chase_shared(ring_s, 1 << 14, out64)
277
- _sync()
278
- hops = 1 << 16
279
- ops.mk_chase_shared(ring_s, hops, out64)
280
- _sync()
281
- lat["smem_cycles"] = round(int(out64[0].item()) / hops, 1)
282
 
283
- l1_ring_n = (16 << 10) // 4 # 16 KB: L1-resident after a warm walk
284
- ring_l1 = _sattolo_ring(l1_ring_n, device)
285
- ops.mk_chase_global(ring_l1, l1_ring_n, out64)
286
- _sync()
287
- hops_l1 = 1 << 15
288
- ops.mk_chase_global(ring_l1, hops_l1, out64)
289
- _sync()
290
- lat["l1_cycles"] = round(int(out64[0].item()) / hops_l1, 1)
291
-
292
- l2_ring_n = (2 << 20) // 4
293
- ring_l2 = _sattolo_ring(l2_ring_n, device)
294
- ops.mk_chase_global(ring_l2, l2_ring_n, out64) # full-cycle warm walk
295
- _sync()
296
- hops_l2 = 1 << 14
297
- ops.mk_chase_global(ring_l2, hops_l2, out64)
298
- _sync()
299
- lat["l2_cycles"] = round(int(out64[0].item()) / hops_l2, 1)
300
 
 
 
 
 
 
 
 
 
 
 
 
 
301
  hbm_bytes = min(max(4 * l2, 64 << 20), props.total_memory // 8)
302
  ring_h = _sattolo_ring(int(hbm_bytes) // 4, device)
303
  hops_h = max((2 * l2) // 128, 1 << 17)
304
- ops.mk_chase_global(ring_h, 1 << 14, out64) # fault pages / warm TLB
305
- _sync()
306
- ops.mk_chase_global(ring_h, hops_h, out64)
307
- _sync()
308
- lat["dram_cycles"] = round(int(out64[0].item()) / hops_h, 1)
309
  lat["ns_per_cycle"] = round(1.0 / idle_clk, 3)
310
  d["latency"] = _entry(lat, "cycles (single-lane dependent loads)",
311
- "Sattolo-cycle pointer chase; 16 KB ring for L1, "
312
- "2 MB TLB-resident ring for L2, self-evicting "
313
- "chain (2x L2 in touched lines) for HBM",
314
- idle_clk, "high",
315
  "smem includes index arithmetic; HBM includes "
316
  "residual TLB effects at large footprints")
317
  del ring_h, ring_l2, ring_l1, ring_s
318
 
319
- # ---- fp32 FMA and tensor-core throughput
320
  blocks = sm * 4
321
  iters = 20_000 if quick else 60_000
322
  ops.mk_fma_f32(blocks, 256, iters, sink)
323
  ms = _median(_events(lambda: ops.mk_fma_f32(blocks, 256, iters, sink),
324
  reps))
325
- flops = blocks * 256 * iters * 16 * 2 * 2 # 2 chains x 2 flops x unroll
326
  d["fma_f32"] = _entry(round(flops / (ms * 1e-3) / 1e12, 2), "TFLOP/s",
327
  "register-resident FMA chains", clk, "high")
328
  sink64 = torch.zeros(2, dtype=torch.float64, device=device)
@@ -334,43 +392,88 @@ def probe_device(device="cuda", quick=False):
334
  d["fma_f64"] = _entry(round(flops64 / (ms * 1e-3) / 1e12, 3), "TFLOP/s",
335
  "register-resident double FMA chains (the DFMA "
336
  "pipe)", clk, "high")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
337
  mma = {}
338
  mma_klanes = {}
339
- for name, (kind, mn, k_nom, mult, nom_lanes) in _MMA_KINDS.items():
 
340
  it = 30_000 if quick else 100_000
341
  r = ops.mk_mma(kind, blocks, 8, 512, sink)
342
  if r != 0:
343
  mma[name] = "unsupported"
344
  continue
345
  _sync()
346
- # verification pass: with unit operands the accumulator gain IS
347
- # the executed K-lane count (exact at these magnitudes)
348
- cal_iters = 512
349
- sink[1] = 0.0
350
- ops.mk_mma(kind, 1, 1, cal_iters, sink)
351
- _sync()
352
- gain_per_iter = float(sink[1].item()) / cal_iters
353
  mma_klanes[name] = {
354
- "executed_k_per_iter": round(gain_per_iter, 2),
355
- "vs_nominal": round(gain_per_iter / nom_lanes, 3),
 
356
  }
 
 
357
  ms = _median(_events(
358
  lambda k=kind, i=it: ops.mk_mma(k, blocks, 8, i, sink), reps))
359
- flops = blocks * 8 * it * 2.0 * mn * gain_per_iter * mult
360
- mma[name] = round(flops / (ms * 1e-3) / 1e12, 1)
 
 
 
 
361
  d["mma"] = _entry(mma, "TFLOP/s (TOP/s for int8)",
362
  "register-fragment mma_sync loops (wmma; fp8 and "
363
- "2:4-sparse fp16 via inline PTX m16n8k32; sparse "
364
- "counted dense-equivalent, the vendor convention); "
365
- "flops derive from the mean accumulator read-back "
366
  "with every fragment element observed, never the "
367
- "declared loop shape",
 
368
  clk, "medium",
369
  "dense-tile rate without memory traffic; real "
370
  "kernels see this only when staging hides behind it")
371
  d["mma"]["verified_k_lanes"] = mma_klanes
 
 
372
 
373
- # ---- shared-memory bandwidth
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
374
  it_sm = 30_000 if quick else 100_000
375
  ops.mk_smem_bw(blocks, 256, it_sm, sink)
376
  ms = _median(_events(lambda: ops.mk_smem_bw(blocks, 256, it_sm, sink),
@@ -380,8 +483,23 @@ def probe_device(device="cuda", quick=False):
380
  "block-resident read+write streaming (8 B per "
381
  "thread-iteration), aggregated across SMs", clk,
382
  "medium",
383
- "bank-conflict-light access pattern; a conflicted "
384
- "pattern can be far lower")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
385
 
386
  # ---- PCIe / host transfer bandwidth
387
  nbytes = 256 << 20
@@ -412,9 +530,7 @@ def probe_device(device="cuda", quick=False):
412
  "d2d counts one direction of traffic")
413
  del host_pin, host_pag, dev_t, dev_u
414
 
415
- # ---- atomic contention curve: throughput vs number of distinct
416
- # addresses the whole grid hammers (slots = 1 is full contention;
417
- # slots = threads is contention-free), plus the shared-memory point
418
  n_thr = blocks * 256
419
  buf = torch.zeros(n_thr, dtype=torch.float32, device=device)
420
  at = {"curve_slots": {}}
@@ -424,7 +540,7 @@ def probe_device(device="cuda", quick=False):
424
  buf.zero_()
425
  ops.mk_atomics(1, blocks, 256, it_at, buf, slots)
426
  ms = _median(_events(
427
- lambda s=slots: ops.mk_atomics(1, blocks, 256, it_at, buf, s),
428
  reps))
429
  at["curve_slots"][str(slots)] = round(
430
  n_thr * it_at / (ms * 1e-3) / 1e9, 2)
@@ -437,7 +553,7 @@ def probe_device(device="cuda", quick=False):
437
  "of distinct global addresses from 1 (full "
438
  "contention) to one per thread", clk, "high")
439
 
440
- # ---- launch overhead
441
  anchor = sink
442
  n_launch = 500 if quick else 2000
443
  _sync()
@@ -452,10 +568,19 @@ def probe_device(device="cuda", quick=False):
452
  ops.mk_empty(anchor)
453
  _sync()
454
  rt = (time.perf_counter() - t0) / n_rt * 1e6
 
 
455
  d["launch_overhead"] = _entry(
456
- {"queued_us": round(queued, 2), "round_trip_us": round(rt, 2)},
457
- "us", "empty-kernel trains, queued vs launch+sync", clk, "high",
458
- "host-dependent: includes torch op dispatch")
 
 
 
 
 
 
 
459
 
460
  # ---- grid barrier
461
  bar_iters = 500 if quick else 2000
@@ -471,9 +596,7 @@ def probe_device(device="cuda", quick=False):
471
  "unavailable or grid too large", clk,
472
  "high")
473
 
474
- # ---- occupancy table (API) plus timed knees: the same random gather
475
- # at every (threads, dyn_smem) point; when the forced blocks/SM stop
476
- # covering memory latency, achieved bandwidth falls off the cliff
477
  occ = {}
478
  for threads in (128, 256, 512, 1024):
479
  row = {}
@@ -494,8 +617,8 @@ def probe_device(device="cuda", quick=False):
494
  ops.mk_fma_dep(knee_blocks, threads, smem_kb * 1024,
495
  knee_iters, sink)
496
  ms = _median(_events(
497
- lambda t=threads, s=smem_kb: ops.mk_fma_dep(
498
- knee_blocks, t, s * 1024, knee_iters, sink), reps))
499
  fl = knee_blocks * threads * knee_iters * 16 * 2
500
  row[f"{smem_kb}KB"] = round(fl / (ms * 1e-3) / 1e12, 2)
501
  knees[f"t{threads}"] = row
@@ -504,27 +627,160 @@ def probe_device(device="cuda", quick=False):
504
  "at forced (threads, dyn smem) occupancies over fixed total work; "
505
  "the cliff marks where occupancy stops covering latency",
506
  clk, "high", "timing-derived counterpart to the API table above")
 
 
 
507
  return d
508
 
509
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
510
  def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
511
  dtype="fp16", clock_guard=0.97, clock_stride=8, graph=False,
512
- device="cuda"):
513
- """Median-of-N cudaEvent timing with throttle rejection.
514
-
515
- The SM clock is probed every `clock_stride` samples; samples in a
516
- window below `clock_guard` x the best observed clock are rejected
517
- and counted. Declared `bytes`/`flops` yield achieved GB/s / TFLOP/s;
518
- a `dossier` adds roofline placement. `graph=True` times CUDA-graph
519
- replays, stripping Python and torch dispatch (fn must be graph-safe:
520
- no sync, no allocation growth, static shapes; a failed capture falls
521
- back to eager with the reason reported). `clock_stride=1` probes
522
- around every sample, ~0.3 ms each."""
523
  for _ in range(warmup):
524
  fn()
525
  _sync()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
526
  graph_state = "off"
527
  graph_reason = ""
 
 
 
 
 
 
528
  if graph:
529
  pre_rng = torch.cuda.get_rng_state(device)
530
  try:
@@ -539,10 +795,7 @@ def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
539
  except Exception as e: # noqa: BLE001 - fall back, say why
540
  graph_state = "fallback_eager"
541
  graph_reason = f"{type(e).__name__}: {str(e)[:120]}"
542
- # drain the invalidated stream so eager timing can proceed.
543
- # On some torch versions a failed capture also wedges the
544
- # device RNG with no in-process recovery; timing uses no RNG,
545
- # but the caller's later randn may raise.
546
  rng_ok = True
547
  for _attempt in range(5):
548
  try:
@@ -572,25 +825,51 @@ def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
572
  samples.append((_events(fn, n), len(clocks) - 1))
573
  clocks.append(measure_clock(device, ticks=probe_ticks))
574
  best_clk = max(clocks)
575
- kept, rejected = [], 0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
576
  flat = []
577
- for group, ci in samples:
578
- window_ok = (clocks[ci] >= clock_guard * best_clk and
579
- clocks[min(ci + 1, len(clocks) - 1)] >=
580
- clock_guard * best_clk)
581
- for ms in group:
582
- flat.append(ms)
583
- if window_ok:
 
 
 
 
 
 
 
 
 
 
 
 
584
  kept.append(ms)
585
  else:
586
  rejected += 1
587
- if not kept:
 
588
  kept = flat
589
  med = _median(kept)
590
  ks = sorted(kept)
591
  n_k = len(ks)
592
- # order-statistic 95% CI on the median (normal approximation to the
593
- # binomial ranks); degenerate at tiny n, where it spans the sample
594
  half = 1.959964 * (n_k ** 0.5) / 2.0
595
  lo_i = max(0, int(n_k / 2 - half) - 1)
596
  hi_i = min(n_k - 1, int(n_k / 2 + half))
@@ -605,12 +884,25 @@ def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
605
  "mean_ms": round(sum(kept) / n_k, 4),
606
  "samples": len(flat),
607
  "rejected_throttled": rejected,
 
608
  "graph_captured": graph_state == "captured",
609
  "clock_ghz": {"best": round(best_clk, 3),
610
  "worst": round(min(clocks), 3)},
611
  }
 
 
 
 
 
 
 
612
  if graph_reason:
613
  rep["graph_fallback_reason"] = graph_reason
 
 
 
 
 
614
  if bytes is not None:
615
  rep["achieved_gbs"] = round(bytes / (med * 1e-3) / 1e9, 1)
616
  if flops is not None:
@@ -620,6 +912,8 @@ def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
620
  if dossier is not None and bytes and flops:
621
  bw = max(dossier["dram_bw"]["value"].values()) * 1e9
622
  mma_v = dossier["mma"]["value"].get(dtype)
 
 
623
  peak = (mma_v if isinstance(mma_v, (int, float))
624
  else dossier["fma_f32"]["value"]) * 1e12
625
  ai = flops / bytes
@@ -630,13 +924,12 @@ def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
630
  "pct_of_roof": round(100 * (flops / (med * 1e-3)) / roof, 1),
631
  "peak_dtype": dtype,
632
  }
 
633
  return rep
634
 
635
 
636
  def _to_ordered_int(t):
637
- """Map float bit patterns to integers whose difference is the ULP
638
- distance: positive floats keep their bits, negative floats mirror
639
- below zero, so the mapping is monotone in the float value."""
640
  if t.dtype == torch.float32:
641
  u = t.view(torch.int32).to(torch.int64) & 0xFFFFFFFF
642
  half = 1 << 31
@@ -652,11 +945,12 @@ def _to_ordered_int(t):
652
 
653
 
654
  def ulp_diff(a, b):
655
- """Elementwise ULP distance between same-dtype tensors. For floats,
656
- NaNs in the same position count as 0 and a one-sided NaN counts as
657
- 2**62; for integer and bool tensors the distance is the absolute
658
- numeric difference (an exact op must be exactly equal)."""
659
- assert a.dtype == b.dtype and a.shape == b.shape
 
660
  if not a.is_floating_point():
661
  return (a.to(torch.int64) - b.to(torch.int64)).abs()
662
  ia, ib = _to_ordered_int(a), _to_ordered_int(b)
@@ -673,9 +967,7 @@ class order_shuffles:
673
 
674
  @staticmethod
675
  def last_dim():
676
- """Permute the last dim of every tensor arg identically: the
677
- accumulation order of a last-dim reduction changes, the value
678
- does not."""
679
  def shuffle(args, seed):
680
  g = torch.Generator(device=args[0].device).manual_seed(seed)
681
  p = torch.randperm(args[0].shape[-1], device=args[0].device,
@@ -685,8 +977,7 @@ class order_shuffles:
685
 
686
  @staticmethod
687
  def matmul_k():
688
- """Permute K of (A [.., M, K], B [.., K, N]) consistently: the
689
- product is unchanged, the accumulation order is not."""
690
  def shuffle(args, seed):
691
  a_m, b_m = args[0], args[1]
692
  g = torch.Generator(device=a_m.device).manual_seed(seed)
@@ -700,37 +991,70 @@ def _as_outputs(x):
700
  return list(x) if isinstance(x, (tuple, list)) else [x]
701
 
702
 
703
- def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
704
- ref_variants=None, ulp_tol=None, atol=None, rtol=None,
705
- band_margin=4.0, zero_atol=None, worst_k=8, seed=0):
706
- """Adversarial correctness comparison with ULP evidence.
707
-
708
- `gen(seed) -> args`; `ref(*args)` and `cand(*args)` return a tensor
709
- or tuple (worst output governs). With `order_shuffle(args, seed)`
710
- reordering contractual-noise dimensions (see `order_shuffles`), each
711
- element is judged against its own measured POINTWISE reduction-order
712
- band: pass is max(ulp / (own_band + 2)) <= band_margin; without a
713
- band, max ULP <= ulp_tol (default 2); `atol`/`rtol` are an
714
- alternative acceptance route. `zero_atol`: elements with
715
- |ref| < zero_atol are judged by absolute difference (ULP explodes
716
- across the denormal range). The report carries the top-`worst_k`
717
- offenders (index, ref, cand, ULP). Order shuffles cover the
718
- reference's own topology; a candidate with a different legitimate
719
- topology (split-K) needs `ref_variants`, callables `f(args) -> out`
720
- whose outputs widen the pointwise band."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
721
  worst = {"max_ulp": -1}
722
  total_hist = {}
723
  per_trial = []
724
  max_abs_seen = 0.0
725
  max_rel_seen = 0.0
 
726
  for t in range(trials):
727
  args = gen(seed + t)
 
 
 
 
728
  r_outs = _as_outputs(ref(*args))
729
  c_outs = _as_outputs(cand(*args))
730
- assert len(r_outs) == len(c_outs), "output arity mismatch"
731
- # pointwise band: each element is judged against its own measured
732
- # order sensitivity (cancellation makes near-zero outputs
733
- # legitimately order-sensitive while neighbors are stable)
 
 
 
 
 
 
 
 
 
 
 
734
  bands = None
735
  band = 0
736
  band_score = 0.0
@@ -754,8 +1078,11 @@ def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
754
  max_ulp = 0
755
  mean_ulp = 0.0
756
  first_div = -1
 
757
  n_el = 0
758
  n_above_band = 0
 
 
759
  nan_r = nan_c = inf_r = inf_c = 0
760
  worst_elements = []
761
  for oi, (r, c) in enumerate(zip(r_outs, c_outs)):
@@ -773,8 +1100,11 @@ def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
773
  mean_ulp += float(d.double().sum().item())
774
  n_el += d.numel()
775
  nz = (d > 0)
 
 
776
  if first_div < 0 and nz.any():
777
  first_div = int(nz.flatten().nonzero()[0].item())
 
778
  if nz.any():
779
  buckets = torch.clamp(
780
  torch.log2(d[nz].double()).floor(), 0, 62
@@ -791,6 +1121,7 @@ def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
791
  if val > 0:
792
  worst_elements.append({
793
  "output": oi, "index": int(ix),
 
794
  "ref": float(rf[ix].item()),
795
  "cand": float(cf[ix].item()),
796
  "ulp": int(val)})
@@ -810,23 +1141,27 @@ def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
810
  inf_r += int(torch.isinf(r).sum().item())
811
  inf_c += int(torch.isinf(c).sum().item())
812
  worst_elements.sort(key=lambda w: -w["ulp"])
 
813
  trial_rep = {
814
  "max_ulp": max_ulp,
815
  "mean_ulp": round(mean_ulp / max(n_el, 1), 3),
816
  "first_divergent_index": first_div,
 
817
  "nan_ref": nan_r, "nan_cand": nan_c,
818
  "inf_ref": inf_r, "inf_cand": inf_c,
819
  "order_band_ulp": band,
820
  "band_score": round(band_score, 2),
821
  "frac_above_band": round(n_above_band / max(n_el, 1), 6),
 
 
822
  "worst_elements": worst_elements[:worst_k],
823
  }
824
  per_trial.append(trial_rep)
825
  if max_ulp > worst["max_ulp"]:
826
  worst = trial_rep
827
- band_max = max(t["order_band_ulp"] for t in per_trial)
828
  max_seen = max(t["max_ulp"] for t in per_trial)
829
- if order_shuffle is not None or ref_variants:
 
830
  score = max(t["band_score"] for t in per_trial)
831
  ulp_ok = score <= band_margin
832
  criterion = (f"pointwise max(ulp / (order_band + 2)) = "
@@ -841,8 +1176,34 @@ def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
841
  max_rel_seen <= (rtol or 0) or max_abs_seen <= (atol or 0)
842
  criterion += (f"; OR max_abs {max_abs_seen:.3e} <= atol {atol} / "
843
  f"max_rel {max_rel_seen:.3e} <= rtol {rtol}")
844
- return {
845
- "pass": bool(ulp_ok or tol_ok),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
846
  "criterion": criterion,
847
  "worst_trial": worst,
848
  "max_abs": max_abs_seen,
@@ -850,6 +1211,9 @@ def compare(ref, cand, gen, trials=8, order_shuffle=None, order_trials=8,
850
  "trials": per_trial,
851
  "ulp_hist_log2": dict(sorted(total_hist.items())),
852
  }
 
 
 
853
 
854
 
855
  _FUZZ_VARIANTS = ("contig", "transposed", "strided_rows", "offset_slice",
@@ -865,6 +1229,30 @@ def _context_alive(device):
865
  return False
866
 
867
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
868
  def _fuzz_build(make, shape, dtype):
869
  try:
870
  out = make(tuple(shape), dtype)
@@ -881,7 +1269,7 @@ def _fuzz_variant(x, v):
881
  if v == "strided_rows":
882
  return x[::2] if x.dim() >= 1 and x.shape[0] >= 2 else x
883
  if v == "offset_slice":
884
- # a storage-offset-1 view: misaligned for vectorized loads
885
  return x.flatten()[1:] if x.numel() > 1 else x
886
  if v == "overflow_expand":
887
  if x.numel() == 0:
@@ -895,8 +1283,7 @@ def _fuzz_variant(x, v):
895
 
896
  def _fuzz_group(op, ref, make, shape, dtype, variants, ulp_tol, device,
897
  stop_on_context_loss=True):
898
- """All variant cases for one (shape, dtype) group; returns (rows,
899
- context_lost)."""
900
  rows = []
901
  bases = _fuzz_build(make, shape, dtype)
902
  lost = False
@@ -933,8 +1320,7 @@ def _fuzz_group(op, ref, make, shape, dtype, variants, ulp_tol, device,
933
  got = op(*xs)
934
  want = ref(*xs)
935
  if v == "overflow_expand":
936
- # bounded comparison: the head plus the high-index tail,
937
- # which is where int32 flat indexing wraps
938
  w = 1 << 20
939
  gf, wf = got.flatten(), want.flatten()
940
  pairs = ((gf[:w], wf[:w]), (gf[-w:], wf[-w:]))
@@ -959,20 +1345,30 @@ def _fuzz_group(op, ref, make, shape, dtype, variants, ulp_tol, device,
959
  return rows, lost
960
 
961
 
962
- # isolate child: a plain subprocess that bootstraps its own metakernel
963
- # (get_kernel, else the dev-root JIT fallback) and unpickles op/ref/make
964
- # by module reference, so it works under get_kernel's synthetic names
965
- _REPO_ID = "phanerozoic/metakernel"
966
- _REPO_VERSION = 1
967
-
968
- _ISOLATE_BOOTSTRAP = r"""
969
- import json
970
- import os
971
- import pickle
972
- import sys
973
-
 
 
 
 
 
 
 
 
 
 
974
  meta = json.load(open(sys.argv[1], "r"))
975
- sys.path[:0] = meta["sys_path"] # BEFORE unpickling module-ref functions
976
  try:
977
  import kernels
978
  _mk = kernels.get_kernel(meta["repo"], version=meta["version"],
@@ -985,85 +1381,165 @@ except Exception:
985
  import load_local
986
  _mk = load_local.load()
987
  import torch
988
- op, ref, make = pickle.load(open(sys.argv[2], "rb"))
989
- rows, _lost = _mk._fuzz_group(
990
- op, ref, make, tuple(meta["shape"]), getattr(torch, meta["dtype"]),
991
- tuple(meta["variants"]), meta["ulp_tol"], meta["device"],
992
- stop_on_context_loss=False)
993
- pickle.dump(rows, open(sys.argv[3], "wb"))
 
 
 
 
 
 
 
 
 
 
 
 
994
  """
995
 
996
 
997
- def fuzz_shapes(op, ref, make, shapes=None, variants=_FUZZ_VARIANTS,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
998
  dtypes=(torch.float32,), ulp_tol=64, isolate=False,
999
- isolate_timeout_s=180, device="cuda"):
1000
- """Run op vs ref across edge shapes, dtypes, and layout mutations;
1001
- report ok / mismatch / exception per case.
1002
-
1003
- `make(shape, dtype)` (or `make(shape)`) builds a tensor or tuple;
1004
- layout variants derive non-contiguous and misaligned views whose
1005
- content equals a contiguous copy. `overflow_expand` stride-0-expands
1006
- past 2**31 logical elements to catch int32 indexing (`skipped_vram`
1007
- when it would not fit). Every case carries a `repro` recipe.
1008
- In-process, the CUDA context is health-checked after exceptions and
1009
- remaining cases marked `context_lost` if killed; `isolate=True`
1010
- spawns each (shape, dtype) group in a subprocess, so a
1011
- context-killing candidate costs one group (`crashed_isolated`);
1012
- op/ref/make must be picklable."""
1013
  if shapes is None:
1014
  shapes = [(), (7,), (0, 8), (1, 1), (1, 17), (3, 63), (17, 129),
1015
  (128, 128), (1023, 5), (2, 4096), (2, 3, 5)]
 
 
1016
 
1017
  rows = []
1018
  lost = False
1019
  if isolate:
1020
  import json
1021
  import pickle
1022
- import subprocess
1023
  import sys as _sys
1024
  import tempfile
1025
  tmpdir = tempfile.mkdtemp(prefix="mk_fuzz_")
1026
  fns = os.path.join(tmpdir, "fns.pkl")
 
 
1027
  with open(fns, "wb") as f:
1028
- pickle.dump((op, ref, make), f)
1029
- for dtype in dtypes:
1030
- for shape in shapes:
1031
- meta = os.path.join(tmpdir, "meta.json")
1032
- out = os.path.join(tmpdir, "rows.pkl")
1033
- if os.path.exists(out):
1034
- os.remove(out)
1035
- with open(meta, "w", encoding="utf-8") as f:
1036
- json.dump({
1037
- "sys_path": [p for p in _sys.path
1038
- if isinstance(p, str)],
1039
- "repo": _REPO_ID, "version": _REPO_VERSION,
 
 
 
 
1040
  "shape": list(shape),
1041
  "dtype": str(dtype).replace("torch.", ""),
1042
  "variants": list(variants),
1043
  "ulp_tol": ulp_tol, "device": device,
1044
- }, f)
1045
- group = None
1046
- stderr_tail = ""
1047
- try:
1048
- r = subprocess.run(
1049
- [_sys.executable, "-c", _ISOLATE_BOOTSTRAP, meta,
1050
- fns, out], timeout=isolate_timeout_s,
1051
- capture_output=True)
1052
- if r.returncode == 0 and os.path.exists(out):
1053
  with open(out, "rb") as f:
1054
- group = pickle.load(f)
1055
- exitcode = r.returncode
1056
- stderr_tail = r.stderr.decode(
1057
- errors="replace")[-400:]
1058
- except subprocess.TimeoutExpired:
1059
- exitcode = "timeout"
1060
- if group is None:
1061
- group = [{"shape": tuple(shape), "variant": "*",
1062
- "dtype": str(dtype).replace("torch.", ""),
1063
- "status": "crashed_isolated",
1064
- "exitcode": exitcode,
1065
- "stderr_tail": stderr_tail}]
1066
- rows.extend(group)
1067
  else:
1068
  pending = [(dt, sh) for dt in dtypes for sh in shapes]
1069
  for gi, (dtype, shape) in enumerate(pending):
@@ -1080,21 +1556,27 @@ def fuzz_shapes(op, ref, make, shapes=None, variants=_FUZZ_VARIANTS,
1080
  n_bad = sum(1 for r in rows
1081
  if r["status"] not in ("ok", "skipped_vram",
1082
  "skipped_empty"))
1083
- return {"cases": rows, "failed": n_bad, "total": len(rows),
1084
- "context_lost": lost}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1085
 
1086
 
1087
  def sweep(factory, grid, iters=30, warmup=5, bytes=None, flops=None,
1088
  check=None, budget_s=None, race=False):
1089
- """Launch-configuration search: `factory(**point) -> callable`; one
1090
- bench row per grid point, sorted fastest first. A point whose factory
1091
- or run raises is reported, not skipped silently. `check(fn) -> bool`
1092
- is an optional correctness gate recorded per row (a fast wrong config
1093
- must not win a sweep). `budget_s` caps total sweep time; points not
1094
- reached are reported as `not_run` rather than silently dropped.
1095
- `race=True` runs successive halving: a quick pass over every point at
1096
- iters/4, then the full budget only on the fastest quartile; pruned
1097
- points keep their quick measurement and status `pruned_race`."""
1098
  keys = list(grid.keys())
1099
 
1100
  def expand(i, acc):
@@ -1159,13 +1641,71 @@ def sweep(factory, grid, iters=30, warmup=5, bytes=None, flops=None,
1159
  continue
1160
  rows.append(run_point(p, iters))
1161
  rows.sort(key=lambda r: (not r.get("check", True), r["median_ms"]))
 
 
 
 
 
 
 
 
 
 
 
1162
  return rows
1163
 
1164
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1165
  def alloc_stamps(n, blocks=None, device="cuda"):
1166
- """Device buffer for the MK_STAMP convention (metakernel_cuda/
1167
- stamps.cuh): the candidate kernel writes clock64() into slot i. With
1168
- `blocks`, allocates the [blocks, n] layout for MK_STAMP_BLOCK."""
1169
  if blocks is not None:
1170
  return torch.zeros(blocks, n, dtype=torch.int64, device=device)
1171
  return torch.zeros(n, dtype=torch.int64, device=device)
@@ -1175,8 +1715,7 @@ _DRIFT_SKIP = ("device", "clock", "launch_overhead", "transfer_bw")
1175
 
1176
 
1177
  def pinned_dossiers():
1178
- """Names of the dossiers pinned in this release (one per certified
1179
- architecture)."""
1180
  return sorted(_PINNED.keys())
1181
 
1182
 
@@ -1195,11 +1734,7 @@ def _numeric_leaves(d, prefix=""):
1195
 
1196
 
1197
  def dossier_drift(current, pinned=None, rel_tol=0.35, skip=_DRIFT_SKIP):
1198
- """Compare a fresh dossier against the release-pinned one for the
1199
- same architecture: per-leaf relative drift, worst drift, entries
1200
- over `rel_tol`. Host-dependent entries and raw clocks are skipped by
1201
- default (`skip=()` compares everything); `pinned=None` auto-selects
1202
- by sm_arch and raises if no pin exists."""
1203
  if pinned is None:
1204
  arch = current["device"]["sm_arch"]
1205
  matches = [k for k, v in _PINNED.items()
@@ -1222,16 +1757,19 @@ def dossier_drift(current, pinned=None, rel_tol=0.35, skip=_DRIFT_SKIP):
1222
  "rel": round(rel, 4)})
1223
  worst = max((e["rel"] for e in entries), default=0.0)
1224
  over = [e for e in entries if e["rel"] > rel_tol]
1225
- return {"entries": entries, "worst_rel": worst, "over_tol": over,
1226
- "rel_tol": rel_tol, "compared": len(entries),
1227
- "pass": len(over) == 0}
 
 
 
 
 
1228
 
1229
 
1230
  def stamped_demo(n=1 << 22, fma_iters=4000, blocks=None, threads=256,
1231
  device="cuda"):
1232
- """Run the shipped MK_STAMP_BLOCK example kernel (global load, FMA
1233
- compute, global store) and decode its stamps; a template for
1234
- instrumenting a candidate."""
1235
  props = torch.cuda.get_device_properties(device)
1236
  if blocks is None:
1237
  blocks = props.multi_processor_count
@@ -1244,11 +1782,7 @@ def stamped_demo(n=1 << 22, fma_iters=4000, blocks=None, threads=256,
1244
 
1245
 
1246
  def read_stamps(stamps, labels=None, clock_ghz=None):
1247
- """Decode MK_STAMP phases: consecutive deltas in ticks and in
1248
- microseconds under the measured (or provided) SM clock. A 1-D buffer
1249
- (MK_STAMP: block 0 only) yields one delta per phase; a 2-D [B, n]
1250
- buffer (MK_STAMP_BLOCK) yields min / median / max across blocks per
1251
- phase, with blocks whose stamps stayed zero excluded."""
1252
  if clock_ghz is None:
1253
  clock_ghz = measure_clock(stamps.device)
1254
 
@@ -1284,3 +1818,104 @@ def read_stamps(stamps, labels=None, clock_ghz=None):
1284
  return {"phases": phases, "clock_ghz": round(clock_ghz, 3),
1285
  "blocks_reporting": int(live.sum().item()),
1286
  "blocks_total": int(live.numel())}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Measuring instrument for the kernel-design loop: measured device facts, throttle-aware timing, adversarial correctness comparison, all as loadable ops returning plain data."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  import math
3
  import os
4
  import time
 
8
  try:
9
  from ._ops import ops
10
  except ImportError: # pragma: no cover - spawned child in a source tree
11
+ # children re-import the package; a source checkout rebuilds the op table from the cached JIT extension
 
 
12
  _root = os.environ.get("METAKERNEL_DEV_ROOT")
13
  if not _root:
14
  raise
 
30
  except ImportError: # pragma: no cover - fresh tree before first pin
31
  _PINNED = {}
32
 
33
+ __all__ = ["probe_device", "probe_all_devices", "bench", "compare", "judge",
34
+ "order_shuffles", "fuzz_shapes", "sweep", "alloc_stamps",
35
+ "read_stamps", "stamped_demo", "measure_clock", "ulp_diff",
36
+ "dossier_drift", "pinned_dossiers", "mma_tile_check", "schema",
37
+ "probe_capturable", "ops"]
38
+
39
+ _REPO_ID = "phanerozoic/metakernel"
40
+ _REPO_VERSION = 1
41
+ _PROBE_SCHEMA = 2
42
+
43
+ # kind id, C-tile M*N, nominal K, nominal K-lanes per unrolled loop iteration
44
  _MMA_KINDS = {
45
+ "fp16": (0, 16 * 16, 16, 64.0),
46
+ "bf16": (1, 16 * 16, 16, 64.0),
47
+ "tf32": (2, 16 * 16, 8, 32.0),
48
+ "int8": (3, 16 * 16, 16, 64.0),
49
+ "fp8_e4m3": (4, 16 * 8, 32, 256.0),
50
+ "fp16_sparse_2_4": (5, 16 * 8, 32, 128.0),
51
+ "fp8_e5m2": (6, 16 * 8, 32, 256.0),
52
  }
53
 
54
 
 
57
 
58
 
59
  def _events(fn, reps):
60
+ """Time reps calls of fn with one cudaEvent pair each; returns a ms list."""
61
  starts = [torch.cuda.Event(enable_timing=True) for _ in range(reps)]
62
  stops = [torch.cuda.Event(enable_timing=True) for _ in range(reps)]
63
  for s, e in zip(starts, stops):
 
69
 
70
 
71
  def measure_clock(device="cuda", ticks=4_000_000, blocks=None):
72
+ """Effective SM clock in GHz from clock64 ticks over wall time under an FMA spin."""
 
73
  props = torch.cuda.get_device_properties(device)
74
  if blocks is None:
75
  blocks = props.multi_processor_count
 
102
 
103
 
104
  def _sattolo_ring(n, device, generator=None):
105
+ """Random single-cycle permutation as an int32 next-pointer table."""
 
106
  perm = torch.randperm(n, generator=generator)
107
  ring = torch.empty(n, dtype=torch.int32)
108
  ring[perm] = torch.roll(perm, -1).to(torch.int32)
 
110
 
111
 
112
  def _smi_settings(index):
113
+ """ECC mode, driver, power limit via nvidia-smi; None fields when unavailable."""
 
114
  import subprocess
115
  try:
116
  out = subprocess.run(
 
130
  return {"ecc": None, "driver": None, "power_limit_w": None}
131
 
132
 
133
+ def _unravel(flat, shape):
134
+ """Multi-dimensional index for a flat index into shape."""
135
+ if not shape:
136
+ return []
137
+ out = []
138
+ for s in reversed(list(shape)):
139
+ out.append(int(flat % s))
140
+ flat //= s
141
+ return list(reversed(out))
142
+
143
+
144
  def mma_tile_check(device="cuda", trials=8):
145
+ """Random small-integer m16n16k16 tiles through the wmma path, compared exactly to the reference GEMM."""
 
 
146
  worst = 0.0
147
  for t in range(trials):
148
  g = torch.Generator(device=device).manual_seed(1000 + t)
 
160
  "trials": trials}
161
 
162
 
163
+ def _tenancy_canary(device, rel_tol=0.10, attempts=3):
164
+ """Fixed-cost triad timed in paired rounds; busy only when every attempt disagrees."""
165
+ n4 = 4 << 20
166
+ a = torch.rand(n4 * 4, device=device)
167
+ b = torch.rand(n4 * 4, device=device)
168
+ c = torch.empty(n4 * 4, device=device)
169
+ ops.mk_triad(a, b, c, 4, 1.5)
170
+ rels = []
171
+ for _ in range(attempts):
172
+ r1 = _median(_events(lambda: ops.mk_triad(a, b, c, 4, 1.5), 3))
173
+ r2 = _median(_events(lambda: ops.mk_triad(a, b, c, 4, 1.5), 3))
174
+ rel = abs(r1 - r2) / min(r1, r2)
175
+ rels.append(round(rel, 4))
176
+ if rel <= rel_tol:
177
+ return False, {"rel_diffs": rels, "rel_tol": rel_tol}
178
+ return True, {"rel_diffs": rels, "rel_tol": rel_tol}
179
+
180
+
181
+ def _cache_dir():
182
+ base = os.environ.get("LOCALAPPDATA") or os.path.expanduser("~/.cache")
183
+ d = os.path.join(base, "metakernel")
184
+ os.makedirs(d, exist_ok=True)
185
+ return d
186
+
187
+
188
+ def _cache_path(device, quick):
189
+ import re
190
+ props = torch.cuda.get_device_properties(device)
191
+ smi = _smi_settings(torch.device(device).index or 0)
192
+ slug = re.sub(r"[^a-z0-9]+", "", props.name.lower())[:24]
193
+ key = "sm%d%d_%s_drv%s_cu%s_q%d_s%d" % (
194
+ props.major, props.minor, slug, smi["driver"] or "na",
195
+ torch.version.cuda, int(quick), _PROBE_SCHEMA)
196
+ return os.path.join(_cache_dir(), key + ".json")
197
+
198
+
199
+ def _mma_calibrate(kind_id, sink):
200
+ """Executed K-lanes per loop iteration from a sub-saturation accumulator read-back, with a linearity check."""
201
+ gains = []
202
+ for cal in (4, 32):
203
+ sink[1] = 0.0
204
+ ops.mk_mma(kind_id, 1, 1, cal, sink)
205
+ _sync()
206
+ gains.append(float(sink[1].item()) / cal)
207
+ linear = gains[0] > 0 and abs(gains[1] - gains[0]) / gains[0] < 0.02
208
+ return gains[0], linear
209
+
210
+
211
+ def _fp8_accum_stall(kind_id, sink, k_exec):
212
+ """Growth curve of the fp8 accumulator; a stall reveals the effective accumulate mantissa width."""
213
+ vals = []
214
+ for it in (32, 64, 128, 256, 512):
215
+ sink[1] = 0.0
216
+ ops.mk_mma(kind_id, 1, 1, it, sink)
217
+ _sync()
218
+ vals.append(float(sink[1].item()) / 2.0)
219
+ if vals[-1] == vals[-2] and vals[-1] < vals[0] * 16 * 0.99:
220
+ bits = int(round(math.log2(vals[-1] / (2.0 * k_exec))))
221
+ return {"stall_element_value": vals[-1],
222
+ "effective_accum_mantissa_bits": bits}
223
+ return {"stall_element_value": None,
224
+ "effective_accum_mantissa_bits": None}
225
+
226
+
227
+ def probe_device(device="cuda", quick=False, cached=True,
228
+ tenancy_check=True):
229
+ """Empirical device dossier; every entry measured by a dedicated probe kernel and stamped with its SM clock."""
230
+ cpath = _cache_path(device, quick)
231
+ if cached and os.path.exists(cpath):
232
+ import json
233
+ return json.load(open(cpath, encoding="utf-8"))
234
+ if tenancy_check:
235
+ busy, ev = _tenancy_canary(device)
236
+ if busy:
237
+ raise RuntimeError("device_busy: canary rounds disagree %s; "
238
+ "idle the device or pass "
239
+ "tenancy_check=False" % ev)
240
  props = torch.cuda.get_device_properties(device)
241
  sm = props.multi_processor_count
242
  l2 = props.L2_cache_size
 
249
  "vram_bytes": props.total_memory,
250
  "torch": torch.__version__,
251
  "cuda_runtime": torch.version.cuda,
252
+ # instrument settings recorded because readings depend on them
 
253
  **_smi_settings(torch.device(device).index or 0),
254
  }
255
  }
 
274
  }
275
  clk = max(traj)
276
 
277
+ # ---- device-memory bandwidth per access width plus read/write/gather
278
+ n4 = 16 << 20
 
279
  if props.total_memory < 4 << 30:
280
  n4 = 4 << 20
281
  a = torch.rand(n4 * 4, device=device)
 
299
  dtype=torch.int32).to(device)
300
  ops.mk_gather(a, idx, c, 256, 0)
301
  ms = _median(_events(lambda: ops.mk_gather(a, idx, c, 256, 0), reps))
 
302
  hbm["random_gather_16B"] = round(
303
  (idx.numel() * 4 + 2 * n4 * 16) / (ms * 1e-3) / 1e9, 1)
304
  d["dram_bw"] = _entry(hbm, "GB/s", "streaming triad c=a+s*b per access "
 
309
  "so its nominal number is the achievable rate for "
310
  "random access, not a line-efficiency claim")
311
 
312
+ # ---- L2 bandwidth over an L2-resident working set
313
  ws = max(l2 // 4, 1 << 20)
314
+ nl2 = ws // 16
315
  al = a[:nl2 * 4].contiguous()
316
  bl = b[:nl2 * 4].contiguous()
317
  cl = c[:nl2 * 4].contiguous()
 
326
  "medium", "first pass streams from HBM; amortized "
327
  f"over {passes} passes")
328
 
329
+ # ---- latencies: min of 3 chases per level so a co-tenant burst cannot land in the dossier
 
 
330
  lat = {}
331
  idle_clk = measure_clock(device, ticks=2_000_000, blocks=1)
 
 
 
 
 
 
 
332
 
333
+ def chase(op, ring, warm_hops, hops, extra=None):
334
+ best = None
335
+ for _ in range(3):
336
+ if extra is None:
337
+ op(ring, warm_hops, out64)
338
+ _sync()
339
+ op(ring, hops, out64)
340
+ else:
341
+ op(ring, extra, warm_hops, out64)
342
+ _sync()
343
+ op(ring, extra, hops, out64)
344
+ _sync()
345
+ t = int(out64[0].item()) / hops
346
+ best = t if best is None else min(best, t)
347
+ return best
 
 
348
 
349
+ ring_s = _sattolo_ring(8192, device)
350
+ lat["smem_cycles"] = round(
351
+ chase(lambda r, h, o: ops.mk_chase_shared(r, h, o), ring_s,
352
+ 1 << 14, 1 << 16), 1)
353
+ ring_l1 = _sattolo_ring((16 << 10) // 4, device)
354
+ lat["l1_cycles"] = round(
355
+ chase(lambda r, h, o: ops.mk_chase_global(r, h, o), ring_l1,
356
+ ring_l1.numel(), 1 << 15), 1)
357
+ ring_l2 = _sattolo_ring((2 << 20) // 4, device)
358
+ lat["l2_cycles"] = round(
359
+ chase(lambda r, h, o: ops.mk_chase_global(r, h, o), ring_l2,
360
+ ring_l2.numel(), 1 << 14), 1)
361
  hbm_bytes = min(max(4 * l2, 64 << 20), props.total_memory // 8)
362
  ring_h = _sattolo_ring(int(hbm_bytes) // 4, device)
363
  hops_h = max((2 * l2) // 128, 1 << 17)
364
+ lat["dram_cycles"] = round(
365
+ chase(lambda r, h, o: ops.mk_chase_global(r, h, o), ring_h,
366
+ 1 << 14, hops_h), 1)
 
 
367
  lat["ns_per_cycle"] = round(1.0 / idle_clk, 3)
368
  d["latency"] = _entry(lat, "cycles (single-lane dependent loads)",
369
+ "Sattolo-cycle pointer chase, min of 3 runs; 16 "
370
+ "KB ring for L1, 2 MB TLB-resident ring for L2, "
371
+ "self-evicting chain (2x L2 in touched lines) "
372
+ "for HBM", idle_clk, "high",
373
  "smem includes index arithmetic; HBM includes "
374
  "residual TLB effects at large footprints")
375
  del ring_h, ring_l2, ring_l1, ring_s
376
 
377
+ # ---- fp32/fp64 FMA and tensor-core throughput
378
  blocks = sm * 4
379
  iters = 20_000 if quick else 60_000
380
  ops.mk_fma_f32(blocks, 256, iters, sink)
381
  ms = _median(_events(lambda: ops.mk_fma_f32(blocks, 256, iters, sink),
382
  reps))
383
+ flops = blocks * 256 * iters * 16 * 2 * 2
384
  d["fma_f32"] = _entry(round(flops / (ms * 1e-3) / 1e12, 2), "TFLOP/s",
385
  "register-resident FMA chains", clk, "high")
386
  sink64 = torch.zeros(2, dtype=torch.float64, device=device)
 
392
  d["fma_f64"] = _entry(round(flops64 / (ms * 1e-3) / 1e12, 3), "TFLOP/s",
393
  "register-resident double FMA chains (the DFMA "
394
  "pipe)", clk, "high")
395
+
396
+ # ---- ILP ladder: 1/2/4 chains at low occupancy separates latency-bound from pipe-bound
397
+ ilp = {}
398
+ it_ilp = 20_000 if quick else 60_000
399
+ for ch in (1, 2, 4):
400
+ ops.mk_fma_ilp(ch, sm, 64, it_ilp, sink)
401
+ ms = _median(_events(
402
+ lambda c2=ch: ops.mk_fma_ilp(c2, sm, 64, it_ilp, sink), reps))
403
+ ilp[f"ilp{ch}"] = round(
404
+ sm * 64 * it_ilp * 16 * 2 * ch / (ms * 1e-3) / 1e12, 2)
405
+ ilp["ratio_4_1"] = round(ilp["ilp4"] / ilp["ilp1"], 2)
406
+ d["fma_ilp"] = _entry(ilp, "TFLOP/s",
407
+ "1/2/4 independent dependent FMA chains per "
408
+ "thread at one block of 64 threads per SM; a "
409
+ "ratio near 4 means latency-bound at that "
410
+ "occupancy", clk, "high")
411
+
412
  mma = {}
413
  mma_klanes = {}
414
+ fp8_accum = {}
415
+ for name, (kind, mn, k_nom, nom_lanes) in _MMA_KINDS.items():
416
  it = 30_000 if quick else 100_000
417
  r = ops.mk_mma(kind, blocks, 8, 512, sink)
418
  if r != 0:
419
  mma[name] = "unsupported"
420
  continue
421
  _sync()
422
+ gain, linear = _mma_calibrate(kind, sink)
 
 
 
 
 
 
423
  mma_klanes[name] = {
424
+ "executed_k_per_iter": round(gain, 2),
425
+ "vs_nominal": round(gain / nom_lanes, 3),
426
+ "calibration_linear": linear,
427
  }
428
+ if name.startswith("fp8"):
429
+ fp8_accum[name] = _fp8_accum_stall(kind, sink, k_nom)
430
  ms = _median(_events(
431
  lambda k=kind, i=it: ops.mk_mma(k, blocks, 8, i, sink), reps))
432
+ tflops = blocks * 8 * it * 2.0 * mn * gain / (ms * 1e-3) / 1e12
433
+ if name == "fp16_sparse_2_4":
434
+ mma[name] = {"executed": round(tflops, 1),
435
+ "dense_equivalent": round(2 * tflops, 1)}
436
+ else:
437
+ mma[name] = round(tflops, 1)
438
  d["mma"] = _entry(mma, "TFLOP/s (TOP/s for int8)",
439
  "register-fragment mma_sync loops (wmma; fp8 and "
440
+ "2:4-sparse fp16 via inline PTX m16n8k32); flops "
441
+ "derive from a sub-saturation accumulator read-back "
 
442
  "with every fragment element observed, never the "
443
+ "declared loop shape; sparse reports executed and "
444
+ "dense-equivalent flops",
445
  clk, "medium",
446
  "dense-tile rate without memory traffic; real "
447
  "kernels see this only when staging hides behind it")
448
  d["mma"]["verified_k_lanes"] = mma_klanes
449
+ if fp8_accum:
450
+ d["mma"]["fp8_accum"] = fp8_accum
451
 
452
+ # ---- fed MMA: smem-staged fp16 tiles under cp.async double buffering
453
+ if props.major >= 8:
454
+ gbuf = torch.ones(16 << 20, dtype=torch.float16, device=device)
455
+ sink[1] = 0.0
456
+ r = ops.mk_mma_fed(gbuf, 1, 1, 64, sink)
457
+ if r == 0:
458
+ _sync()
459
+ fed_gain = float(sink[1].item()) / 64
460
+ it_fed = 6_000 if quick else 20_000
461
+ ops.mk_mma_fed(gbuf, sm * 2, 8, it_fed, sink)
462
+ ms = _median(_events(
463
+ lambda: ops.mk_mma_fed(gbuf, sm * 2, 8, it_fed, sink),
464
+ reps))
465
+ fed = sm * 2 * 8 * it_fed * 2.0 * 256 * fed_gain / \
466
+ (ms * 1e-3) / 1e12
467
+ d["mma_fed"] = _entry(
468
+ round(fed, 1), "TFLOP/s",
469
+ "m16n16k16 fp16 tiles fed from an L2-resident buffer "
470
+ "through cp.async double-buffered shared memory; "
471
+ "accumulator-verified", clk, "high",
472
+ "the achieved counterpart to the register peak: staging "
473
+ "exposed, one dependent accumulator chain per warp")
474
+ del gbuf
475
+
476
+ # ---- shared-memory bandwidth and the bank-conflict stride sweep
477
  it_sm = 30_000 if quick else 100_000
478
  ops.mk_smem_bw(blocks, 256, it_sm, sink)
479
  ms = _median(_events(lambda: ops.mk_smem_bw(blocks, 256, it_sm, sink),
 
483
  "block-resident read+write streaming (8 B per "
484
  "thread-iteration), aggregated across SMs", clk,
485
  "medium",
486
+ "bank-conflict-light access pattern; the stride "
487
+ "sweep below prices the conflicted case")
488
+ strides = (1, 8, 32) if quick else (1, 2, 4, 8, 16, 32)
489
+ it_st = 8_000 if quick else 20_000
490
+ st_ms = {}
491
+ for s2 in strides:
492
+ ops.mk_smem_stride(s2, blocks, 256, it_st, sink)
493
+ st_ms[s2] = _median(_events(
494
+ lambda s3=s2: ops.mk_smem_stride(s3, blocks, 256, it_st, sink),
495
+ reps))
496
+ d["smem_conflict_penalty"] = _entry(
497
+ {f"stride_{s2}": round(st_ms[s2] / st_ms[1], 2) for s2 in strides},
498
+ "x (time ratio vs stride 1)",
499
+ "dependent read-FMA-write per thread at warp-lane bank distance "
500
+ "`stride`; stride 32 puts every lane in one bank", clk, "high",
501
+ "the direct conflict measurement that disambiguates a "
502
+ "bank-conflict signature from an ILP stall")
503
 
504
  # ---- PCIe / host transfer bandwidth
505
  nbytes = 256 << 20
 
530
  "d2d counts one direction of traffic")
531
  del host_pin, host_pag, dev_t, dev_u
532
 
533
+ # ---- atomic contention curve
 
 
534
  n_thr = blocks * 256
535
  buf = torch.zeros(n_thr, dtype=torch.float32, device=device)
536
  at = {"curve_slots": {}}
 
540
  buf.zero_()
541
  ops.mk_atomics(1, blocks, 256, it_at, buf, slots)
542
  ms = _median(_events(
543
+ lambda s4=slots: ops.mk_atomics(1, blocks, 256, it_at, buf, s4),
544
  reps))
545
  at["curve_slots"][str(slots)] = round(
546
  n_thr * it_at / (ms * 1e-3) / 1e9, 2)
 
553
  "of distinct global addresses from 1 (full "
554
  "contention) to one per thread", clk, "high")
555
 
556
+ # ---- launch overhead: torch-dispatch path and the raw C++ path
557
  anchor = sink
558
  n_launch = 500 if quick else 2000
559
  _sync()
 
568
  ops.mk_empty(anchor)
569
  _sync()
570
  rt = (time.perf_counter() - t0) / n_rt * 1e6
571
+ raw_q = float(ops.mk_launch_raw(n_launch, 0, anchor))
572
+ raw_rt = float(ops.mk_launch_raw(n_rt, 1, anchor))
573
  d["launch_overhead"] = _entry(
574
+ {"torch_queued_us": round(queued, 2),
575
+ "torch_round_trip_us": round(rt, 2),
576
+ "raw_queued_us": round(raw_q / n_launch * 1e6, 2)
577
+ if raw_q >= 0 else None,
578
+ "raw_round_trip_us": round(raw_rt / n_rt * 1e6, 2)
579
+ if raw_rt >= 0 else None},
580
+ "us", "empty-kernel trains; torch path per-launch through op "
581
+ "dispatch, raw path looped inside one binding call", clk,
582
+ "medium", "host-state-dependent; the torch-minus-raw gap is the "
583
+ "dispatch cost a torch-launched candidate pays")
584
 
585
  # ---- grid barrier
586
  bar_iters = 500 if quick else 2000
 
596
  "unavailable or grid too large", clk,
597
  "high")
598
 
599
+ # ---- occupancy API table plus timed knees
 
 
600
  occ = {}
601
  for threads in (128, 256, 512, 1024):
602
  row = {}
 
617
  ops.mk_fma_dep(knee_blocks, threads, smem_kb * 1024,
618
  knee_iters, sink)
619
  ms = _median(_events(
620
+ lambda t=threads, s5=smem_kb: ops.mk_fma_dep(
621
+ knee_blocks, t, s5 * 1024, knee_iters, sink), reps))
622
  fl = knee_blocks * threads * knee_iters * 16 * 2
623
  row[f"{smem_kb}KB"] = round(fl / (ms * 1e-3) / 1e12, 2)
624
  knees[f"t{threads}"] = row
 
627
  "at forced (threads, dyn smem) occupancies over fixed total work; "
628
  "the cliff marks where occupancy stops covering latency",
629
  clk, "high", "timing-derived counterpart to the API table above")
630
+ if cached:
631
+ import json
632
+ json.dump(d, open(cpath, "w", encoding="utf-8"))
633
  return d
634
 
635
 
636
+ def probe_all_devices(quick=True, cached=True, tenancy_check=True):
637
+ """One dossier per visible device plus a pairwise P2P bandwidth matrix."""
638
+ n = torch.cuda.device_count()
639
+ devices = {}
640
+ for i in range(n):
641
+ devices[i] = probe_device(f"cuda:{i}", quick=quick, cached=cached,
642
+ tenancy_check=tenancy_check)
643
+ p2p = {}
644
+ if n > 1:
645
+ nbytes = 64 << 20
646
+ for i in range(n):
647
+ for j in range(n):
648
+ if i == j:
649
+ continue
650
+ src = torch.rand(nbytes // 4, device=f"cuda:{i}")
651
+ dst = torch.empty(nbytes // 4, device=f"cuda:{j}")
652
+ dst.copy_(src)
653
+ torch.cuda.synchronize(i)
654
+ torch.cuda.synchronize(j)
655
+ t0 = time.perf_counter()
656
+ for _ in range(5):
657
+ dst.copy_(src)
658
+ torch.cuda.synchronize(i)
659
+ torch.cuda.synchronize(j)
660
+ ms = (time.perf_counter() - t0) / 5 * 1e3
661
+ p2p[f"{i}->{j}"] = {
662
+ "gbs": round(nbytes / (ms * 1e-3) / 1e9, 1),
663
+ "peer_access": bool(
664
+ torch.cuda.can_device_access_peer(i, j)),
665
+ }
666
+ del src, dst
667
+ return {"devices": devices, "p2p": p2p}
668
+
669
+
670
+ _CAPTURE_PROBE = r"""
671
+ import pickle, sys, torch
672
+ fn = pickle.load(open(sys.argv[1], "rb"))
673
+ for _ in range(3):
674
+ fn()
675
+ torch.cuda.synchronize()
676
+ try:
677
+ g = torch.cuda.CUDAGraph()
678
+ with torch.cuda.graph(g):
679
+ fn()
680
+ print("capturable", flush=True)
681
+ except Exception as e:
682
+ print("uncapturable: %s: %s" % (type(e).__name__, str(e)[:120]),
683
+ flush=True)
684
+ """
685
+
686
+
687
+ def probe_capturable(fn, timeout_s=180):
688
+ """Test CUDA-graph capturability of fn in a child process, so a capture-wedged RNG never reaches the caller."""
689
+ import pickle
690
+ import subprocess
691
+ import sys as _sys
692
+ import tempfile
693
+ try:
694
+ blob = pickle.dumps(fn)
695
+ except Exception: # noqa: BLE001
696
+ return {"capturable": None,
697
+ "reason": "fn is not picklable; probe skipped (define fn "
698
+ "at module top level to enable it)"}
699
+ path = os.path.join(tempfile.mkdtemp(prefix="mk_cap_"), "fn.pkl")
700
+ with open(path, "wb") as f:
701
+ f.write(blob)
702
+ try:
703
+ r = subprocess.run([_sys.executable, "-c", _CAPTURE_PROBE, path],
704
+ capture_output=True, text=True,
705
+ timeout=timeout_s)
706
+ except Exception as e: # noqa: BLE001
707
+ return {"capturable": None,
708
+ "reason": f"probe child failed: {type(e).__name__}"}
709
+ out = (r.stdout or "").strip()
710
+ if out == "capturable":
711
+ return {"capturable": True, "reason": ""}
712
+ if out.startswith("uncapturable"):
713
+ return {"capturable": False, "reason": out}
714
+ return {"capturable": None,
715
+ "reason": "probe child exit %s: %s" % (
716
+ r.returncode, (r.stderr or "")[-200:])}
717
+
718
+
719
+ def _bench_verdict(rep):
720
+ parts = ["median %.4g ms" % rep["median_ms"]]
721
+ if "achieved_tflops" in rep:
722
+ parts.append("%.4g TFLOP/s" % rep["achieved_tflops"])
723
+ elif "achieved_gbs" in rep:
724
+ parts.append("%.4g GB/s" % rep["achieved_gbs"])
725
+ if "roofline" in rep:
726
+ parts.append("%.0f%% of %s roof" % (rep["roofline"]["pct_of_roof"],
727
+ rep["roofline"]["bound"]))
728
+ if rep["rejected_throttled"]:
729
+ parts.append("%d/%d rejected (%s)" % (
730
+ rep["rejected_throttled"], rep["samples"],
731
+ rep["rejection_basis"]))
732
+ if rep.get("no_clean_window"):
733
+ parts.append("no clean window: the whole run was throttled")
734
+ if rep.get("alloc_growth_bytes_per_call"):
735
+ parts.append("fn allocates %d B/call" %
736
+ rep["alloc_growth_bytes_per_call"])
737
+ if rep.get("host_overhead_suspected"):
738
+ parts.append("fn appears to sync or do host work inside")
739
+ return "; ".join(parts)
740
+
741
+
742
  def bench(fn, iters=100, warmup=10, bytes=None, flops=None, dossier=None,
743
  dtype="fp16", clock_guard=0.97, clock_stride=8, graph=False,
744
+ graph_probe_isolated=False, device="cuda"):
745
+ """Median-of-N cudaEvent timing with throttle rejection, roofline placement, and footgun detection."""
746
+ for name, v in (("iters", iters), ("warmup", warmup)):
747
+ if not isinstance(v, int) or v < (1 if name == "iters" else 0):
748
+ raise ValueError(f"{name} must be a non-negative int; "
749
+ f"got {v!r}")
750
+ for name, v in (("bytes", bytes), ("flops", flops)):
751
+ if v is not None and (not isinstance(v, (int, float)) or v <= 0):
752
+ raise ValueError(f"{name} must be a positive number counting "
753
+ f"{name} per call of fn; got {v!r}")
 
754
  for _ in range(warmup):
755
  fn()
756
  _sync()
757
+ # footgun probes: per-call allocation growth and hidden host work
758
+ alloc0 = torch.cuda.memory_allocated(device)
759
+ fn()
760
+ fn()
761
+ _sync()
762
+ alloc_growth = max(0, (torch.cuda.memory_allocated(device) - alloc0)
763
+ // 2)
764
+ # a burst of enqueues that leaves the stream idle means fn drains it; only trustworthy above the launch-overhead floor
765
+ for _ in range(16):
766
+ fn()
767
+ stream_idle = bool(torch.cuda.current_stream(device).query())
768
+ _sync()
769
+ t0 = time.perf_counter()
770
+ pre = _events(fn, 4)
771
+ wall_ms = (time.perf_counter() - t0) * 1e3
772
+ ev_ms = sum(pre)
773
+ host_suspect = (stream_idle and ev_ms / 4 > 0.05) or \
774
+ wall_ms > ev_ms * 1.5 + 2.0
775
+
776
  graph_state = "off"
777
  graph_reason = ""
778
+ if graph and graph_probe_isolated:
779
+ cap = probe_capturable(fn)
780
+ if cap["capturable"] is False:
781
+ graph = False
782
+ graph_state = "fallback_eager"
783
+ graph_reason = "isolated probe: " + cap["reason"]
784
  if graph:
785
  pre_rng = torch.cuda.get_rng_state(device)
786
  try:
 
795
  except Exception as e: # noqa: BLE001 - fall back, say why
796
  graph_state = "fallback_eager"
797
  graph_reason = f"{type(e).__name__}: {str(e)[:120]}"
798
+ # drain the invalidated stream; a failed capture can wedge the device RNG on some torch versions
 
 
 
799
  rng_ok = True
800
  for _attempt in range(5):
801
  try:
 
825
  samples.append((_events(fn, n), len(clocks) - 1))
826
  clocks.append(measure_clock(device, ticks=probe_ticks))
827
  best_clk = max(clocks)
828
+
829
+ # rejection basis: memory-bound work is judged on its own rate, not the SM clock
830
+ ridge_memory = False
831
+ if dossier is not None and bytes and flops:
832
+ bw0 = max(dossier["dram_bw"]["value"].values()) * 1e9
833
+ mma_v0 = dossier["mma"]["value"].get(dtype)
834
+ if isinstance(mma_v0, dict):
835
+ mma_v0 = mma_v0.get("dense_equivalent")
836
+ peak0 = (mma_v0 if isinstance(mma_v0, (int, float))
837
+ else dossier["fma_f32"]["value"]) * 1e12
838
+ ridge_memory = (flops / bytes) * bw0 < peak0
839
+ basis = "bandwidth_consistency" if (
840
+ bytes is not None and (flops is None or ridge_memory)) \
841
+ else "sm_clock"
842
+
843
  flat = []
844
+ kept = []
845
+ rejected = 0
846
+ if basis == "sm_clock":
847
+ for group, ci in samples:
848
+ window_ok = (clocks[ci] >= clock_guard * best_clk and
849
+ clocks[min(ci + 1, len(clocks) - 1)] >=
850
+ clock_guard * best_clk)
851
+ for ms in group:
852
+ flat.append(ms)
853
+ if window_ok:
854
+ kept.append(ms)
855
+ else:
856
+ rejected += 1
857
+ else:
858
+ for group, _ci in samples:
859
+ flat.extend(group)
860
+ floor = sorted(flat)[max(0, int(0.05 * len(flat)) - 1)]
861
+ for ms in flat:
862
+ if ms <= floor * 1.15:
863
  kept.append(ms)
864
  else:
865
  rejected += 1
866
+ all_rejected = not kept
867
+ if all_rejected:
868
  kept = flat
869
  med = _median(kept)
870
  ks = sorted(kept)
871
  n_k = len(ks)
872
+ # order-statistic 95% CI on the median; degenerate at tiny n
 
873
  half = 1.959964 * (n_k ** 0.5) / 2.0
874
  lo_i = max(0, int(n_k / 2 - half) - 1)
875
  hi_i = min(n_k - 1, int(n_k / 2 + half))
 
884
  "mean_ms": round(sum(kept) / n_k, 4),
885
  "samples": len(flat),
886
  "rejected_throttled": rejected,
887
+ "rejection_basis": basis,
888
  "graph_captured": graph_state == "captured",
889
  "clock_ghz": {"best": round(best_clk, 3),
890
  "worst": round(min(clocks), 3)},
891
  }
892
+ no_clean = all_rejected
893
+ if basis == "sm_clock" and dossier is not None:
894
+ ref_clk = dossier["clock"]["max_ghz"]
895
+ if best_clk < clock_guard * ref_clk:
896
+ no_clean = True
897
+ if no_clean:
898
+ rep["no_clean_window"] = True
899
  if graph_reason:
900
  rep["graph_fallback_reason"] = graph_reason
901
+ if alloc_growth:
902
+ rep["alloc_growth_bytes_per_call"] = int(alloc_growth)
903
+ if host_suspect:
904
+ rep["host_overhead_suspected"] = True
905
+ rep["host_wall_over_event"] = round(wall_ms / max(ev_ms, 1e-9), 2)
906
  if bytes is not None:
907
  rep["achieved_gbs"] = round(bytes / (med * 1e-3) / 1e9, 1)
908
  if flops is not None:
 
912
  if dossier is not None and bytes and flops:
913
  bw = max(dossier["dram_bw"]["value"].values()) * 1e9
914
  mma_v = dossier["mma"]["value"].get(dtype)
915
+ if isinstance(mma_v, dict):
916
+ mma_v = mma_v.get("dense_equivalent")
917
  peak = (mma_v if isinstance(mma_v, (int, float))
918
  else dossier["fma_f32"]["value"]) * 1e12
919
  ai = flops / bytes
 
924
  "pct_of_roof": round(100 * (flops / (med * 1e-3)) / roof, 1),
925
  "peak_dtype": dtype,
926
  }
927
+ rep["verdict"] = _bench_verdict(rep)
928
  return rep
929
 
930
 
931
  def _to_ordered_int(t):
932
+ """Monotone integer mapping of float bit patterns; differences are ULP distances."""
 
 
933
  if t.dtype == torch.float32:
934
  u = t.view(torch.int32).to(torch.int64) & 0xFFFFFFFF
935
  half = 1 << 31
 
945
 
946
 
947
  def ulp_diff(a, b):
948
+ """Elementwise ULP distance; both-NaN counts 0, one-sided NaN 2**62, integers by absolute difference."""
949
+ if a.dtype != b.dtype or a.shape != b.shape:
950
+ raise ValueError("ulp_diff needs matching dtype and shape; got "
951
+ f"{a.dtype}{tuple(a.shape)} vs "
952
+ f"{b.dtype}{tuple(b.shape)}; cast or reshape the "
953
+ "candidate to the reference layout")
954
  if not a.is_floating_point():
955
  return (a.to(torch.int64) - b.to(torch.int64)).abs()
956
  ia, ib = _to_ordered_int(a), _to_ordered_int(b)
 
967
 
968
  @staticmethod
969
  def last_dim():
970
+ """Identical permutation of every arg's last dim: reduction order changes, value does not."""
 
 
971
  def shuffle(args, seed):
972
  g = torch.Generator(device=args[0].device).manual_seed(seed)
973
  p = torch.randperm(args[0].shape[-1], device=args[0].device,
 
977
 
978
  @staticmethod
979
  def matmul_k():
980
+ """Consistent K permutation of (A [.., M, K], B [.., K, N]): product unchanged, accumulation order not."""
 
981
  def shuffle(args, seed):
982
  a_m, b_m = args[0], args[1]
983
  g = torch.Generator(device=a_m.device).manual_seed(seed)
 
991
  return list(x) if isinstance(x, (tuple, list)) else [x]
992
 
993
 
994
+ def _compare_hints(trial):
995
+ hints = []
996
+ if trial.get("bias_one_sided_frac", 0) >= 0.9 and \
997
+ trial.get("frac_above_band", 0) > 0.5:
998
+ hints.append("systematic one-sided bias: check scale factors or "
999
+ "the accumulator dtype")
1000
+ if trial["nan_cand"] > trial["nan_ref"]:
1001
+ hints.append("candidate introduces NaN: check uninitialized "
1002
+ "memory or masking")
1003
+ nd = trial.get("first_divergent_nd")
1004
+ if nd and nd[-1] > 0 and any(nd[-1] % t == 0 for t in (16, 32, 64,
1005
+ 128)):
1006
+ hints.append("divergence starts on a tile-aligned index: check "
1007
+ "boundary indexing")
1008
+ return hints
1009
+
1010
+
1011
+ def compare(ref, cand, gen=None, trials=8, order_shuffle=None,
1012
+ order_trials=8, ref_variants=None, ulp_tol=None, atol=None,
1013
+ rtol=None, band_margin=4.0, zero_atol=None, worst_k=8, seed=0,
1014
+ inputs=None, frac_above_tol=0.5):
1015
+ """Adversarial correctness comparison with pointwise order bands, ULP evidence, and a one-sided-bias gate; the banded route cannot reject errors under band_margin*(band+2) ULP at order-stable elements."""
1016
+ if (gen is None) == (inputs is None):
1017
+ raise ValueError("pass exactly one of gen (a seed -> args "
1018
+ "callable) or inputs (a tuple of concrete "
1019
+ "tensors)")
1020
+ if inputs is not None:
1021
+ if isinstance(inputs, torch.Tensor):
1022
+ inputs = (inputs,)
1023
+ if not all(isinstance(x, torch.Tensor) for x in inputs):
1024
+ raise ValueError("inputs must be tensors; wrap scalars with "
1025
+ "torch.tensor first")
1026
+ fixed = tuple(inputs)
1027
+ gen = lambda seed: fixed # noqa: E731
1028
+ trials = 1
1029
  worst = {"max_ulp": -1}
1030
  total_hist = {}
1031
  per_trial = []
1032
  max_abs_seen = 0.0
1033
  max_rel_seen = 0.0
1034
+ out_shapes = []
1035
  for t in range(trials):
1036
  args = gen(seed + t)
1037
+ if not isinstance(args, (tuple, list)):
1038
+ raise ValueError("gen(seed) must return a tuple of args; got "
1039
+ f"{type(args).__name__}; return (x,) for a "
1040
+ "single-argument op")
1041
  r_outs = _as_outputs(ref(*args))
1042
  c_outs = _as_outputs(cand(*args))
1043
+ if len(r_outs) != len(c_outs):
1044
+ raise ValueError(f"reference returned {len(r_outs)} outputs, "
1045
+ f"candidate {len(c_outs)}; make the arities "
1046
+ "match")
1047
+ for oi, (r, c) in enumerate(zip(r_outs, c_outs)):
1048
+ if r.dtype != c.dtype:
1049
+ raise ValueError(f"output {oi} dtype mismatch: ref "
1050
+ f"{r.dtype}, cand {c.dtype}; cast the "
1051
+ "candidate to the reference dtype")
1052
+ if r.shape != c.shape:
1053
+ raise ValueError(f"output {oi} shape mismatch: ref "
1054
+ f"{tuple(r.shape)}, cand "
1055
+ f"{tuple(c.shape)}")
1056
+ out_shapes = [tuple(r.shape) for r in r_outs]
1057
+ # pointwise band: each element judged against its own measured order sensitivity
1058
  bands = None
1059
  band = 0
1060
  band_score = 0.0
 
1078
  max_ulp = 0
1079
  mean_ulp = 0.0
1080
  first_div = -1
1081
+ first_div_nd = None
1082
  n_el = 0
1083
  n_above_band = 0
1084
+ n_diverging = 0
1085
+ n_cand_high = 0
1086
  nan_r = nan_c = inf_r = inf_c = 0
1087
  worst_elements = []
1088
  for oi, (r, c) in enumerate(zip(r_outs, c_outs)):
 
1100
  mean_ulp += float(d.double().sum().item())
1101
  n_el += d.numel()
1102
  nz = (d > 0)
1103
+ n_diverging += int(nz.sum().item())
1104
+ n_cand_high += int(((c > r) & nz).sum().item())
1105
  if first_div < 0 and nz.any():
1106
  first_div = int(nz.flatten().nonzero()[0].item())
1107
+ first_div_nd = _unravel(first_div, r.shape)
1108
  if nz.any():
1109
  buckets = torch.clamp(
1110
  torch.log2(d[nz].double()).floor(), 0, 62
 
1121
  if val > 0:
1122
  worst_elements.append({
1123
  "output": oi, "index": int(ix),
1124
+ "index_nd": _unravel(int(ix), r.shape),
1125
  "ref": float(rf[ix].item()),
1126
  "cand": float(cf[ix].item()),
1127
  "ulp": int(val)})
 
1141
  inf_r += int(torch.isinf(r).sum().item())
1142
  inf_c += int(torch.isinf(c).sum().item())
1143
  worst_elements.sort(key=lambda w: -w["ulp"])
1144
+ frac_pos = n_cand_high / max(n_diverging, 1)
1145
  trial_rep = {
1146
  "max_ulp": max_ulp,
1147
  "mean_ulp": round(mean_ulp / max(n_el, 1), 3),
1148
  "first_divergent_index": first_div,
1149
+ "first_divergent_nd": first_div_nd,
1150
  "nan_ref": nan_r, "nan_cand": nan_c,
1151
  "inf_ref": inf_r, "inf_cand": inf_c,
1152
  "order_band_ulp": band,
1153
  "band_score": round(band_score, 2),
1154
  "frac_above_band": round(n_above_band / max(n_el, 1), 6),
1155
+ "bias_one_sided_frac": round(max(frac_pos, 1 - frac_pos), 4)
1156
+ if n_diverging else 0.0,
1157
  "worst_elements": worst_elements[:worst_k],
1158
  }
1159
  per_trial.append(trial_rep)
1160
  if max_ulp > worst["max_ulp"]:
1161
  worst = trial_rep
 
1162
  max_seen = max(t["max_ulp"] for t in per_trial)
1163
+ banded = order_shuffle is not None or ref_variants
1164
+ if banded:
1165
  score = max(t["band_score"] for t in per_trial)
1166
  ulp_ok = score <= band_margin
1167
  criterion = (f"pointwise max(ulp / (order_band + 2)) = "
 
1176
  max_rel_seen <= (rtol or 0) or max_abs_seen <= (atol or 0)
1177
  criterion += (f"; OR max_abs {max_abs_seen:.3e} <= atol {atol} / "
1178
  f"max_rel {max_rel_seen:.3e} <= rtol {rtol}")
1179
+ # one-sided bias gate: a uniform small error is above-band nearly everywhere in one direction
1180
+ bias_fail = False
1181
+ if banded and (ulp_ok or tol_ok):
1182
+ w_frac = max(t["frac_above_band"] for t in per_trial)
1183
+ w_side = max(t["bias_one_sided_frac"] for t in per_trial)
1184
+ if w_frac > frac_above_tol and w_side >= 0.9:
1185
+ bias_fail = True
1186
+ criterion += ("; FAILED bias gate: frac_above_band %.3f > %s "
1187
+ "with one-sided fraction %.2f" %
1188
+ (w_frac, frac_above_tol, w_side))
1189
+ passed = bool((ulp_ok or tol_ok) and not bias_fail)
1190
+ hints = [] if passed else _compare_hints(worst)
1191
+ if passed:
1192
+ verdict = "pass: " + (
1193
+ f"max {max_seen} ULP within the order band" if banded
1194
+ else f"max {max_seen} ULP")
1195
+ elif bias_fail:
1196
+ verdict = ("fail: uniform bias, %.0f%% of elements above their "
1197
+ "order band in one direction" %
1198
+ (100 * max(t["frac_above_band"] for t in per_trial)))
1199
+ else:
1200
+ w0 = worst["worst_elements"][0] if worst["worst_elements"] else None
1201
+ verdict = "fail: max %d ULP" % max_seen + (
1202
+ " at index %s (ref %.6g, cand %.6g)" %
1203
+ (w0["index_nd"], w0["ref"], w0["cand"]) if w0 else "")
1204
+ rep = {
1205
+ "pass": passed,
1206
+ "verdict": verdict,
1207
  "criterion": criterion,
1208
  "worst_trial": worst,
1209
  "max_abs": max_abs_seen,
 
1211
  "trials": per_trial,
1212
  "ulp_hist_log2": dict(sorted(total_hist.items())),
1213
  }
1214
+ if hints:
1215
+ rep["hints"] = hints
1216
+ return rep
1217
 
1218
 
1219
  _FUZZ_VARIANTS = ("contig", "transposed", "strided_rows", "offset_slice",
 
1229
  return False
1230
 
1231
 
1232
+ class _RandnMaker:
1233
+ """Picklable default maker: `arity` same-shape randn tensors (zeros for empty shapes)."""
1234
+
1235
+ def __init__(self, arity=1):
1236
+ self.arity = arity
1237
+
1238
+ def __call__(self, shape, dtype=torch.float32):
1239
+ n = math.prod(shape)
1240
+ if n:
1241
+ out = tuple(torch.randn(tuple(shape), device="cuda",
1242
+ dtype=dtype)
1243
+ for _ in range(self.arity))
1244
+ else:
1245
+ out = tuple(torch.zeros(tuple(shape), device="cuda",
1246
+ dtype=dtype)
1247
+ for _ in range(self.arity))
1248
+ return out if self.arity > 1 else out[0]
1249
+
1250
+
1251
+ def _default_make(arity=1):
1252
+ """Default maker instance; a class so isolate=True can transport it."""
1253
+ return _RandnMaker(arity)
1254
+
1255
+
1256
  def _fuzz_build(make, shape, dtype):
1257
  try:
1258
  out = make(tuple(shape), dtype)
 
1269
  if v == "strided_rows":
1270
  return x[::2] if x.dim() >= 1 and x.shape[0] >= 2 else x
1271
  if v == "offset_slice":
1272
+ # storage-offset-1 view: misaligned for vectorized loads
1273
  return x.flatten()[1:] if x.numel() > 1 else x
1274
  if v == "overflow_expand":
1275
  if x.numel() == 0:
 
1283
 
1284
  def _fuzz_group(op, ref, make, shape, dtype, variants, ulp_tol, device,
1285
  stop_on_context_loss=True):
1286
+ """All variant cases for one (shape, dtype) group; returns (rows, context_lost)."""
 
1287
  rows = []
1288
  bases = _fuzz_build(make, shape, dtype)
1289
  lost = False
 
1320
  got = op(*xs)
1321
  want = ref(*xs)
1322
  if v == "overflow_expand":
1323
+ # bounded comparison: head plus the high-index tail where int32 indexing wraps
 
1324
  w = 1 << 20
1325
  gf, wf = got.flatten(), want.flatten()
1326
  pairs = ((gf[:w], wf[:w]), (gf[-w:], wf[-w:]))
 
1345
  return rows, lost
1346
 
1347
 
1348
+ def _fn_spec(fn, name):
1349
+ """Picklable transport spec for a callable, with a source fallback for local defs."""
1350
+ import pickle
1351
+ try:
1352
+ return ("pickle", pickle.dumps(fn), None)
1353
+ except Exception: # noqa: BLE001
1354
+ import inspect
1355
+ import textwrap
1356
+ try:
1357
+ src = textwrap.dedent(inspect.getsource(fn))
1358
+ except (OSError, TypeError):
1359
+ src = ""
1360
+ if not src.startswith("def "):
1361
+ raise ValueError(
1362
+ f"{name} is not picklable and not a plain def; define "
1363
+ "op/ref/make as top-level functions (not lambdas or "
1364
+ "closures) to use isolate=True") from None
1365
+ return ("source", src, fn.__name__)
1366
+
1367
+
1368
+ _POOL_BOOTSTRAP = r"""
1369
+ import json, os, pickle, sys
1370
  meta = json.load(open(sys.argv[1], "r"))
1371
+ sys.path[:0] = meta["sys_path"]
1372
  try:
1373
  import kernels
1374
  _mk = kernels.get_kernel(meta["repo"], version=meta["version"],
 
1381
  import load_local
1382
  _mk = load_local.load()
1383
  import torch
1384
+ _ns = {"torch": torch}
1385
+ def _resolve(spec):
1386
+ kind, payload, name = spec
1387
+ if kind == "pickle":
1388
+ return pickle.loads(payload)
1389
+ exec(payload, _ns)
1390
+ return _ns[name]
1391
+ _op, _ref, _make = (_resolve(s) for s in
1392
+ pickle.load(open(meta["fns"], "rb")))
1393
+ print("ready", flush=True)
1394
+ for line in sys.stdin:
1395
+ job = json.loads(line)
1396
+ rows, _l = _mk._fuzz_group(
1397
+ _op, _ref, _make, tuple(job["shape"]),
1398
+ getattr(torch, job["dtype"]), tuple(job["variants"]),
1399
+ job["ulp_tol"], job["device"], stop_on_context_loss=False)
1400
+ pickle.dump(rows, open(job["out"], "wb"))
1401
+ print("done", flush=True)
1402
  """
1403
 
1404
 
1405
+ class _FuzzPool:
1406
+ """One persistent isolation worker fed groups over stdin, respawned only after a crash."""
1407
+
1408
+ def __init__(self, meta_path, stderr_path, timeout_s):
1409
+ self.meta_path = meta_path
1410
+ self.stderr_path = stderr_path
1411
+ self.timeout_s = timeout_s
1412
+ self.proc = None
1413
+ self.q = None
1414
+
1415
+ def _spawn(self):
1416
+ import queue
1417
+ import subprocess
1418
+ import sys as _sys
1419
+ import threading
1420
+ self.errf = open(self.stderr_path, "ab")
1421
+ self.proc = subprocess.Popen(
1422
+ [_sys.executable, "-c", _POOL_BOOTSTRAP, self.meta_path],
1423
+ stdin=subprocess.PIPE, stdout=subprocess.PIPE,
1424
+ stderr=self.errf, text=True)
1425
+ self.q = queue.Queue()
1426
+
1427
+ def reader(p, q):
1428
+ for line in p.stdout:
1429
+ q.put(line.strip())
1430
+ threading.Thread(target=reader, args=(self.proc, self.q),
1431
+ daemon=True).start()
1432
+ try:
1433
+ first = self.q.get(timeout=self.timeout_s)
1434
+ except Exception: # noqa: BLE001
1435
+ first = None
1436
+ return first == "ready"
1437
+
1438
+ def run(self, job_line):
1439
+ """Returns True when the worker reports done, False on crash or timeout."""
1440
+ import queue
1441
+ if self.proc is None or self.proc.poll() is not None:
1442
+ if not self._spawn():
1443
+ return False
1444
+ try:
1445
+ self.proc.stdin.write(job_line + "\n")
1446
+ self.proc.stdin.flush()
1447
+ except OSError:
1448
+ return False
1449
+ try:
1450
+ return self.q.get(timeout=self.timeout_s) == "done"
1451
+ except queue.Empty:
1452
+ if self.proc.poll() is None:
1453
+ self.proc.kill()
1454
+ return False
1455
+
1456
+ def stderr_tail(self):
1457
+ try:
1458
+ self.errf.flush()
1459
+ with open(self.stderr_path, "rb") as f:
1460
+ return f.read()[-400:].decode(errors="replace")
1461
+ except OSError:
1462
+ return ""
1463
+
1464
+ def close(self):
1465
+ if self.proc is not None and self.proc.poll() is None:
1466
+ self.proc.kill()
1467
+ try:
1468
+ self.errf.close()
1469
+ except Exception: # noqa: BLE001
1470
+ pass
1471
+
1472
+
1473
+ def _fuzz_hints(rows):
1474
+ hints = []
1475
+ bad = [c for c in rows if c["status"] == "mismatch"]
1476
+ if bad and all(c["variant"] in ("transposed", "strided_rows",
1477
+ "offset_slice") for c in bad):
1478
+ hints.append("mismatches only on non-contiguous variants: the op "
1479
+ "assumes contiguity; call .contiguous() or honor "
1480
+ "strides")
1481
+ if any(c["variant"] == "overflow_expand" and
1482
+ c["status"] == "mismatch" for c in rows):
1483
+ hints.append("overflow_expand mismatch: int32 indexing wraps past "
1484
+ "2^31 elements; use 64-bit index arithmetic")
1485
+ return hints
1486
+
1487
+
1488
+ def fuzz_shapes(op, ref, make=None, shapes=None, variants=_FUZZ_VARIANTS,
1489
  dtypes=(torch.float32,), ulp_tol=64, isolate=False,
1490
+ isolate_timeout_s=180, device="cuda", arity=1):
1491
+ """Edge shapes, dtypes, and layout mutations with per-case repro recipes; isolate=True survives context-killing candidates via a pooled worker."""
 
 
 
 
 
 
 
 
 
 
 
 
1492
  if shapes is None:
1493
  shapes = [(), (7,), (0, 8), (1, 1), (1, 17), (3, 63), (17, 129),
1494
  (128, 128), (1023, 5), (2, 4096), (2, 3, 5)]
1495
+ if make is None:
1496
+ make = _default_make(arity)
1497
 
1498
  rows = []
1499
  lost = False
1500
  if isolate:
1501
  import json
1502
  import pickle
 
1503
  import sys as _sys
1504
  import tempfile
1505
  tmpdir = tempfile.mkdtemp(prefix="mk_fuzz_")
1506
  fns = os.path.join(tmpdir, "fns.pkl")
1507
+ specs = [_fn_spec(f, n) for f, n in ((op, "op"), (ref, "ref"),
1508
+ (make, "make"))]
1509
  with open(fns, "wb") as f:
1510
+ pickle.dump(specs, f)
1511
+ meta_path = os.path.join(tmpdir, "meta.json")
1512
+ with open(meta_path, "w", encoding="utf-8") as f:
1513
+ json.dump({"sys_path": [p for p in _sys.path
1514
+ if isinstance(p, str)],
1515
+ "repo": _REPO_ID, "version": _REPO_VERSION,
1516
+ "fns": fns}, f)
1517
+ pool = _FuzzPool(meta_path, os.path.join(tmpdir, "stderr.log"),
1518
+ isolate_timeout_s)
1519
+ try:
1520
+ for dtype in dtypes:
1521
+ for shape in shapes:
1522
+ out = os.path.join(tmpdir, "rows.pkl")
1523
+ if os.path.exists(out):
1524
+ os.remove(out)
1525
+ job = json.dumps({
1526
  "shape": list(shape),
1527
  "dtype": str(dtype).replace("torch.", ""),
1528
  "variants": list(variants),
1529
  "ulp_tol": ulp_tol, "device": device,
1530
+ "out": out})
1531
+ ok = pool.run(job)
1532
+ if ok and os.path.exists(out):
 
 
 
 
 
 
1533
  with open(out, "rb") as f:
1534
+ rows.extend(pickle.load(f))
1535
+ else:
1536
+ rows.append({
1537
+ "shape": tuple(shape), "variant": "*",
1538
+ "dtype": str(dtype).replace("torch.", ""),
1539
+ "status": "crashed_isolated",
1540
+ "stderr_tail": pool.stderr_tail()})
1541
+ finally:
1542
+ pool.close()
 
 
 
 
1543
  else:
1544
  pending = [(dt, sh) for dt in dtypes for sh in shapes]
1545
  for gi, (dtype, shape) in enumerate(pending):
 
1556
  n_bad = sum(1 for r in rows
1557
  if r["status"] not in ("ok", "skipped_vram",
1558
  "skipped_empty"))
1559
+ rep = {"cases": rows, "failed": n_bad, "total": len(rows),
1560
+ "context_lost": lost}
1561
+ hints = _fuzz_hints(rows)
1562
+ if hints:
1563
+ rep["hints"] = hints
1564
+ if n_bad == 0:
1565
+ rep["verdict"] = "pass: %d/%d cases ok" % (len(rows) - n_bad,
1566
+ len(rows))
1567
+ else:
1568
+ worst_case = next(c for c in rows
1569
+ if c["status"] not in ("ok", "skipped_vram",
1570
+ "skipped_empty"))
1571
+ rep["verdict"] = "fail: %d/%d cases (%s at %s %s)" % (
1572
+ n_bad, len(rows), worst_case["status"], worst_case["shape"],
1573
+ worst_case["variant"])
1574
+ return rep
1575
 
1576
 
1577
  def sweep(factory, grid, iters=30, warmup=5, bytes=None, flops=None,
1578
  check=None, budget_s=None, race=False):
1579
+ """Launch-configuration search, fastest first, with a correctness gate per row and no silently dropped points."""
 
 
 
 
 
 
 
 
1580
  keys = list(grid.keys())
1581
 
1582
  def expand(i, acc):
 
1641
  continue
1642
  rows.append(run_point(p, iters))
1643
  rows.sort(key=lambda r: (not r.get("check", True), r["median_ms"]))
1644
+ if rows:
1645
+ n_pruned = sum(1 for r in rows if r.get("status") == "pruned_race")
1646
+ n_skip = sum(1 for r in rows if r.get("status") == "not_run")
1647
+ best = rows[0]
1648
+ rows_verdict = "fastest %s at %.4g ms" % (
1649
+ {k: best[k] for k in keys}, best["median_ms"])
1650
+ if n_pruned:
1651
+ rows_verdict += "; %d pruned" % n_pruned
1652
+ if n_skip:
1653
+ rows_verdict += "; %d not run" % n_skip
1654
+ best["verdict"] = rows_verdict
1655
  return rows
1656
 
1657
 
1658
+ def judge(cand, ref, inputs=None, gen=None, bytes=None, flops=None,
1659
+ dtype=None, order_shuffle=None, make=None, quick_fuzz=True,
1660
+ device="cuda"):
1661
+ """One-call verdict: compare, a quick fuzz, and a roofline bench against the cached dossier."""
1662
+ if (gen is None) == (inputs is None):
1663
+ raise ValueError("pass exactly one of inputs (concrete tensors) "
1664
+ "or gen (seed -> args)")
1665
+ if inputs is not None and isinstance(inputs, torch.Tensor):
1666
+ inputs = (inputs,)
1667
+ args0 = tuple(inputs) if inputs is not None else gen(0)
1668
+ arity = len(args0)
1669
+ if dtype is None:
1670
+ dt0 = args0[0].dtype
1671
+ dtype = {torch.float16: "fp16", torch.bfloat16: "bf16"}.get(
1672
+ dt0, "f32")
1673
+ dossier = probe_device(device, quick=True, cached=True,
1674
+ tenancy_check=False)
1675
+ cmp_rep = compare(ref, cand, gen=gen, inputs=inputs, trials=4,
1676
+ order_shuffle=order_shuffle)
1677
+ fuzz_rep = None
1678
+ if quick_fuzz:
1679
+ fuzz_rep = fuzz_shapes(
1680
+ cand, ref, make=make, arity=arity,
1681
+ shapes=[(1, 17), (3, 63), (128, 128)],
1682
+ variants=("contig", "transposed", "offset_slice"),
1683
+ dtypes=(args0[0].dtype,))
1684
+ bench_rep = None
1685
+ if inputs is not None:
1686
+ bench_rep = bench(lambda: cand(*inputs), iters=32, warmup=6,
1687
+ bytes=bytes, flops=flops, dossier=dossier,
1688
+ dtype=dtype, device=device)
1689
+ passed = cmp_rep["pass"] and (fuzz_rep is None or
1690
+ fuzz_rep["failed"] == 0)
1691
+ parts = [cmp_rep["verdict"]]
1692
+ if fuzz_rep is not None:
1693
+ parts.append(fuzz_rep["verdict"])
1694
+ if bench_rep is not None:
1695
+ parts.append(bench_rep["verdict"])
1696
+ rep = {"pass": passed, "verdict" : " | ".join(parts),
1697
+ "compare": cmp_rep, "fuzz": fuzz_rep, "bench": bench_rep,
1698
+ "device": dossier["device"]["name"]}
1699
+ hints = list(cmp_rep.get("hints", []))
1700
+ if fuzz_rep is not None:
1701
+ hints += fuzz_rep.get("hints", [])
1702
+ if hints:
1703
+ rep["hints"] = hints
1704
+ return rep
1705
+
1706
+
1707
  def alloc_stamps(n, blocks=None, device="cuda"):
1708
+ """Device buffer for MK_STAMP (1-D) or MK_STAMP_BLOCK ([blocks, n]) stamps."""
 
 
1709
  if blocks is not None:
1710
  return torch.zeros(blocks, n, dtype=torch.int64, device=device)
1711
  return torch.zeros(n, dtype=torch.int64, device=device)
 
1715
 
1716
 
1717
  def pinned_dossiers():
1718
+ """Names of the release-pinned dossiers, one per certified architecture."""
 
1719
  return sorted(_PINNED.keys())
1720
 
1721
 
 
1734
 
1735
 
1736
  def dossier_drift(current, pinned=None, rel_tol=0.35, skip=_DRIFT_SKIP):
1737
+ """Per-leaf relative drift of a fresh dossier against the release pin for the same architecture."""
 
 
 
 
1738
  if pinned is None:
1739
  arch = current["device"]["sm_arch"]
1740
  matches = [k for k, v in _PINNED.items()
 
1757
  "rel": round(rel, 4)})
1758
  worst = max((e["rel"] for e in entries), default=0.0)
1759
  over = [e for e in entries if e["rel"] > rel_tol]
1760
+ rep = {"entries": entries, "worst_rel": worst, "over_tol": over,
1761
+ "rel_tol": rel_tol, "compared": len(entries),
1762
+ "pass": len(over) == 0}
1763
+ rep["verdict"] = ("pass: worst drift %.3f over %d entries" %
1764
+ (worst, len(entries))) if rep["pass"] else \
1765
+ ("fail: %d entries over %.2f, worst %s at %.3f" %
1766
+ (len(over), rel_tol, over[0]["path"], worst))
1767
+ return rep
1768
 
1769
 
1770
  def stamped_demo(n=1 << 22, fma_iters=4000, blocks=None, threads=256,
1771
  device="cuda"):
1772
+ """Run the shipped MK_STAMP_BLOCK example (load, compute, drained store) and decode its stamps."""
 
 
1773
  props = torch.cuda.get_device_properties(device)
1774
  if blocks is None:
1775
  blocks = props.multi_processor_count
 
1782
 
1783
 
1784
  def read_stamps(stamps, labels=None, clock_ghz=None):
1785
+ """Decode MK_STAMP deltas to ticks and microseconds; 2-D buffers yield min/median/max across blocks."""
 
 
 
 
1786
  if clock_ghz is None:
1787
  clock_ghz = measure_clock(stamps.device)
1788
 
 
1818
  return {"phases": phases, "clock_ghz": round(clock_ghz, 3),
1819
  "blocks_reporting": int(live.sum().item()),
1820
  "blocks_total": int(live.numel())}
1821
+
1822
+
1823
+ _SCHEMAS = {
1824
+ "probe_device": {
1825
+ "device": "name, sm_arch, sms, l2_bytes, vram_bytes, torch, "
1826
+ "cuda_runtime, ecc, driver, power_limit_w",
1827
+ "clock": "max/min/final GHz under sustained FMA load plus sag_pct",
1828
+ "dram_bw": "GB/s: triad_4B/8B/16B, read_only, write_only, "
1829
+ "random_gather_16B",
1830
+ "l2_bw": "GB/s over an L2-resident working set",
1831
+ "latency": "cycles: smem, l1, l2, dram (min of 3 chases) plus "
1832
+ "ns_per_cycle",
1833
+ "fma_f32 / fma_f64": "TFLOP/s of the register-resident pipes",
1834
+ "fma_ilp": "TFLOP/s at 1/2/4 chains plus ratio_4_1; near 4 means "
1835
+ "latency-bound",
1836
+ "mma": "TFLOP/s per dtype; sparse carries executed and "
1837
+ "dense_equivalent; verified_k_lanes carries "
1838
+ "executed_k_per_iter, vs_nominal, calibration_linear; "
1839
+ "fp8_accum carries the accumulate-precision stall",
1840
+ "mma_fed": "TFLOP/s of smem-staged fp16 tiles (staging exposed)",
1841
+ "smem_bw": "GB/s aggregate, conflict-light pattern",
1842
+ "smem_conflict_penalty": "time ratio vs stride 1 per lane stride",
1843
+ "transfer_bw": "GB/s: h2d/d2h pinned and pageable, d2d",
1844
+ "atomics_f32": "Gop/s curve over distinct addresses plus "
1845
+ "shared_same_addr",
1846
+ "launch_overhead": "us: torch_queued, torch_round_trip, "
1847
+ "raw_queued, raw_round_trip",
1848
+ "grid_barrier": "us per grid.sync or 'unsupported'",
1849
+ "occupancy_blocks_per_sm": "API table by (threads, dyn smem)",
1850
+ "occupancy_knees_fma_tflops": "timed dependent-chain table",
1851
+ },
1852
+ "bench": {
1853
+ "median_ms": "median over accepted samples",
1854
+ "sustained_ms": "median over all samples",
1855
+ "median_ci95_ms / p05_ms / p95_ms / min_ms / mean_ms":
1856
+ "distribution of accepted samples",
1857
+ "samples / rejected_throttled / rejection_basis":
1858
+ "counts and whether rejection used sm_clock or "
1859
+ "bandwidth_consistency",
1860
+ "no_clean_window": "present when every window ran throttled; the "
1861
+ "median is then a throttled-regime number",
1862
+ "clock_ghz": "best and worst SM clock probes around the run",
1863
+ "achieved_gbs / achieved_tflops / arithmetic_intensity":
1864
+ "rates from declared traffic",
1865
+ "roofline": "bound, ridge_flops_per_byte, pct_of_roof, peak_dtype",
1866
+ "graph_captured / graph_fallback_reason": "CUDA-graph state",
1867
+ "alloc_growth_bytes_per_call": "present when fn leaks allocations",
1868
+ "host_overhead_suspected": "present when fn syncs or does host "
1869
+ "work inside; ops under ~50 us are "
1870
+ "below the detection floor",
1871
+ "verdict": "one-sentence summary",
1872
+ },
1873
+ "compare": {
1874
+ "pass": "final verdict incl. the one-sided bias gate",
1875
+ "verdict / criterion / hints": "summary, acceptance rule, and "
1876
+ "failure-pattern suggestions",
1877
+ "worst_trial": "max_ulp, mean_ulp, first_divergent_index(+_nd), "
1878
+ "NaN/Inf census, order_band_ulp, band_score, "
1879
+ "frac_above_band, bias_one_sided_frac, "
1880
+ "worst_elements (index, index_nd, ref, cand, ulp)",
1881
+ "max_abs / max_rel / ulp_hist_log2": "aggregate evidence",
1882
+ "floor": "the banded route cannot reject errors under "
1883
+ "band_margin*(band+2) ULP at order-stable elements",
1884
+ },
1885
+ "fuzz_shapes": {
1886
+ "cases": "per-case shape, variant, dtype, status, repro recipe",
1887
+ "status values": "ok | mismatch | exception | context_killed | "
1888
+ "context_lost | crashed_isolated | skipped_vram "
1889
+ "| skipped_empty",
1890
+ "failed / total / context_lost / verdict / hints": "aggregates",
1891
+ },
1892
+ "sweep": {
1893
+ "rows": "grid point plus median_ms, rejected_throttled, check?, "
1894
+ "achieved rates, status in pruned_race | not_run",
1895
+ "verdict": "on the fastest row",
1896
+ },
1897
+ "dossier_drift": {
1898
+ "entries": "path, current, pinned, rel per intersecting leaf",
1899
+ "worst_rel / over_tol / compared / pass / verdict": "aggregates",
1900
+ },
1901
+ "judge": {
1902
+ "pass / verdict / hints": "composite over compare, fuzz, bench",
1903
+ "compare / fuzz / bench": "the full sub-reports",
1904
+ },
1905
+ "read_stamps": {
1906
+ "phases": "ticks and us per phase; 2-D input adds min/median/max "
1907
+ "across blocks",
1908
+ "note": "stamps record issue; DRAIN variants include the store "
1909
+ "drain to the coherence point",
1910
+ },
1911
+ }
1912
+
1913
+
1914
+ def schema(name=None):
1915
+ """Field layout of every report, so an agent can look up what a number means."""
1916
+ if name is None:
1917
+ return _SCHEMAS
1918
+ if name not in _SCHEMAS:
1919
+ raise KeyError(f"no schema for {name!r}; have "
1920
+ f"{sorted(_SCHEMAS)}")
1921
+ return _SCHEMAS[name]
build/torch213-cxx11-cu132-x86_64-linux/_metakernel_cuda_0b9de42.abi3.so ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:8161b3fe4adbec8d22ecff765130ad6b9893ecca46b1e65c09398d763750b592
3
+ size 1434632
build/torch213-cxx11-cu132-x86_64-linux/_metakernel_cuda_b62a1ce.abi3.so DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:6ce8f061538f15eb45b47250112237c577a079f3ed6b7c54a5ff6d963ff0ba65
3
- size 1188488
 
 
 
 
build/torch213-cxx11-cu132-x86_64-linux/_ops.py CHANGED
@@ -1,9 +1,9 @@
1
  import torch
2
- from . import _metakernel_cuda_b62a1ce
3
- ops = torch.ops._metakernel_cuda_b62a1ce
4
 
5
  def add_op_namespace_prefix(op_name: str):
6
  """
7
  Prefix op by namespace.
8
  """
9
- return f"_metakernel_cuda_b62a1ce::{op_name}"
 
1
  import torch
2
+ from . import _metakernel_cuda_0b9de42
3
+ ops = torch.ops._metakernel_cuda_0b9de42
4
 
5
  def add_op_namespace_prefix(op_name: str):
6
  """
7
  Prefix op by namespace.
8
  """
9
+ return f"_metakernel_cuda_0b9de42::{op_name}"
build/torch213-cxx11-cu132-x86_64-linux/_pinned.py CHANGED
@@ -1,8 +1,4 @@
1
- """Release-pinned device dossiers, one per certified architecture.
2
-
3
- Generated by dev/pin_dossier.py from full probe_device() runs on idle,
4
- single-tenant cards; consumed by metakernel.dossier_drift.
5
- """
6
 
7
  PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
8
  'confidence': 'high',
@@ -295,7 +291,7 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
295
  'd2h_pinned': 10.8,
296
  'h2d_pageable': 10.3,
297
  'h2d_pinned': 11.8}}},
298
- 'sm89_nvidiartx6000adagenerati': {'atomics_f32': {'clock_ghz': 2.706,
299
  'confidence': 'high',
300
  'method': 'atomicAdd '
301
  'hammer; curve '
@@ -308,21 +304,21 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
308
  'contention) to '
309
  'one per thread',
310
  'units': 'Gop/s',
311
- 'value': {'curve_slots': {'1': 0.72,
312
- '145408': 483.54,
313
- '4096': 209.84,
314
- '512': 55.6,
315
- '64': 13.82,
316
- '8': 5.77},
317
- 'shared_same_addr': 12.88}},
318
  'clock': {'confidence': 'high',
319
- 'final_ghz': 2.699,
320
- 'max_ghz': 2.706,
321
  'method': '24 back-to-back FMA '
322
  'spin windows of 30M '
323
  'ticks; wall/ticks '
324
  'per window',
325
- 'min_ghz': 2.501,
326
  'sag_pct': 0.3,
327
  'units': 'GHz'},
328
  'device': {'cuda_runtime': '12.6',
@@ -336,7 +332,7 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
336
  'sms': 142,
337
  'torch': '2.10.0+cu126',
338
  'vram_bytes': 48305274880},
339
- 'dram_bw': {'clock_ghz': 2.706,
340
  'confidence': 'high',
341
  'method': 'streaming triad '
342
  'c=a+s*b per access '
@@ -356,26 +352,41 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
356
  'line-efficiency '
357
  'claim',
358
  'units': 'GB/s',
359
- 'value': {'random_gather_16B': 218.5,
360
- 'read_only': 771.0,
361
- 'triad_16B': 663.1,
362
- 'triad_4B': 638.3,
363
- 'triad_8B': 705.3,
364
  'write_only': 766.5}},
365
- 'fma_f32': {'clock_ghz': 2.706,
366
  'confidence': 'high',
367
  'method': 'register-resident '
368
  'FMA chains',
369
  'units': 'TFLOP/s',
370
- 'value': 90.85},
371
- 'fma_f64': {'clock_ghz': 2.706,
372
  'confidence': 'high',
373
  'method': 'register-resident '
374
  'double FMA chains '
375
  '(the DFMA pipe)',
376
  'units': 'TFLOP/s',
377
- 'value': 1.282},
378
- 'grid_barrier': {'clock_ghz': 2.706,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
379
  'confidence': 'high',
380
  'method': 'cooperative '
381
  'kernel, 142 '
@@ -383,8 +394,8 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
383
  'threads',
384
  'units': 'us per '
385
  'grid.sync',
386
- 'value': 0.54},
387
- 'l2_bw': {'clock_ghz': 2.706,
388
  'confidence': 'medium',
389
  'method': 'in-kernel repeated '
390
  'triad passes over 72 '
@@ -394,14 +405,15 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
394
  'from HBM; amortized '
395
  'over 64 passes',
396
  'units': 'GB/s',
397
- 'value': 9325.3},
398
- 'latency': {'clock_ghz': 2.687,
399
  'confidence': 'high',
400
  'method': 'Sattolo-cycle '
401
- 'pointer chase; 16 '
402
- 'KB ring for L1, 2 '
403
- 'MB TLB-resident '
404
- 'ring for L2, '
 
405
  'self-evicting '
406
  'chain (2x L2 in '
407
  'touched lines) for '
@@ -413,40 +425,58 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
413
  'footprints',
414
  'units': 'cycles (single-lane '
415
  'dependent loads)',
416
- 'value': {'dram_cycles': 649.4,
417
  'l1_cycles': 45.8,
418
- 'l2_cycles': 291.5,
419
  'ns_per_cycle': 0.372,
420
  'smem_cycles': 30.0}},
421
- 'launch_overhead': {'clock_ghz': 2.706,
422
- 'confidence': 'high',
423
  'method': 'empty-kernel '
424
- 'trains, '
425
- 'queued vs '
426
- 'launch+sync',
427
- 'note': 'host-dependent: '
428
- 'includes '
429
- 'torch op '
430
- 'dispatch',
 
 
 
 
 
 
 
 
 
 
 
 
431
  'units': 'us',
432
- 'value': {'queued_us': 5.55,
433
- 'round_trip_us': 14.1}},
434
- 'mma': {'clock_ghz': 2.706,
 
 
435
  'confidence': 'medium',
 
 
 
 
436
  'method': 'register-fragment '
437
  'mma_sync loops (wmma; '
438
  'fp8 and 2:4-sparse '
439
  'fp16 via inline PTX '
440
- 'm16n8k32; sparse '
441
- 'counted '
442
- 'dense-equivalent, the '
443
- 'vendor convention); '
444
- 'flops derive from the '
445
- 'mean accumulator '
446
- 'read-back with every '
447
- 'fragment element '
448
- 'observed, never the '
449
- 'declared loop shape',
450
  'note': 'dense-tile rate without '
451
  'memory traffic; real '
452
  'kernels see this only '
@@ -454,25 +484,54 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
454
  'behind it',
455
  'units': 'TFLOP/s (TOP/s for '
456
  'int8)',
457
- 'value': {'bf16': 365.5,
458
- 'fp16': 375.9,
459
- 'fp16_sparse_2_4': 713.4,
460
- 'fp8_e4m3': 193.7,
461
- 'int8': 770.4,
462
- 'tf32': 177.1},
463
- 'verified_k_lanes': {'bf16': {'executed_k_per_iter': 64.0,
 
 
 
464
  'vs_nominal': 1.0},
465
- 'fp16': {'executed_k_per_iter': 64.0,
 
466
  'vs_nominal': 1.0},
467
- 'fp16_sparse_2_4': {'executed_k_per_iter': 128.0,
 
468
  'vs_nominal': 1.0},
469
- 'fp8_e4m3': {'executed_k_per_iter': 64.0,
470
- 'vs_nominal': 0.25},
471
- 'int8': {'executed_k_per_iter': 64.0,
 
 
 
 
 
472
  'vs_nominal': 1.0},
473
- 'tf32': {'executed_k_per_iter': 32.0,
 
474
  'vs_nominal': 1.0}}},
475
- 'occupancy_blocks_per_sm': {'clock_ghz': 2.706,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
476
  'confidence': 'high',
477
  'method': 'occupancy '
478
  'API '
@@ -501,7 +560,7 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
501
  '16KB': 3,
502
  '32KB': 3,
503
  '48KB': 2}}},
504
- 'occupancy_knees_fma_tflops': {'clock_ghz': 2.706,
505
  'confidence': 'high',
506
  'method': 'a '
507
  'single '
@@ -521,17 +580,6 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
521
  'fixed '
522
  'total '
523
  'work; '
524
- 'throughput '
525
- 'is '
526
- 'proportional '
527
- 'to '
528
- 'resident '
529
- 'threads '
530
- 'until '
531
- 'the '
532
- 'pipe '
533
- 'saturates, '
534
- 'so '
535
  'the '
536
  'cliff '
537
  'marks '
@@ -549,15 +597,15 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
549
  'above',
550
  'units': 'TFLOP/s',
551
  'value': {'t1024': {'0KB': 75.5,
552
- '32KB': 73.66,
553
- '48KB': 74.95},
554
- 't128': {'0KB': 79.86,
555
  '32KB': 60.31,
556
- '48KB': 44.57},
557
- 't256': {'0KB': 81.69,
558
- '32KB': 79.48,
559
- '48KB': 71.47}}},
560
- 'smem_bw': {'clock_ghz': 2.706,
561
  'confidence': 'medium',
562
  'method': 'block-resident '
563
  'read+write '
@@ -566,12 +614,56 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
566
  'aggregated across '
567
  'SMs',
568
  'note': 'bank-conflict-light '
569
- 'access pattern; a '
570
- 'conflicted pattern '
571
- 'can be far lower',
 
572
  'units': 'GB/s',
573
- 'value': 21250.0},
574
- 'transfer_bw': {'clock_ghz': 2.706,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
575
  'confidence': 'high',
576
  'method': '256 MB torch '
577
  'copies, '
@@ -583,8 +675,8 @@ PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
583
  'direction of '
584
  'traffic',
585
  'units': 'GB/s',
586
- 'value': {'d2d': 330.7,
587
- 'd2h_pageable': 15.6,
588
- 'd2h_pinned': 26.2,
589
- 'h2d_pageable': 16.6,
590
  'h2d_pinned': 24.0}}}}
 
1
+ '''Release-pinned device dossiers, one per certified architecture; generated by dev/pin_dossier.py from idle single-tenant runs, consumed by dossier_drift.'''
 
 
 
 
2
 
3
  PINNED = {'sm86_nvidiageforcertx3070tila': {'atomics_f32': {'clock_ghz': 1.785,
4
  'confidence': 'high',
 
291
  'd2h_pinned': 10.8,
292
  'h2d_pageable': 10.3,
293
  'h2d_pinned': 11.8}}},
294
+ 'sm89_nvidiartx6000adagenerati': {'atomics_f32': {'clock_ghz': 2.705,
295
  'confidence': 'high',
296
  'method': 'atomicAdd '
297
  'hammer; curve '
 
304
  'contention) to '
305
  'one per thread',
306
  'units': 'Gop/s',
307
+ 'value': {'curve_slots': {'1': 0.71,
308
+ '145408': 413.99,
309
+ '4096': 184.9,
310
+ '512': 49.84,
311
+ '64': 13.25,
312
+ '8': 5.74},
313
+ 'shared_same_addr': 12.23}},
314
  'clock': {'confidence': 'high',
315
+ 'final_ghz': 2.698,
316
+ 'max_ghz': 2.705,
317
  'method': '24 back-to-back FMA '
318
  'spin windows of 30M '
319
  'ticks; wall/ticks '
320
  'per window',
321
+ 'min_ghz': 2.5,
322
  'sag_pct': 0.3,
323
  'units': 'GHz'},
324
  'device': {'cuda_runtime': '12.6',
 
332
  'sms': 142,
333
  'torch': '2.10.0+cu126',
334
  'vram_bytes': 48305274880},
335
+ 'dram_bw': {'clock_ghz': 2.705,
336
  'confidence': 'high',
337
  'method': 'streaming triad '
338
  'c=a+s*b per access '
 
352
  'line-efficiency '
353
  'claim',
354
  'units': 'GB/s',
355
+ 'value': {'random_gather_16B': 218.1,
356
+ 'read_only': 766.5,
357
+ 'triad_16B': 682.1,
358
+ 'triad_4B': 654.8,
359
+ 'triad_8B': 670.9,
360
  'write_only': 766.5}},
361
+ 'fma_f32': {'clock_ghz': 2.705,
362
  'confidence': 'high',
363
  'method': 'register-resident '
364
  'FMA chains',
365
  'units': 'TFLOP/s',
366
+ 'value': 86.58},
367
+ 'fma_f64': {'clock_ghz': 2.705,
368
  'confidence': 'high',
369
  'method': 'register-resident '
370
  'double FMA chains '
371
  '(the DFMA pipe)',
372
  'units': 'TFLOP/s',
373
+ 'value': 1.257},
374
+ 'fma_ilp': {'clock_ghz': 2.705,
375
+ 'confidence': 'high',
376
+ 'method': '1/2/4 independent '
377
+ 'dependent FMA '
378
+ 'chains per thread '
379
+ 'at one block of 64 '
380
+ 'threads per SM; a '
381
+ 'ratio near 4 means '
382
+ 'latency-bound at '
383
+ 'that occupancy',
384
+ 'units': 'TFLOP/s',
385
+ 'value': {'ilp1': 11.75,
386
+ 'ilp2': 23.37,
387
+ 'ilp4': 43.03,
388
+ 'ratio_4_1': 3.66}},
389
+ 'grid_barrier': {'clock_ghz': 2.705,
390
  'confidence': 'high',
391
  'method': 'cooperative '
392
  'kernel, 142 '
 
394
  'threads',
395
  'units': 'us per '
396
  'grid.sync',
397
+ 'value': 0.55},
398
+ 'l2_bw': {'clock_ghz': 2.705,
399
  'confidence': 'medium',
400
  'method': 'in-kernel repeated '
401
  'triad passes over 72 '
 
405
  'from HBM; amortized '
406
  'over 64 passes',
407
  'units': 'GB/s',
408
+ 'value': 9590.6},
409
+ 'latency': {'clock_ghz': 2.691,
410
  'confidence': 'high',
411
  'method': 'Sattolo-cycle '
412
+ 'pointer chase, min '
413
+ 'of 3 runs; 16 KB '
414
+ 'ring for L1, 2 MB '
415
+ 'TLB-resident ring '
416
+ 'for L2, '
417
  'self-evicting '
418
  'chain (2x L2 in '
419
  'touched lines) for '
 
425
  'footprints',
426
  'units': 'cycles (single-lane '
427
  'dependent loads)',
428
+ 'value': {'dram_cycles': 656.9,
429
  'l1_cycles': 45.8,
430
+ 'l2_cycles': 292.0,
431
  'ns_per_cycle': 0.372,
432
  'smem_cycles': 30.0}},
433
+ 'launch_overhead': {'clock_ghz': 2.705,
434
+ 'confidence': 'medium',
435
  'method': 'empty-kernel '
436
+ 'trains; '
437
+ 'torch path '
438
+ 'per-launch '
439
+ 'through op '
440
+ 'dispatch, '
441
+ 'raw path '
442
+ 'looped '
443
+ 'inside one '
444
+ 'binding '
445
+ 'call',
446
+ 'note': 'host-state-dependent; '
447
+ 'the '
448
+ 'torch-minus-raw '
449
+ 'gap is the '
450
+ 'dispatch '
451
+ 'cost a '
452
+ 'torch-launched '
453
+ 'candidate '
454
+ 'pays',
455
  'units': 'us',
456
+ 'value': {'raw_queued_us': 6.75,
457
+ 'raw_round_trip_us': 13.87,
458
+ 'torch_queued_us': 7.09,
459
+ 'torch_round_trip_us': 18.53}},
460
+ 'mma': {'clock_ghz': 2.705,
461
  'confidence': 'medium',
462
+ 'fp8_accum': {'fp8_e4m3': {'effective_accum_mantissa_bits': 8,
463
+ 'stall_element_value': 16384.0},
464
+ 'fp8_e5m2': {'effective_accum_mantissa_bits': 8,
465
+ 'stall_element_value': 16384.0}},
466
  'method': 'register-fragment '
467
  'mma_sync loops (wmma; '
468
  'fp8 and 2:4-sparse '
469
  'fp16 via inline PTX '
470
+ 'm16n8k32); flops '
471
+ 'derive from a '
472
+ 'sub-saturation '
473
+ 'accumulator read-back '
474
+ 'with every fragment '
475
+ 'element observed, '
476
+ 'never the declared '
477
+ 'loop shape; sparse '
478
+ 'reports executed and '
479
+ 'dense-equivalent flops',
480
  'note': 'dense-tile rate without '
481
  'memory traffic; real '
482
  'kernels see this only '
 
484
  'behind it',
485
  'units': 'TFLOP/s (TOP/s for '
486
  'int8)',
487
+ 'value': {'bf16': 369.6,
488
+ 'fp16': 365.3,
489
+ 'fp16_sparse_2_4': {'dense_equivalent': 711.1,
490
+ 'executed': 355.6},
491
+ 'fp8_e4m3': 772.3,
492
+ 'fp8_e5m2': 771.2,
493
+ 'int8': 750.0,
494
+ 'tf32': 178.3},
495
+ 'verified_k_lanes': {'bf16': {'calibration_linear': True,
496
+ 'executed_k_per_iter': 64.0,
497
  'vs_nominal': 1.0},
498
+ 'fp16': {'calibration_linear': True,
499
+ 'executed_k_per_iter': 64.0,
500
  'vs_nominal': 1.0},
501
+ 'fp16_sparse_2_4': {'calibration_linear': True,
502
+ 'executed_k_per_iter': 128.0,
503
  'vs_nominal': 1.0},
504
+ 'fp8_e4m3': {'calibration_linear': True,
505
+ 'executed_k_per_iter': 256.0,
506
+ 'vs_nominal': 1.0},
507
+ 'fp8_e5m2': {'calibration_linear': True,
508
+ 'executed_k_per_iter': 256.0,
509
+ 'vs_nominal': 1.0},
510
+ 'int8': {'calibration_linear': True,
511
+ 'executed_k_per_iter': 64.0,
512
  'vs_nominal': 1.0},
513
+ 'tf32': {'calibration_linear': True,
514
+ 'executed_k_per_iter': 32.0,
515
  'vs_nominal': 1.0}}},
516
+ 'mma_fed': {'clock_ghz': 2.705,
517
+ 'confidence': 'high',
518
+ 'method': 'm16n16k16 fp16 '
519
+ 'tiles fed from an '
520
+ 'L2-resident buffer '
521
+ 'through cp.async '
522
+ 'double-buffered '
523
+ 'shared memory; '
524
+ 'accumulator-verified',
525
+ 'note': 'the achieved '
526
+ 'counterpart to the '
527
+ 'register peak: '
528
+ 'staging exposed, one '
529
+ 'dependent '
530
+ 'accumulator chain '
531
+ 'per warp',
532
+ 'units': 'TFLOP/s',
533
+ 'value': 85.2},
534
+ 'occupancy_blocks_per_sm': {'clock_ghz': 2.705,
535
  'confidence': 'high',
536
  'method': 'occupancy '
537
  'API '
 
560
  '16KB': 3,
561
  '32KB': 3,
562
  '48KB': 2}}},
563
+ 'occupancy_knees_fma_tflops': {'clock_ghz': 2.705,
564
  'confidence': 'high',
565
  'method': 'a '
566
  'single '
 
580
  'fixed '
581
  'total '
582
  'work; '
 
 
 
 
 
 
 
 
 
 
 
583
  'the '
584
  'cliff '
585
  'marks '
 
597
  'above',
598
  'units': 'TFLOP/s',
599
  'value': {'t1024': {'0KB': 75.5,
600
+ '32KB': 73.71,
601
+ '48KB': 72.69},
602
+ 't128': {'0KB': 79.72,
603
  '32KB': 60.31,
604
+ '48KB': 45.81},
605
+ 't256': {'0KB': 78.79,
606
+ '32KB': 78.85,
607
+ '48KB': 72.97}}},
608
+ 'smem_bw': {'clock_ghz': 2.705,
609
  'confidence': 'medium',
610
  'method': 'block-resident '
611
  'read+write '
 
614
  'aggregated across '
615
  'SMs',
616
  'note': 'bank-conflict-light '
617
+ 'access pattern; the '
618
+ 'stride sweep below '
619
+ 'prices the '
620
+ 'conflicted case',
621
  'units': 'GB/s',
622
+ 'value': 22244.0},
623
+ 'smem_conflict_penalty': {'clock_ghz': 2.705,
624
+ 'confidence': 'high',
625
+ 'method': 'dependent '
626
+ 'read-FMA-write '
627
+ 'per '
628
+ 'thread '
629
+ 'at '
630
+ 'warp-lane '
631
+ 'bank '
632
+ 'distance '
633
+ '`stride`; '
634
+ 'stride '
635
+ '32 '
636
+ 'puts '
637
+ 'every '
638
+ 'lane '
639
+ 'in '
640
+ 'one '
641
+ 'bank',
642
+ 'note': 'the '
643
+ 'direct '
644
+ 'conflict '
645
+ 'measurement '
646
+ 'that '
647
+ 'disambiguates '
648
+ 'a '
649
+ 'bank-conflict '
650
+ 'signature '
651
+ 'from '
652
+ 'an ILP '
653
+ 'stall',
654
+ 'units': 'x '
655
+ '(time '
656
+ 'ratio '
657
+ 'vs '
658
+ 'stride '
659
+ '1)',
660
+ 'value': {'stride_1': 1.0,
661
+ 'stride_16': 7.13,
662
+ 'stride_2': 1.07,
663
+ 'stride_32': 14.08,
664
+ 'stride_4': 2.04,
665
+ 'stride_8': 3.91}},
666
+ 'transfer_bw': {'clock_ghz': 2.705,
667
  'confidence': 'high',
668
  'method': '256 MB torch '
669
  'copies, '
 
675
  'direction of '
676
  'traffic',
677
  'units': 'GB/s',
678
+ 'value': {'d2d': 324.0,
679
+ 'd2h_pageable': 7.8,
680
+ 'd2h_pinned': 26.3,
681
+ 'h2d_pageable': 7.6,
682
  'h2d_pinned': 24.0}}}}
build/torch213-cxx11-cu132-x86_64-linux/metadata.json CHANGED
@@ -1,9 +1,10 @@
1
  {
2
  "name": "metakernel",
3
- "id": "_metakernel_cuda_b62a1ce",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "python-depends": [],
 
7
  "backend": {
8
  "type": "cuda",
9
  "archs": [
@@ -18,21 +19,20 @@
18
  "digest": {
19
  "algorithm": "sha256",
20
  "files": {
21
- "__init__.py": "e/P3LIoxZqzuhgyc26YJRkdLj4OilextyRiU8YbAvIA=",
22
- "_metakernel_cuda_b62a1ce.abi3.so": "bOjwYVOPFetFtHJQESI3xXegefPta3xUpf9tlj/wumU=",
23
- "_ops.py": "QokZXDYGh+rLEKO/wNDRcMkJPtPWK568RiTlCHopsHM=",
24
- "_pinned.py": "4i1FvV3kWanEdFLd9+rGOjHcHDAyOjKnjLt5LUzgASs=",
25
- "metakernel/__init__.py": "DFYPlrhXwYjEqCl/8n0SmWGZV8NFml5DPhMjKfv98GY="
26
  }
27
  },
28
  "provenance": {
29
  "kernel-builder": {
30
  "version": "0.17.0-dev0",
31
- "sha": "19aaa6421e674e9fecc352bbae6eab81d19a6bf4",
32
  "dirty": false
33
  },
34
  "kernel": {
35
- "sha": "b62a1cef64b6e3b2cabf774c82ea7ce61dcac6c0",
36
  "dirty": false
37
  }
38
  }
 
1
  {
2
  "name": "metakernel",
3
+ "id": "_metakernel_cuda_0b9de42",
4
  "version": 1,
5
  "license": "Apache-2.0",
6
  "python-depends": [],
7
+ "kernel-depends": [],
8
  "backend": {
9
  "type": "cuda",
10
  "archs": [
 
19
  "digest": {
20
  "algorithm": "sha256",
21
  "files": {
22
+ "__init__.py": "lDQ+fJ0JnI3sQGR3UMnDovBbG4ciYBsaFjmgYdKDTKI=",
23
+ "_metakernel_cuda_0b9de42.abi3.so": "gWGz/krb7I0i7P92UTCta5iT7MpGseZcCTmNdjdQtZI=",
24
+ "_ops.py": "x88Bkm1nxRli0nIu2u4ztaJopS1IAZWdN9LZPfspJdo=",
25
+ "_pinned.py": "UyzMniAjfVMIrIbbrSq92KKo5dX5l/GwV3pMVA+eNtM="
 
26
  }
27
  },
28
  "provenance": {
29
  "kernel-builder": {
30
  "version": "0.17.0-dev0",
31
+ "sha": "2c40e10d3da1392245ee81bd05b55fb3103b2b34",
32
  "dirty": false
33
  },
34
  "kernel": {
35
+ "sha": "0b9de42fee8a1e52593f2f1f58460c0acc93d7e8",
36
  "dirty": false
37
  }
38
  }
build/torch213-cxx11-cu132-x86_64-linux/metakernel/__init__.py DELETED
@@ -1,26 +0,0 @@
1
- import ctypes
2
- import importlib.util
3
- import sys
4
- from pathlib import Path
5
- from types import ModuleType
6
-
7
-
8
- def _import_from_path(file_path: Path) -> ModuleType:
9
- # We cannot use the module name as-is, after adding it to `sys.modules`,
10
- # it would also be used for other imports. So, we make a module name that
11
- # depends on the path for it to be unique using the hex-encoded hash of
12
- # the path.
13
- path_hash = "{:x}".format(ctypes.c_size_t(hash(file_path.absolute())).value)
14
- module_name = path_hash
15
- spec = importlib.util.spec_from_file_location(module_name, file_path)
16
- if spec is None:
17
- raise ImportError(f"Cannot load spec for {module_name} from {file_path}")
18
- module = importlib.util.module_from_spec(spec)
19
- if module is None:
20
- raise ImportError(f"Cannot load module {module_name} from spec")
21
- sys.modules[module_name] = module
22
- spec.loader.exec_module(module) # type: ignore
23
- return module
24
-
25
-
26
- globals().update(vars(_import_from_path(Path(__file__).parent.parent / "__init__.py")))