nbxtensor 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nbxtensor/THIRD_PARTY_NOTICES.md +287 -0
- nbxtensor/__init__.py +17 -0
- nbxtensor/backend/__init__.py +1 -0
- nbxtensor/backend/capabilities.py +207 -0
- nbxtensor/backend/cpu/__init__.py +1 -0
- nbxtensor/backend/cpu/blas.py +102 -0
- nbxtensor/backend/cpu/pool.py +204 -0
- nbxtensor/backend/cpu_backend.py +162 -0
- nbxtensor/backend/device_facts.py +105 -0
- nbxtensor/backend/device_files.py +129 -0
- nbxtensor/backend/device_names.py +18 -0
- nbxtensor/backend/devices/amd/cdna.yml +228 -0
- nbxtensor/backend/devices/amd/cdna2.yml +228 -0
- nbxtensor/backend/devices/amd/cdna3.yml +227 -0
- nbxtensor/backend/devices/apple/apple_m1.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m1_max.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m1_pro.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m1_ultra.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m2.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m2_max.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m2_pro.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m2_ultra.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m3.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m3_max.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m3_pro.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m3_ultra.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m4.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m4_max.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m4_pro.yml +308 -0
- nbxtensor/backend/devices/apple/apple_m5.yml +269 -0
- nbxtensor/backend/devices/apple/apple_m5_max.yml +269 -0
- nbxtensor/backend/devices/apple/apple_m5_pro.yml +269 -0
- nbxtensor/backend/devices/apple/apple_m5_ultra.yml +269 -0
- nbxtensor/backend/devices/apple/apple_m6.yml +269 -0
- nbxtensor/backend/devices/apple/apple_silicon.yml +263 -0
- nbxtensor/backend/devices/nvidia/ampere.yml +214 -0
- nbxtensor/backend/devices/nvidia/hopper.yml +213 -0
- nbxtensor/backend/devices/nvidia/volta.yml +411 -0
- nbxtensor/backend/faults.py +151 -0
- nbxtensor/backend/host_memory.py +185 -0
- nbxtensor/backend/launcher.py +2350 -0
- nbxtensor/backend/launcher_contract.py +429 -0
- nbxtensor/backend/metal_backend.py +661 -0
- nbxtensor/backend/metal_device.py +1274 -0
- nbxtensor/backend/process_memory.py +73 -0
- nbxtensor/backend/replay_graph.py +322 -0
- nbxtensor/backend/runtime.py +409 -0
- nbxtensor/backend/triton_ext_driver.py +735 -0
- nbxtensor/dtype/__init__.py +1 -0
- nbxtensor/dtype/convert.py +107 -0
- nbxtensor/dtype/dtypes.yml +70 -0
- nbxtensor/dtype/itemsize.py +166 -0
- nbxtensor/dtype/nbx_dtype.py +72 -0
- nbxtensor/dtype/operands.py +158 -0
- nbxtensor/dtype/outputs.py +64 -0
- nbxtensor/dtype/policy.py +155 -0
- nbxtensor/dtype/policy.yml +51 -0
- nbxtensor/dtype/promotion.py +112 -0
- nbxtensor/host.py +55 -0
- nbxtensor/io/__init__.py +1 -0
- nbxtensor/io/read_rate.py +197 -0
- nbxtensor/io/safetensors.py +314 -0
- nbxtensor/memory/__init__.py +9 -0
- nbxtensor/memory/allocator.py +1988 -0
- nbxtensor/memory/arena.py +75 -0
- nbxtensor/memory/device_memory.py +150 -0
- nbxtensor/memory/device_transfer.py +154 -0
- nbxtensor/memory/layout.py +43 -0
- nbxtensor/memory/memory_pool.py +191 -0
- nbxtensor/memory/metal.py +96 -0
- nbxtensor/memory/oom_advice.py +91 -0
- nbxtensor/memory/pointer_tables.py +323 -0
- nbxtensor/memory/pool.py +507 -0
- nbxtensor/memory/shadow.py +43 -0
- nbxtensor/memory/slab.py +273 -0
- nbxtensor/memory/trace_replay.py +122 -0
- nbxtensor/memory/traces/sana-video-2b-720p.16g.2026-11-02.tsv +13610 -0
- nbxtensor/memory/vmm.py +118 -0
- nbxtensor/ops/__init__.py +1 -0
- nbxtensor/ops/_autotune_policy.py +90 -0
- nbxtensor/ops/_common.py +32 -0
- nbxtensor/ops/_configs.py +1041 -0
- nbxtensor/ops/_cost.py +231 -0
- nbxtensor/ops/abs.py +17 -0
- nbxtensor/ops/adaptive_avg_pool2d.py +161 -0
- nbxtensor/ops/add.py +96 -0
- nbxtensor/ops/addcdiv.py +29 -0
- nbxtensor/ops/addcmul.py +29 -0
- nbxtensor/ops/addmv_op.py +64 -0
- nbxtensor/ops/addr.py +68 -0
- nbxtensor/ops/all_reduce.py +83 -0
- nbxtensor/ops/amax.py +40 -0
- nbxtensor/ops/any_reduce.py +83 -0
- nbxtensor/ops/arange_op.py +27 -0
- nbxtensor/ops/argmax.py +107 -0
- nbxtensor/ops/argmin.py +93 -0
- nbxtensor/ops/atan2.py +71 -0
- nbxtensor/ops/attention_wrappers.py +1648 -0
- nbxtensor/ops/autotune_bucket.py +137 -0
- nbxtensor/ops/autotune_cache.py +430 -0
- nbxtensor/ops/autotune_certified.py +1262 -0
- nbxtensor/ops/autotune_refusals.py +380 -0
- nbxtensor/ops/autotune_settings.py +58 -0
- nbxtensor/ops/avg_pool2d.py +73 -0
- nbxtensor/ops/baddbmm_op.py +132 -0
- nbxtensor/ops/batch_norm.py +163 -0
- nbxtensor/ops/binary_strided.py +196 -0
- nbxtensor/ops/bitwise_and.py +26 -0
- nbxtensor/ops/bitwise_left_shift.py +47 -0
- nbxtensor/ops/bitwise_not.py +24 -0
- nbxtensor/ops/bitwise_or.py +26 -0
- nbxtensor/ops/bitwise_right_shift.py +47 -0
- nbxtensor/ops/bucketize.py +37 -0
- nbxtensor/ops/cat_op.py +87 -0
- nbxtensor/ops/celu.py +20 -0
- nbxtensor/ops/clamp.py +24 -0
- nbxtensor/ops/clamp_min.py +22 -0
- nbxtensor/ops/conv2d.py +265 -0
- nbxtensor/ops/conv2d_m8n8k4.py +165 -0
- nbxtensor/ops/conv_depthwise2d.py +154 -0
- nbxtensor/ops/conv_transpose2d.py +148 -0
- nbxtensor/ops/conv_wrappers.py +1482 -0
- nbxtensor/ops/copy.py +18 -0
- nbxtensor/ops/copy_op.py +39 -0
- nbxtensor/ops/cos.py +17 -0
- nbxtensor/ops/count_nonzero.py +64 -0
- nbxtensor/ops/cross_entropy.py +67 -0
- nbxtensor/ops/cummax.py +109 -0
- nbxtensor/ops/cummin.py +105 -0
- nbxtensor/ops/cumsum.py +244 -0
- nbxtensor/ops/decode_attn_vec.py +162 -0
- nbxtensor/ops/depthwise_conv2d.py +132 -0
- nbxtensor/ops/dequant_gemv.py +190 -0
- nbxtensor/ops/dft.py +67 -0
- nbxtensor/ops/dispatch.py +1228 -0
- nbxtensor/ops/div.py +75 -0
- nbxtensor/ops/dot_op.py +67 -0
- nbxtensor/ops/dropout.py +23 -0
- nbxtensor/ops/dtype_convert.py +42 -0
- nbxtensor/ops/elementwise_launch.py +81 -0
- nbxtensor/ops/elementwise_wrappers.py +2315 -0
- nbxtensor/ops/elu.py +20 -0
- nbxtensor/ops/embedding.py +69 -0
- nbxtensor/ops/eq.py +32 -0
- nbxtensor/ops/erf.py +18 -0
- nbxtensor/ops/exp.py +17 -0
- nbxtensor/ops/exp2.py +18 -0
- nbxtensor/ops/fft_op.py +125 -0
- nbxtensor/ops/fill_op.py +20 -0
- nbxtensor/ops/flash_attention.py +307 -0
- nbxtensor/ops/flash_attention_m8n8k4.py +384 -0
- nbxtensor/ops/flash_decode.py +78 -0
- nbxtensor/ops/flip_op.py +60 -0
- nbxtensor/ops/floor.py +90 -0
- nbxtensor/ops/full_op.py +24 -0
- nbxtensor/ops/fused_moe.py +417 -0
- nbxtensor/ops/fused_moe_m8n8k4.py +99 -0
- nbxtensor/ops/fused_upsample_conv.py +413 -0
- nbxtensor/ops/gather_op.py +52 -0
- nbxtensor/ops/ge.py +32 -0
- nbxtensor/ops/gelu.py +31 -0
- nbxtensor/ops/gemv_vec.py +80 -0
- nbxtensor/ops/glu.py +43 -0
- nbxtensor/ops/grid_sampler.py +145 -0
- nbxtensor/ops/group_norm_tiled.py +67 -0
- nbxtensor/ops/groupnorm.py +123 -0
- nbxtensor/ops/gt.py +32 -0
- nbxtensor/ops/hardsigmoid.py +18 -0
- nbxtensor/ops/hardswish.py +19 -0
- nbxtensor/ops/im2col.py +57 -0
- nbxtensor/ops/index_add.py +95 -0
- nbxtensor/ops/index_put_op.py +92 -0
- nbxtensor/ops/index_select.py +121 -0
- nbxtensor/ops/index_wrappers.py +770 -0
- nbxtensor/ops/isfinite.py +20 -0
- nbxtensor/ops/isinf.py +20 -0
- nbxtensor/ops/isnan.py +18 -0
- nbxtensor/ops/launch_keys.py +1054 -0
- nbxtensor/ops/launch_observer.py +54 -0
- nbxtensor/ops/layernorm.py +71 -0
- nbxtensor/ops/le.py +35 -0
- nbxtensor/ops/leaky_relu.py +19 -0
- nbxtensor/ops/lerp.py +76 -0
- nbxtensor/ops/linalg_wrappers.py +1370 -0
- nbxtensor/ops/linspace_op.py +47 -0
- nbxtensor/ops/log.py +17 -0
- nbxtensor/ops/log_sigmoid.py +23 -0
- nbxtensor/ops/logical_and.py +26 -0
- nbxtensor/ops/logical_not.py +24 -0
- nbxtensor/ops/logical_or.py +26 -0
- nbxtensor/ops/logical_xor.py +37 -0
- nbxtensor/ops/loss_wrappers.py +282 -0
- nbxtensor/ops/lt.py +32 -0
- nbxtensor/ops/masked_fill.py +21 -0
- nbxtensor/ops/matmul.py +377 -0
- nbxtensor/ops/matmul_m8n8k4.py +235 -0
- nbxtensor/ops/max_pool2d.py +74 -0
- nbxtensor/ops/max_pool3d.py +93 -0
- nbxtensor/ops/max_reduce.py +91 -0
- nbxtensor/ops/maximum.py +18 -0
- nbxtensor/ops/mean.py +40 -0
- nbxtensor/ops/min_reduce.py +91 -0
- nbxtensor/ops/minimum.py +18 -0
- nbxtensor/ops/mish.py +22 -0
- nbxtensor/ops/mma_m8n8k4.py +152 -0
- nbxtensor/ops/moe_align.py +195 -0
- nbxtensor/ops/moe_decode_vec.py +374 -0
- nbxtensor/ops/moe_device_residency.py +141 -0
- nbxtensor/ops/moe_wrappers.py +345 -0
- nbxtensor/ops/mse_loss.py +79 -0
- nbxtensor/ops/mul.py +51 -0
- nbxtensor/ops/nan_to_num.py +23 -0
- nbxtensor/ops/ne.py +32 -0
- nbxtensor/ops/neg.py +17 -0
- nbxtensor/ops/nllloss.py +124 -0
- nbxtensor/ops/nonzero.py +33 -0
- nbxtensor/ops/nonzero_op.py +62 -0
- nbxtensor/ops/norm_wrappers.py +579 -0
- nbxtensor/ops/ones_op.py +20 -0
- nbxtensor/ops/oracles/__init__.py +6 -0
- nbxtensor/ops/oracles/conv2d_fp64.py +450 -0
- nbxtensor/ops/pad_op.py +159 -0
- nbxtensor/ops/paged_decode_attn.py +215 -0
- nbxtensor/ops/pixel_shuffle.py +144 -0
- nbxtensor/ops/pixel_unshuffle.py +72 -0
- nbxtensor/ops/pow.py +137 -0
- nbxtensor/ops/prod.py +81 -0
- nbxtensor/ops/rand_op.py +52 -0
- nbxtensor/ops/random_wrappers.py +366 -0
- nbxtensor/ops/reciprocal.py +17 -0
- nbxtensor/ops/reduction_wrappers.py +845 -0
- nbxtensor/ops/relu.py +18 -0
- nbxtensor/ops/remainder.py +46 -0
- nbxtensor/ops/repeat_interleave.py +48 -0
- nbxtensor/ops/repeat_op.py +132 -0
- nbxtensor/ops/residual_chain.py +374 -0
- nbxtensor/ops/rmsnorm.py +68 -0
- nbxtensor/ops/rope.py +142 -0
- nbxtensor/ops/rsqrt.py +17 -0
- nbxtensor/ops/scaled_softmax.py +96 -0
- nbxtensor/ops/scatter_op.py +139 -0
- nbxtensor/ops/screen_oracle.py +479 -0
- nbxtensor/ops/selu.py +22 -0
- nbxtensor/ops/shape_wrappers.py +1032 -0
- nbxtensor/ops/sigmoid.py +19 -0
- nbxtensor/ops/silu.py +27 -0
- nbxtensor/ops/sin.py +17 -0
- nbxtensor/ops/softmax.py +169 -0
- nbxtensor/ops/softplus.py +27 -0
- nbxtensor/ops/sort_op.py +304 -0
- nbxtensor/ops/spectral_wrappers.py +481 -0
- nbxtensor/ops/sqrt.py +17 -0
- nbxtensor/ops/stack_op.py +84 -0
- nbxtensor/ops/std.py +113 -0
- nbxtensor/ops/strided_copy.py +200 -0
- nbxtensor/ops/sub.py +35 -0
- nbxtensor/ops/sum.py +40 -0
- nbxtensor/ops/tan.py +19 -0
- nbxtensor/ops/tanh.py +21 -0
- nbxtensor/ops/threshold.py +19 -0
- nbxtensor/ops/tiling.yml +91 -0
- nbxtensor/ops/tiling_policy.py +43 -0
- nbxtensor/ops/tiling_sizes.py +370 -0
- nbxtensor/ops/topk.py +267 -0
- nbxtensor/ops/trace_op.py +64 -0
- nbxtensor/ops/tril.py +64 -0
- nbxtensor/ops/triu.py +62 -0
- nbxtensor/ops/unfold_op.py +37 -0
- nbxtensor/ops/unique_op.py +97 -0
- nbxtensor/ops/upsample_bilinear2d.py +106 -0
- nbxtensor/ops/upsample_nearest2d.py +52 -0
- nbxtensor/ops/upsample_nearest3d.py +61 -0
- nbxtensor/ops/var.py +136 -0
- nbxtensor/ops/vector_norm.py +216 -0
- nbxtensor/ops/weight_norm.py +104 -0
- nbxtensor/ops/where.py +39 -0
- nbxtensor/ops/wrappers.py +825 -0
- nbxtensor/ops/zeros_op.py +20 -0
- nbxtensor/quant/__init__.py +1 -0
- nbxtensor/quant/quantized_tensor.py +187 -0
- nbxtensor/random/__init__.py +1 -0
- nbxtensor/random/mt_randn.py +154 -0
- nbxtensor/random/rng_pin.py +59 -0
- nbxtensor/random/rng_stream.py +118 -0
- nbxtensor/random/seeded_draw.py +223 -0
- nbxtensor/shape/__init__.py +157 -0
- nbxtensor/tensor.py +1867 -0
- nbxtensor-0.0.1.dist-info/METADATA +379 -0
- nbxtensor-0.0.1.dist-info/RECORD +291 -0
- nbxtensor-0.0.1.dist-info/WHEEL +4 -0
- nbxtensor-0.0.1.dist-info/licenses/LICENSE +189 -0
|
@@ -0,0 +1,1262 @@
|
|
|
1
|
+
"""The certified autotune directory — an engine component.
|
|
2
|
+
|
|
3
|
+
The settings the autotuner would otherwise discover by sweeping at runtime
|
|
4
|
+
live WITH the engine, versioned with it, under
|
|
5
|
+
``src/neurobrix/config/autotune/<vendor>/<profile>/<kernel>.<dtype>.json``:
|
|
6
|
+
one file per kernel and per dtype of the buffer the kernel writes, indexed
|
|
7
|
+
inside by the shape key the launcher already computes (``autotune_cache.key_of``
|
|
8
|
+
— the Autotuner's own key). Each entry carries the setting retained and its
|
|
9
|
+
PROOF — the date, the engine and backend versions, the shape, the deviation
|
|
10
|
+
measured against the fp64 oracle, the profile's tolerance, the machine — and
|
|
11
|
+
the settings excluded with their deviation.
|
|
12
|
+
|
|
13
|
+
The directory is filled by ``neurobrix autotune certify --profile <profile>``
|
|
14
|
+
(``autotune_certify.py``), never by a request: at load the launcher looks the
|
|
15
|
+
certified setting up for the profile Prism detected, the kernel, the dtype and
|
|
16
|
+
the shape; if it exists it is applied without a sweep and without the screen;
|
|
17
|
+
if it is missing the runtime sweeps with the consensus screen as before, SAYS
|
|
18
|
+
SO, and keeps the result in the machine's local replay cache — never in this
|
|
19
|
+
directory. A file without a proof, or whose proof does not re-read, is refused
|
|
20
|
+
here with its reason and the runtime falls back to the sweep.
|
|
21
|
+
|
|
22
|
+
Nothing here names a backend or a model: the vendor and profile come from the
|
|
23
|
+
vendor profile in force, the kernels from the launcher, the tolerances from the
|
|
24
|
+
profile.
|
|
25
|
+
"""
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
import ast
|
|
29
|
+
import datetime as _dt
|
|
30
|
+
import json
|
|
31
|
+
import os
|
|
32
|
+
from pathlib import Path
|
|
33
|
+
from typing import Any, Dict, Iterator, List, Optional, Tuple
|
|
34
|
+
|
|
35
|
+
FORMAT = "nbx-autotune-certified/2"
|
|
36
|
+
|
|
37
|
+
#: Formats this reader accepts. `/2` added the `built` field, which says the
|
|
38
|
+
#: kernel actually COMPILED on the device during its certifying run. A `/1`
|
|
39
|
+
#: file pre-dates that field and is READ, not refused: the 5628 shapes the
|
|
40
|
+
#: other machine certified before it existed are proven work, and discarding
|
|
41
|
+
#: them over a field that did not exist when they were written would be
|
|
42
|
+
#: destroying a measurement to tidy a schema. What `/1` cannot say is said
|
|
43
|
+
#: instead — `built_unknown_formats` names them to the caller.
|
|
44
|
+
_FORMATS = ("nbx-autotune-certified/1", "nbx-autotune-certified/2")
|
|
45
|
+
|
|
46
|
+
#: Which proof fields each format requires.
|
|
47
|
+
_REQUIRED = {
|
|
48
|
+
"nbx-autotune-certified/1": ("date", "engine_version", "backend", "shape",
|
|
49
|
+
"deviation", "tolerance", "machine", "oracle"),
|
|
50
|
+
"nbx-autotune-certified/2": ("date", "engine_version", "backend", "shape",
|
|
51
|
+
"deviation", "tolerance", "machine", "oracle",
|
|
52
|
+
"built"),
|
|
53
|
+
}
|
|
54
|
+
#: Every dtype name the launcher can append to a key. The launcher writes
|
|
55
|
+
#: `str(arg.dtype)` of a Triton dtype, so these are TRITON spellings.
|
|
56
|
+
#:
|
|
57
|
+
#: `uint8` was missing, and the omission was silent in the worst way: `key_dtypes`
|
|
58
|
+
#: DROPPED the unknown name instead of refusing it, so a key ending
|
|
59
|
+
#: `('fp16','fp16','fp16','uint8')` returned three dtypes, the bias index fell off the
|
|
60
|
+
#: end, and `synthesize` built an fp16 bias for a uint8 key. The wrapper then computed
|
|
61
|
+
#: an fp16 key — correctly — and the certifier reported the census key UNREACHABLE.
|
|
62
|
+
#: A real miss on MiniCPM-o was read for a day as "the engine cannot produce this key".
|
|
63
|
+
#: Same shape as `_NP` in autotune_certify.py, which did not know an integer dtype and
|
|
64
|
+
#: made it float32 in silence: the same bug written twice is a missing brick.
|
|
65
|
+
_DTYPES = ("fp16", "bf16", "fp32", "fp64",
|
|
66
|
+
"int1", "int8", "int16", "int32", "int64",
|
|
67
|
+
"uint8", "uint16", "uint32", "uint64", "bool")
|
|
68
|
+
#: `built` says the kernel actually COMPILED on the device during the
|
|
69
|
+
#: certifying run. It is required because the screen cannot answer it: a CPU
|
|
70
|
+
#: fallback computes correctly, so its deviation against the fp64 oracle is
|
|
71
|
+
#: excellent — an entry certified on one would record a configuration chosen
|
|
72
|
+
#: for a path that never runs, and nothing in the proof would say so.
|
|
73
|
+
_PROOF_FIELDS = ("date", "engine_version", "backend", "shape", "deviation",
|
|
74
|
+
"tolerance", "machine", "oracle", "built")
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def format_for(entries: Dict[str, Dict]) -> str:
|
|
79
|
+
"""The format a file of these entries may claim: `/2` only when EVERY
|
|
80
|
+
certification in it — primary and variants — carries `built`; `/1`
|
|
81
|
+
otherwise. A writer that stamped `/2` over a `/1` file's entries made the
|
|
82
|
+
gate refuse the whole file (three files, 6 344 entries, 2026-09-13 22:40):
|
|
83
|
+
the stamp is a claim about every entry, and the claim was false."""
|
|
84
|
+
for entry in (entries or {}).values():
|
|
85
|
+
certs = [entry] + list((entry.get("variants") or {}).values()) if isinstance(entry, dict) else [entry]
|
|
86
|
+
for c in certs:
|
|
87
|
+
if not isinstance(c, dict) or "built" not in (c.get("proof") or {}):
|
|
88
|
+
return "nbx-autotune-certified/1"
|
|
89
|
+
return FORMAT
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def restamp(path: Path) -> Optional[str]:
|
|
93
|
+
"""Repair a file's format claim to what its entries satisfy, entries
|
|
94
|
+
untouched. Returns the new format when it changed, None otherwise."""
|
|
95
|
+
doc = json.loads(path.read_text(encoding="utf-8"))
|
|
96
|
+
want = format_for(doc.get("entries") or {})
|
|
97
|
+
if doc.get("format") == want:
|
|
98
|
+
return None
|
|
99
|
+
doc["format"] = want
|
|
100
|
+
tmp = path.with_suffix(".json.tmp")
|
|
101
|
+
tmp.write_text(json.dumps(doc, indent=1, default=str), encoding="utf-8")
|
|
102
|
+
os.replace(tmp, path)
|
|
103
|
+
return want
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
_LOADED: Dict[Tuple[str, str, str, str], Optional[Dict[str, Dict]]] = {} # (vendor, profile, kernel, dtype) -> entries
|
|
107
|
+
_UNIT_CHOICES: Dict[Tuple[str, tuple, Optional[int]], Any] = {} # (kernel, key, memory class) -> `unit_choice`'s answer
|
|
108
|
+
_REFUSED: Dict[str, str] = {} # file -> reason (said once)
|
|
109
|
+
_ANNOUNCED: set = set() # (kernel, key) already said missing
|
|
110
|
+
_SERVED: Dict[str, int] = {"certified": 0, "swept": 0, "local": 0} # local = keys seeded from the machine's replay cache
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
# ---------------------------------------------------------------------------
|
|
114
|
+
# where
|
|
115
|
+
# ---------------------------------------------------------------------------
|
|
116
|
+
# The store and the miss explainer an application may set: `ops.autotune_settings` (standard library only, so an
|
|
117
|
+
# application sets them at start without importing the autotune); read here at call time.
|
|
118
|
+
from nbxtensor.ops.autotune_settings import directory, miss_explainer
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def enabled() -> bool:
|
|
122
|
+
"""NBX_AUTOTUNE_CERTIFIED=off makes every key sweep at runtime (the proof's "runtime sweep" arm)."""
|
|
123
|
+
return os.environ.get("NBX_AUTOTUNE_CERTIFIED", "on").strip().lower() != "off"
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def active_profile() -> Optional[Tuple[str, str]]:
|
|
127
|
+
"""(vendor, profile) of the vendor profile in force, from the profile files themselves."""
|
|
128
|
+
try:
|
|
129
|
+
from nbxtensor.ops._configs import active_vendor_profile
|
|
130
|
+
cfg = active_vendor_profile()
|
|
131
|
+
except Exception:
|
|
132
|
+
return None
|
|
133
|
+
vendor, profile = cfg.get("_vendor"), cfg.get("_profile")
|
|
134
|
+
if not vendor or not profile:
|
|
135
|
+
return None
|
|
136
|
+
return str(vendor), str(profile)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def kernel_short(qual: str) -> str:
|
|
140
|
+
return qual.rsplit(".", 1)[-1]
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def file_for(vendor: str, profile: str, kernel_qual: str, dtype: str, root: Optional[Path] = None) -> Path:
|
|
144
|
+
return (root or directory()) / vendor / profile / f"{kernel_short(kernel_qual)}.{dtype}.json"
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
# ---------------------------------------------------------------------------
|
|
148
|
+
# the key and its dtype
|
|
149
|
+
# ---------------------------------------------------------------------------
|
|
150
|
+
def key_dtypes(key: tuple) -> List[str]:
|
|
151
|
+
"""The dtype names the launcher appended to the key, in argument order.
|
|
152
|
+
|
|
153
|
+
ZERO FALLBACK on an unknown name. Every string the launcher puts in a key is a
|
|
154
|
+
dtype (`key_of` appends `str(arg.dtype)` per tensor argument, after the `keys`
|
|
155
|
+
values, which are the bucket integers). So a string this table does not know is
|
|
156
|
+
not a thing to skip — skipping it SHIFTS every operand after it, and the caller
|
|
157
|
+
then synthesises the wrong dtype for the wrong argument and blames the kernel.
|
|
158
|
+
"""
|
|
159
|
+
out: List[str] = []
|
|
160
|
+
for k in key:
|
|
161
|
+
if not isinstance(k, str):
|
|
162
|
+
continue
|
|
163
|
+
if str(k).lower() not in _DTYPES:
|
|
164
|
+
raise RuntimeError(
|
|
165
|
+
f"certify: key element {k!r} is a string this engine does not recognise as a "
|
|
166
|
+
f"dtype, and every string a key carries is one.\n"
|
|
167
|
+
f" Known: {', '.join(_DTYPES)}.\n"
|
|
168
|
+
f" Dropping it would shift every operand after it and synthesise the wrong "
|
|
169
|
+
f"dtype for the wrong argument — which reads as an UNREACHABLE key and hides "
|
|
170
|
+
f"a real miss.")
|
|
171
|
+
out.append(str(k))
|
|
172
|
+
return out
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def output_dtype(tuner, key: tuple) -> str:
|
|
176
|
+
"""The dtype of the buffer the kernel WRITES — the file's dtype and the
|
|
177
|
+
tolerance's. `key_of` appends one dtype per tensor argument in the
|
|
178
|
+
kernel's argument order, so the i-th dtype belongs to the i-th pointer
|
|
179
|
+
argument; the output pointer is the one named for it."""
|
|
180
|
+
dts = key_dtypes(key)
|
|
181
|
+
if not dts:
|
|
182
|
+
return "any"
|
|
183
|
+
names = [n for n in getattr(tuner, "arg_names", []) if n.endswith("_ptr") or n.endswith("_pointer")]
|
|
184
|
+
if not names:
|
|
185
|
+
# a kernel whose pointers carry no suffix (the m8n8k4 flash: Q, K, V, Bias, Out, Lse) leads with its tensors:
|
|
186
|
+
# read without it, the log-sum-exp's fp32 was taken for the fp16 output and set the tolerance (2026-10-21)
|
|
187
|
+
names = list(getattr(tuner, "arg_names", []))[:len(dts)]
|
|
188
|
+
for i, name in enumerate(names):
|
|
189
|
+
low = name.lower()
|
|
190
|
+
if i < len(dts) and (low.startswith(("out", "c_", "output", "y_")) or low in ("c_ptr", "out_ptr", "output_pointer")):
|
|
191
|
+
return dts[i]
|
|
192
|
+
return dts[-1]
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
#: Key positions that are TENSOR EXTENTS or divisors — every one of them must be at least 1
|
|
196
|
+
#: for the key to name a launch that can exist. A padding may be 0 and is not listed; a
|
|
197
|
+
#: negative value is impossible anywhere. The positions mirror what the certifier unpacks to
|
|
198
|
+
#: synthesise a shape (`autotune_certify._synth`), and a test pins the two together: a key
|
|
199
|
+
#: whose extent is 0 can be neither synthesised nor launched, and the served directory holds
|
|
200
|
+
#: one already (`conv2d_forward_kernel.fp32`, batch_dim 0) from a census that recorded a
|
|
201
|
+
#: shadow's degenerate arithmetic (2026-09-22).
|
|
202
|
+
EXTENT_POSITIONS: Dict[str, tuple] = {
|
|
203
|
+
"matmul_kernel": (0, 1, 2), # M_BUCKET, N, K
|
|
204
|
+
"addmm_kernel": (0, 1, 2),
|
|
205
|
+
"baddbmm_kernel": (0, 1, 2), # M_BUCKET, N_BUCKET, K_BUCKET
|
|
206
|
+
# batch, in_feat, in_h, in_w, out_feat, out_h, out_w, kh, kw, stride_h, stride_w,
|
|
207
|
+
# (padding_h, padding_w may be 0), dilation_h, dilation_w, groups
|
|
208
|
+
"conv2d_forward_kernel": (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 13, 14, 15),
|
|
209
|
+
# C, H_in, W_in, H_out, W_out, kh, kw, stride_h, stride_w, (pad_h, pad_w may be 0)
|
|
210
|
+
"depthwise_conv2d_kernel": (0, 1, 2, 3, 4, 5, 6, 7, 8),
|
|
211
|
+
"fused_moe_kernel": (0, 1, 2, 3), # N, K, NV_BUCKET, NUM_EXPERTS
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def degenerate_extent(qual: str, key: tuple) -> Optional[str]:
|
|
216
|
+
"""The name and value of the first key position that cannot be a launch, or None.
|
|
217
|
+
|
|
218
|
+
A key is refused ONLY on a position this table names: a kernel not listed is answered
|
|
219
|
+
None rather than guessed at, because refusing a legitimate key would shrink a census in
|
|
220
|
+
silence — the failure mode this whole register exists to stop."""
|
|
221
|
+
short = qual.rsplit(".", 1)[-1]
|
|
222
|
+
positions = EXTENT_POSITIONS.get(short)
|
|
223
|
+
if not positions:
|
|
224
|
+
return None
|
|
225
|
+
for i in positions:
|
|
226
|
+
if i < len(key):
|
|
227
|
+
v = key[i]
|
|
228
|
+
if isinstance(v, int) and not isinstance(v, bool) and v < 1:
|
|
229
|
+
return f"position {i} = {v}"
|
|
230
|
+
return None
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def key_repr(key: tuple) -> str:
|
|
234
|
+
return repr(tuple(key))
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def parse_key(text: str) -> Optional[tuple]:
|
|
238
|
+
try:
|
|
239
|
+
value = ast.literal_eval(text)
|
|
240
|
+
except (ValueError, SyntaxError):
|
|
241
|
+
return None
|
|
242
|
+
return tuple(value) if isinstance(value, (tuple, list)) else None
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def describe_key(tuner, key: tuple) -> str:
|
|
246
|
+
"""`M=1500 N=1280 K=1280 IEEE_PRECISION=True … fp32,fp16,fp32` — readable in a log line."""
|
|
247
|
+
names = list(getattr(tuner, "keys", []) or [])
|
|
248
|
+
parts = [f"{n}={v}" for n, v in zip(names, key)]
|
|
249
|
+
rest = [str(v) for v in key[len(names):]]
|
|
250
|
+
return " ".join(parts) + (" " + ",".join(rest) if rest else "")
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
# ---------------------------------------------------------------------------
|
|
254
|
+
# the gate: a file is trusted only if its proof re-reads
|
|
255
|
+
# ---------------------------------------------------------------------------
|
|
256
|
+
def _tolerance_for(vendor: str, profile: str, dtype: str) -> Optional[float]:
|
|
257
|
+
"""The profile's `autotune_screen_rtol[dtype]`, read from the profile file itself."""
|
|
258
|
+
try:
|
|
259
|
+
from nbxtensor.backend import device_files
|
|
260
|
+
cfg = device_files.merged(vendor, profile) # the device's description (library half + application's)
|
|
261
|
+
except FileNotFoundError:
|
|
262
|
+
return None
|
|
263
|
+
table = cfg.get("autotune_screen_rtol") or {}
|
|
264
|
+
canon = {"fp16": "float16", "bf16": "bfloat16", "fp32": "float32", "fp64": "float64"}.get(dtype, dtype)
|
|
265
|
+
val = table.get(canon, table.get(dtype))
|
|
266
|
+
return float(val) if val is not None else None
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
# ---------------------------------------------------------------------------
|
|
270
|
+
# the memory size a proof covers (register entry 56)
|
|
271
|
+
# ---------------------------------------------------------------------------
|
|
272
|
+
# One architecture can be carried by cards of different memory: this rack's V100s are two of 16 GB and two of 32 GB.
|
|
273
|
+
# A proof was made on ONE card, and an entry serves only cards of the memory it was proven on — a card of another
|
|
274
|
+
# size sweeps at runtime, announced. The size is the card's memory rounded to GB (`device_facts.memory_gb`), read from
|
|
275
|
+
# the proof's device record (every committed proof carries one: 76 133 of 76 133 on 2026-10-29) and, for the card
|
|
276
|
+
# running, from the device or what the application declared of it (`device_facts.device_memory_gb`).
|
|
277
|
+
from nbxtensor.backend.device_facts import memory_gb, device_memory_gb, declared_device_memory
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def proof_memory_gb(proof: Optional[Dict[str, Any]]) -> Optional[int]:
|
|
281
|
+
"""The memory (GB) of the card a proof was made on, from its device record (`machine.device.memory_mb`); None when
|
|
282
|
+
the proof has none (it then serves no card: a size is never reconstructed from a name)."""
|
|
283
|
+
device = ((proof or {}).get("machine") or {}).get("device")
|
|
284
|
+
return memory_gb(device.get("memory_mb")) if isinstance(device, dict) else None
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def _variant_slot(cls: int) -> str:
|
|
288
|
+
return f"{int(cls)}g"
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def proof_backend(proof: Optional[Dict[str, Any]]) -> Optional[str]:
|
|
292
|
+
"""The code generator a proof was made with, as one label: `triton <version>`
|
|
293
|
+
plus the backend name when it is not cuda (e.g. `triton 3.7.0 metal`). A
|
|
294
|
+
setting stays correct under any generator — the oracle proved the source,
|
|
295
|
+
not the compiler; what a newer generator may age is its RANK, by a few
|
|
296
|
+
percent — so the directory is re-proven under a new one as an optimisation
|
|
297
|
+
pass and the document reads each rank with its generator's date (owner,
|
|
298
|
+
2026-09-16).
|
|
299
|
+
|
|
300
|
+
A certificate asserts CORRECT and PINNED, not fastest: measured 2026-09-23,
|
|
301
|
+
88.1 % of Apple proofs and 33.2 % of CUDA ones were decided by a margin
|
|
302
|
+
smaller than that host's own spread, so for most entries the rank is not
|
|
303
|
+
established at all and the seated config is pinned for determinism. See
|
|
304
|
+
`docs/reference/what-certified-means.md`."""
|
|
305
|
+
if not proof:
|
|
306
|
+
return None
|
|
307
|
+
b = proof.get("backend") or {}
|
|
308
|
+
ver = b.get("triton")
|
|
309
|
+
if not ver:
|
|
310
|
+
return None
|
|
311
|
+
name = b.get("name")
|
|
312
|
+
label = f"triton {ver}" + (f" {name}" if name and name != "cuda" else "")
|
|
313
|
+
# The out-of-tree backend's source hash, when the identity carries one, is
|
|
314
|
+
# part of the generator: two triton-ext builds share the distribution
|
|
315
|
+
# version but differ here, and the gate must see them apart. In-tree
|
|
316
|
+
# (cuda/amd) proofs carry no hash, so their labels are unchanged.
|
|
317
|
+
bh = b.get("backend_hash")
|
|
318
|
+
if bh:
|
|
319
|
+
label += f" {str(bh)[:12]}"
|
|
320
|
+
return label
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def proof_backends(entry: Dict[str, Any]) -> set:
|
|
324
|
+
"""Every generator label this entry carries a proof from (primary and variants)."""
|
|
325
|
+
out = set()
|
|
326
|
+
lab = proof_backend(entry.get("proof"))
|
|
327
|
+
if lab:
|
|
328
|
+
out.add(lab)
|
|
329
|
+
for slot, var in (entry.get("variants") or {}).items():
|
|
330
|
+
lab = proof_backend((var or {}).get("proof"))
|
|
331
|
+
if lab:
|
|
332
|
+
out.add(lab)
|
|
333
|
+
return out
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def covered_memory_gbs(entry: Dict[str, Any]) -> set:
|
|
337
|
+
"""Every memory class this entry carries a proof for: its primary proof's
|
|
338
|
+
class and each variant's."""
|
|
339
|
+
out = set()
|
|
340
|
+
cls = proof_memory_gb(entry.get("proof"))
|
|
341
|
+
if cls is not None:
|
|
342
|
+
out.add(cls)
|
|
343
|
+
for slot, var in (entry.get("variants") or {}).items():
|
|
344
|
+
vcls = proof_memory_gb((var or {}).get("proof"))
|
|
345
|
+
if vcls is not None:
|
|
346
|
+
out.add(vcls)
|
|
347
|
+
return out
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
_GEN_REFUSED: Dict[str, int] = {}
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
def running_generator() -> Optional[str]:
|
|
354
|
+
"""The code generator about to run, as a proof's `backend` records it.
|
|
355
|
+
|
|
356
|
+
None when Triton cannot be imported: an engine that cannot say which
|
|
357
|
+
generator it is cannot refuse an entry for not matching it, and refusing
|
|
358
|
+
everything because the question is unanswerable is worse than serving.
|
|
359
|
+
"""
|
|
360
|
+
try:
|
|
361
|
+
import triton
|
|
362
|
+
return proof_backend({"backend": generator_identity()})
|
|
363
|
+
except Exception:
|
|
364
|
+
# Triton missing, or the target/backend cannot be identified
|
|
365
|
+
# (generator_identity raises rather than answer a wrong "cuda"): the
|
|
366
|
+
# engine cannot say which generator it is, so it refuses NOTHING for
|
|
367
|
+
# not matching — serving on an unanswerable question beats refusing all.
|
|
368
|
+
return None
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
_OUT_OF_TREE_HASH: Dict[str, Optional[str]] = {}
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
_SAID: set = set()
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def _say_once(msg: str) -> None:
|
|
378
|
+
if msg not in _SAID:
|
|
379
|
+
_SAID.add(msg)
|
|
380
|
+
print(msg, flush=True)
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
def _out_of_tree_backend_hash() -> Optional[str]:
|
|
384
|
+
"""The source hash of the Triton backend that will ACTUALLY generate code,
|
|
385
|
+
but ONLY when that backend is out of tree — its version does not travel in
|
|
386
|
+
the `triton` distribution metadata.
|
|
387
|
+
|
|
388
|
+
`generator_identity`'s `triton` field is the DISTRIBUTION version, which
|
|
389
|
+
tracks the IN-TREE compiler (nvidia/amd): move the Triton pin and it moves.
|
|
390
|
+
An out-of-tree backend — the Metal backend under triton-ext, target `mps` —
|
|
391
|
+
is a separately built shared library whose distribution metadata is a
|
|
392
|
+
static `0.1.0` and does NOT move when triton-ext is rebuilt from a new head
|
|
393
|
+
(5439436 -> b9d5c06d), so a stamp built from the distribution version alone
|
|
394
|
+
would serve certified entries to a generator that had moved. That is the
|
|
395
|
+
same vacuous-gate class as the bare `triton.__version__` the version half
|
|
396
|
+
already fixed.
|
|
397
|
+
|
|
398
|
+
triton's OWN backend `hash()` folds in the compiled `PLUGIN_LIBRARY`'s stat
|
|
399
|
+
and the compiler source, and is exactly what triton puts in its kernel
|
|
400
|
+
cache key — so reusing it means the identity moves precisely when triton
|
|
401
|
+
would recompile. One canonical hash, not a second spelling. The active
|
|
402
|
+
backend is resolved by triton's own `make_backend` (matches `supports_target`),
|
|
403
|
+
so nothing here hardcodes the mps/apple mapping. Returns None for an
|
|
404
|
+
in-tree backend (cuda/amd), whose generator the distribution version
|
|
405
|
+
already tracks, leaving those labels unchanged."""
|
|
406
|
+
from nbxtensor.backend.launcher import target as _target
|
|
407
|
+
try:
|
|
408
|
+
tgt = _target()
|
|
409
|
+
except Exception as exc: # noqa: BLE001 — no driver to ask: see below
|
|
410
|
+
# There is no target to name because there is no device to ask (a CPU-only
|
|
411
|
+
# machine, a census behind `CUDA_VISIBLE_DEVICES=`, a build host). The
|
|
412
|
+
# question "which OUT-OF-TREE backend will generate the code" is then
|
|
413
|
+
# unanswerable, and an unanswerable question refuses nothing — the rule the
|
|
414
|
+
# generator gate is built on. It is said once rather than swallowed, because
|
|
415
|
+
# an out-of-tree backend that silently lost its hash would serve entries to a
|
|
416
|
+
# generator that had moved, which is the defect this hash exists to stop.
|
|
417
|
+
_say_once(f"[autotune] no device to name the code generator ({exc.__class__.__name__}): "
|
|
418
|
+
f"an out-of-tree backend hash cannot be read, so no entry is refused for one")
|
|
419
|
+
return None
|
|
420
|
+
if tgt is None:
|
|
421
|
+
return None
|
|
422
|
+
key = f"{getattr(tgt, 'backend', '?')}:{getattr(tgt, 'arch', '?')}"
|
|
423
|
+
if key in _OUT_OF_TREE_HASH:
|
|
424
|
+
return _OUT_OF_TREE_HASH[key]
|
|
425
|
+
from triton.compiler.compiler import make_backend
|
|
426
|
+
backend = make_backend(tgt)
|
|
427
|
+
module = (type(backend).__module__ or "")
|
|
428
|
+
result = None if module.startswith("triton.backends.") else _backend_content_hash(backend)
|
|
429
|
+
_OUT_OF_TREE_HASH[key] = result
|
|
430
|
+
return result
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
def _backend_content_hash(backend) -> str:
|
|
434
|
+
"""The out-of-tree backend's identity from its CONTENT: the bytes of every source file and shared library in its
|
|
435
|
+
package (and of its plugin library, wherever it lies) and the code-generation gates it declares (`CODEGEN_ENV`,
|
|
436
|
+
set or unset). triton's own `backend.hash()` folds in FILE DATES (the plugin's mtime, every .py's mtime): it decides
|
|
437
|
+
when triton recompiles, which a date may well do; a certificate proves a CODE, and a date is not code. The Mac,
|
|
438
|
+
2026-10-20: the served AppleGPU backend restored byte for byte with `cp -R` moved triton's hash 42ebffa ->
|
|
439
|
+
62109df8 and no Apple certificate served; byte-identical files keep this identity."""
|
|
440
|
+
import hashlib
|
|
441
|
+
import sys as _sys
|
|
442
|
+
mod = _sys.modules.get(type(backend).__module__)
|
|
443
|
+
pkg = Path(getattr(mod, "__file__", "") or "").parent
|
|
444
|
+
h = hashlib.sha256()
|
|
445
|
+
for name in getattr(type(backend), "CODEGEN_ENV", ()) or ():
|
|
446
|
+
h.update(f"{name}={name in os.environ}:{os.environ.get(name, '')}".encode())
|
|
447
|
+
files = sorted(f for f in pkg.rglob("*") if f.is_file() and f.suffix in (".py", ".so", ".dylib")
|
|
448
|
+
and "__pycache__" not in f.parts) if pkg.is_dir() else []
|
|
449
|
+
plugin = getattr(mod, "PLUGIN_LIBRARY", None)
|
|
450
|
+
if plugin is not None and Path(plugin).is_file() and Path(plugin) not in files:
|
|
451
|
+
files.append(Path(plugin))
|
|
452
|
+
for f in files:
|
|
453
|
+
h.update(str(f.relative_to(pkg) if pkg in f.parents else f.name).encode())
|
|
454
|
+
h.update(hashlib.sha256(f.read_bytes()).digest())
|
|
455
|
+
return f"content-{h.hexdigest()[:16]}"
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
def generator_identity() -> Dict[str, Any]:
|
|
459
|
+
"""The compiler's identity, as ONE DOOR for the stamp writer and the gate.
|
|
460
|
+
|
|
461
|
+
Two halves, each chosen for a measured reason:
|
|
462
|
+
|
|
463
|
+
* **The version comes from the DISTRIBUTION metadata, not `__version__`.**
|
|
464
|
+
Moving the Triton pin 5a495ee2 -> 4a15f415 — 114 commits, 92 C++ files —
|
|
465
|
+
left `triton.__version__` at a bare `3.8.0` on BOTH sides, so a label
|
|
466
|
+
built from it served all 964 certified entries to a compiler that had
|
|
467
|
+
moved, and the gate never knew (measured 2026-09-20; the re-sweep found 0
|
|
468
|
+
oracle refusals and 562 rank changes, so the serving was correct and up
|
|
469
|
+
to 58% stale in rank — but the gate's job is to KNOW, not to be lucky).
|
|
470
|
+
`importlib.metadata.version("triton")` reads `3.8.0+git<pin>`, which
|
|
471
|
+
moves when the compiler does. `__version__` remains the fallback for an
|
|
472
|
+
install with no distribution metadata.
|
|
473
|
+
|
|
474
|
+
* **The name is the TRITON TARGET's backend** (`mps` under triton-ext,
|
|
475
|
+
`cuda` on the rack), not the engine's own name (`metal` here either way)
|
|
476
|
+
and not `nbx_tensor.BACKEND_NAME`, a symbol that never existed — reading
|
|
477
|
+
it defaulted every machine to "cuda" and refused all 945 Apple entries
|
|
478
|
+
(fixed 2026-09-19). It no longer defaults to "cuda" at all: a target that
|
|
479
|
+
cannot be read RAISES (ZERO FALLBACK). `autotune_certify._current_backend()`
|
|
480
|
+
documents the metal/mps divergence at length.
|
|
481
|
+
|
|
482
|
+
* **The OUT-OF-TREE backend carries its own source hash** (`backend_hash`,
|
|
483
|
+
present only when the active backend is out of tree). The `triton` field
|
|
484
|
+
above tracks the IN-TREE compiler; the Metal backend (target `mps`) is a
|
|
485
|
+
separately built library whose distribution metadata is a static `0.1.0`,
|
|
486
|
+
so without this a triton-ext move (5439436 -> b9d5c06d) would change the
|
|
487
|
+
compiled kernels while the identity stayed put — the same vacuous-gate
|
|
488
|
+
class as the bare `__version__`. The hash is triton's own backend
|
|
489
|
+
`hash()`, the one it puts in its kernel cache key. See
|
|
490
|
+
`_out_of_tree_backend_hash`.
|
|
491
|
+
|
|
492
|
+
`autotune_certify._backend()` writes this identity into every proof and
|
|
493
|
+
`running_generator()` reads it back: two hands, one spelling, because two
|
|
494
|
+
spellings is how each of those defects happened.
|
|
495
|
+
"""
|
|
496
|
+
try:
|
|
497
|
+
import importlib.metadata as _md
|
|
498
|
+
ver = _md.version("triton")
|
|
499
|
+
except Exception:
|
|
500
|
+
ver = str(triton.__version__)
|
|
501
|
+
from nbxtensor.backend.launcher import target as _target
|
|
502
|
+
tgt = _target()
|
|
503
|
+
name = getattr(tgt, "backend", None)
|
|
504
|
+
if not name:
|
|
505
|
+
# ZERO FALLBACK: a target that cannot be read is an ERROR, not "cuda".
|
|
506
|
+
# A silent default to "cuda" once labelled every Apple run as the rack's
|
|
507
|
+
# generator and refused all 945 Apple entries (2026-09-19). The caller
|
|
508
|
+
# (running_generator) turns this into "cannot say" rather than a wrong
|
|
509
|
+
# answer; the writer (_backend) fails the stamp rather than mis-certify.
|
|
510
|
+
raise RuntimeError(
|
|
511
|
+
"generator_identity: the Triton target names no backend "
|
|
512
|
+
f"({tgt!r}); a certification generator that cannot be identified is "
|
|
513
|
+
"an error, not 'cuda'.")
|
|
514
|
+
ident = {"triton": ver, "name": name}
|
|
515
|
+
bh = _out_of_tree_backend_hash()
|
|
516
|
+
if bh:
|
|
517
|
+
ident["backend_hash"] = bh
|
|
518
|
+
return ident
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
def entry_for_generator(entry: Optional[Dict[str, Any]],
|
|
522
|
+
gen: Optional[str]) -> Optional[Dict[str, Any]]:
|
|
523
|
+
"""The certification proven under THIS generator — the entry when its own
|
|
524
|
+
proof names it, a variant that does, or None.
|
|
525
|
+
|
|
526
|
+
Symmetric with `entry_for_memory_gb`. Register 56 established that an
|
|
527
|
+
entry serves only the memory class it was proven on; the same argument
|
|
528
|
+
applies to the compiler, and `entry_covers(..., need_generator=...)`
|
|
529
|
+
already encodes it for the CERTIFIER (`--reprove-generator`). Nothing asked
|
|
530
|
+
it at the serving side, which is the gap this closes: measured 2026-09-19,
|
|
531
|
+
all 9,655 NVIDIA entries on `main` carry `triton 3.6.0`, the production
|
|
532
|
+
stack runs 3.6.0 and the candidate stack runs 3.8.0, so a stack move would
|
|
533
|
+
have served every one of them under a generator none names.
|
|
534
|
+
"""
|
|
535
|
+
if entry is None or gen is None:
|
|
536
|
+
return None
|
|
537
|
+
if proof_backend(entry.get("proof")) == gen:
|
|
538
|
+
return entry
|
|
539
|
+
for var in (entry.get("variants") or {}).values():
|
|
540
|
+
if proof_backend((var or {}).get("proof")) == gen:
|
|
541
|
+
return var
|
|
542
|
+
return None
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
def generator_refusals() -> Dict[str, int]:
|
|
546
|
+
"""How many keys each kernel was refused for, by generator. For a report."""
|
|
547
|
+
return dict(_GEN_REFUSED)
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
def entry_for_memory_gb(entry: Optional[Dict[str, Any]], cls: Optional[int]) -> Optional[Dict[str, Any]]:
|
|
551
|
+
"""The certification that covers this memory class — the entry itself when
|
|
552
|
+
its primary proof was made at that class, the variant filed under it
|
|
553
|
+
otherwise — or None. None for an unknown class: an engine that cannot say
|
|
554
|
+
which card it is on cannot say it is covered."""
|
|
555
|
+
if entry is None or cls is None:
|
|
556
|
+
return None
|
|
557
|
+
if proof_memory_gb(entry.get("proof")) == cls:
|
|
558
|
+
return entry
|
|
559
|
+
var = (entry.get("variants") or {}).get(_variant_slot(cls))
|
|
560
|
+
if var and proof_memory_gb(var.get("proof")) == cls:
|
|
561
|
+
return var
|
|
562
|
+
return None
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
def proof_records_regime(proof: Optional[Dict[str, Any]]) -> bool:
|
|
566
|
+
"""True when the proof records the stability regime its timings were measured
|
|
567
|
+
under — EITHER the clock every card was held to (`machine.clocks_mhz`, an
|
|
568
|
+
NVIDIA lock) OR the witness that proved the GPU held still across the sweep
|
|
569
|
+
(`stability_witness`, Apple, where the clock cannot be locked).
|
|
570
|
+
|
|
571
|
+
5 628 proofs of 2026-09-07 record neither: made before the door, at a regime
|
|
572
|
+
nothing recorded — their timings read 1.176× those of a proof taken under a
|
|
573
|
+
known one (2026-09-14, register 56). A witness and a lock are two proofs of
|
|
574
|
+
the same fact — the sweep was comparable — so either satisfies this.
|
|
575
|
+
"""
|
|
576
|
+
p = proof or {}
|
|
577
|
+
return bool((p.get("machine") or {}).get("clocks_mhz") or p.get("stability_witness"))
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
# Back-compat alias: the check used to ask only about the clock, before the
|
|
581
|
+
# regime became clock-or-witness. Callers that named the clock keep working.
|
|
582
|
+
proof_records_clock = proof_records_regime
|
|
583
|
+
|
|
584
|
+
|
|
585
|
+
def entry_covers(entries: Dict[str, Dict[str, Any]], ktext: str, cls: Optional[int],
|
|
586
|
+
need_clock: bool = False, need_generator: Optional[str] = None) -> bool:
|
|
587
|
+
"""`--only-missing`'s question, asked per memory class; with `need_clock`
|
|
588
|
+
(`--reprove-unclocked`) a certification whose proof records no clock does
|
|
589
|
+
not count as coverage — it is re-proven at the protocol clock; with
|
|
590
|
+
`need_generator` (`--reprove-generator`, the running code generator's label,
|
|
591
|
+
e.g. `triton 3.8.0`) a certification proven under another generator does
|
|
592
|
+
not count either — a Triton upgrade changes the code it emits, so every
|
|
593
|
+
setting is re-proven under the new one (owner, 2026-09-16)."""
|
|
594
|
+
cert = entry_for_memory_gb(entries.get(ktext), cls)
|
|
595
|
+
if cert is None:
|
|
596
|
+
return False
|
|
597
|
+
if need_clock and not proof_records_clock(cert.get("proof")):
|
|
598
|
+
return False
|
|
599
|
+
if need_generator is not None and proof_backend(cert.get("proof")) != need_generator:
|
|
600
|
+
return False
|
|
601
|
+
return True
|
|
602
|
+
|
|
603
|
+
|
|
604
|
+
def coverage_asked(entries: Dict[str, Dict[str, Any]], ktext: str, cls: Optional[int], only_missing: bool,
|
|
605
|
+
reprove_unclocked: bool, reprove_generator: bool, running_generator: Optional[str]) -> bool:
|
|
606
|
+
"""The certifier's one question before a key: is it covered already, for what the flags ask? `--only-missing`
|
|
607
|
+
asks for a certification THE RUNTIME SERVES — the running code generator's (`entry_for_generator`): an entry
|
|
608
|
+
proven under a retired generator is not served, so it is missing, not present (the Mac, 2026-10-25 02:43: 17
|
|
609
|
+
addmm fp32 entries under a retired generator counted present and withheld HAT's cells). `--reprove-unclocked`
|
|
610
|
+
adds the clock; `--reprove-generator` asks the same generator question and re-proves the stored configuration."""
|
|
611
|
+
if not (only_missing or reprove_unclocked or reprove_generator):
|
|
612
|
+
return False
|
|
613
|
+
return entry_covers(entries, ktext, cls, need_clock=reprove_unclocked,
|
|
614
|
+
need_generator=running_generator if (only_missing or reprove_generator) else None)
|
|
615
|
+
|
|
616
|
+
|
|
617
|
+
def file_certification(entries: Dict[str, Dict[str, Any]], ktext: str, cert: Dict[str, Any]) -> None:
|
|
618
|
+
"""Place a fresh certification in a file's entries by the class its proof
|
|
619
|
+
names: the primary slot when the key is new or the class is the primary's,
|
|
620
|
+
a variant slot otherwise. A proof that cannot say its card is refused — an
|
|
621
|
+
entry no card is ever served is not worth writing. A legacy primary whose
|
|
622
|
+
class is unknown (a rig-wide proof) is REPLACED by the fresh one: it served
|
|
623
|
+
no card, and its config, deviation and timings leave with it — said here
|
|
624
|
+
because a discarded measurement should be discarded on purpose."""
|
|
625
|
+
cls = proof_memory_gb(cert.get("proof"))
|
|
626
|
+
if cls is None:
|
|
627
|
+
raise ValueError("the certification's proof does not say which card's memory it was made on "
|
|
628
|
+
"(no machine.device.memory_mb): refused — an entry without a memory class is served to no card")
|
|
629
|
+
body = {"config": cert["config"], "proof": cert["proof"], "excluded": cert.get("excluded") or []}
|
|
630
|
+
if cert.get("unit"):
|
|
631
|
+
body["unit"] = cert["unit"] # the matrix unit's tile, measured faster than `config` on this class
|
|
632
|
+
cur = entries.get(ktext)
|
|
633
|
+
if cur is None or proof_memory_gb(cur.get("proof")) in (cls, None):
|
|
634
|
+
variants = (cur or {}).get("variants")
|
|
635
|
+
entries[ktext] = body if not variants else {**body, "variants": variants}
|
|
636
|
+
entries[ktext].get("variants", {}).pop(_variant_slot(cls), None)
|
|
637
|
+
if "variants" in entries[ktext] and not entries[ktext]["variants"]:
|
|
638
|
+
del entries[ktext]["variants"]
|
|
639
|
+
return
|
|
640
|
+
cur.setdefault("variants", {})[_variant_slot(cls)] = body
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
#: the fields of a matrix-unit tile (`matrix_unit.mm` rows of the vendor file) a certificate's choice carries
|
|
644
|
+
_UNIT_TILE_FIELDS = ("block_m", "block_n", "block_k", "warps_m", "warps_n", "group_m")
|
|
645
|
+
|
|
646
|
+
_CERTIFYING = [False]
|
|
647
|
+
_UNIT_QUALS: Dict[int, Optional[str]] = {}
|
|
648
|
+
|
|
649
|
+
|
|
650
|
+
def certifying(on: Optional[bool] = None) -> bool:
|
|
651
|
+
"""True while the certifier sweeps a key: a GEMM the matrix unit could take then runs its FMA kernel, whose
|
|
652
|
+
configurations the sweep measures (the unit's tiles are measured beside them, `autotune_certify`). `on` sets it."""
|
|
653
|
+
if on is not None:
|
|
654
|
+
_CERTIFYING[0] = bool(on)
|
|
655
|
+
return _CERTIFYING[0]
|
|
656
|
+
|
|
657
|
+
|
|
658
|
+
def unit_choice(tuner, args, kwargs) -> Optional[Tuple[str, Optional[Dict[str, int]]]]:
|
|
659
|
+
"""What the certificate of this FMA launch chose on the executing card's class: ("unit", tile) when the matrix
|
|
660
|
+
unit was measured faster, ("fma", None) when the FMA kernel was, None when no certificate serves the key — read at
|
|
661
|
+
the key the door forms (`autotune_cache.key_of`), and at its computed twin as the door does."""
|
|
662
|
+
from nbxtensor.ops import autotune_cache as _atc
|
|
663
|
+
qual = _UNIT_QUALS.get(id(tuner))
|
|
664
|
+
if qual is None:
|
|
665
|
+
qual = _UNIT_QUALS[id(tuner)] = _atc._qual_of(tuner) # once per kernel: a GEMM asks at every launch
|
|
666
|
+
if qual is None:
|
|
667
|
+
return None
|
|
668
|
+
key = _atc.key_of(tuner, args, kwargs)
|
|
669
|
+
mcls = executing_memory_gb(args)
|
|
670
|
+
memo = (qual, key, mcls)
|
|
671
|
+
if memo in _UNIT_CHOICES: # a GEMM asks at every launch: the directory's answer, read once per key and class
|
|
672
|
+
return _UNIT_CHOICES[memo]
|
|
673
|
+
cert = lookup(qual, tuner, key, memory_gb=mcls)
|
|
674
|
+
if cert is None:
|
|
675
|
+
twin = computed_key(tuner, key, kwargs)
|
|
676
|
+
cert = lookup(qual, tuner, twin, memory_gb=mcls) if twin is not None else None
|
|
677
|
+
if cert is None:
|
|
678
|
+
choice = None
|
|
679
|
+
else:
|
|
680
|
+
unit = cert.get("unit")
|
|
681
|
+
choice = ("unit", {f: int(unit["tile"][f]) for f in _UNIT_TILE_FIELDS}) if unit else ("fma", None)
|
|
682
|
+
_UNIT_CHOICES[memo] = choice
|
|
683
|
+
return choice
|
|
684
|
+
|
|
685
|
+
|
|
686
|
+
def executing_memory_gb(args=None, device_idx: Optional[int] = None) -> Optional[int]:
|
|
687
|
+
"""The memory (GB) of the card a launch executes on: the device of its first device-resident tensor argument (or
|
|
688
|
+
the index given), as the application declared it or the driver reports it (`device_facts.device_memory_gb`, read
|
|
689
|
+
once per device — never a driver query in the hot path). Without an index: the size every declared device shares;
|
|
690
|
+
on a rig of mixed sizes, or with nothing declared and no card, None."""
|
|
691
|
+
if device_idx is None and args:
|
|
692
|
+
for a in args:
|
|
693
|
+
idx = getattr(a, "_device_idx", None)
|
|
694
|
+
if idx is not None and getattr(a, "_device", None) != "cpu":
|
|
695
|
+
device_idx = int(idx)
|
|
696
|
+
break
|
|
697
|
+
if device_idx is not None:
|
|
698
|
+
return device_memory_gb(int(device_idx))
|
|
699
|
+
declared = declared_device_memory()
|
|
700
|
+
if declared:
|
|
701
|
+
sizes = {memory_gb(b / (1 << 20)) for b in declared.values()}
|
|
702
|
+
return sizes.pop() if len(sizes) == 1 else None
|
|
703
|
+
return None
|
|
704
|
+
|
|
705
|
+
|
|
706
|
+
def _validate_certification(where: str, entry: Any, fmt: str, expected_tol, key, stem_dtype, problems: List[str]) -> None:
|
|
707
|
+
"""The per-certification checks, shared by an entry's primary and its variants."""
|
|
708
|
+
if not isinstance(entry, dict):
|
|
709
|
+
problems.append(f"{where}: not an object")
|
|
710
|
+
return
|
|
711
|
+
cfg = entry.get("config")
|
|
712
|
+
if not isinstance(cfg, dict) or not isinstance(cfg.get("kwargs"), dict) or "num_warps" not in cfg or "num_stages" not in cfg:
|
|
713
|
+
problems.append(f"{where}: no config (kwargs, num_warps, num_stages)")
|
|
714
|
+
proof = entry.get("proof")
|
|
715
|
+
if not isinstance(proof, dict):
|
|
716
|
+
problems.append(f"{where}: no proof")
|
|
717
|
+
return
|
|
718
|
+
missing = [f for f in _REQUIRED[fmt] if f not in proof]
|
|
719
|
+
if missing:
|
|
720
|
+
problems.append(f"{where}: proof without {', '.join(missing)}")
|
|
721
|
+
return
|
|
722
|
+
try:
|
|
723
|
+
dev, tol = float(proof["deviation"]), float(proof["tolerance"])
|
|
724
|
+
except (TypeError, ValueError):
|
|
725
|
+
problems.append(f"{where}: deviation/tolerance are not numbers")
|
|
726
|
+
return
|
|
727
|
+
if not (dev == dev) or dev < 0 or dev > tol:
|
|
728
|
+
problems.append(f"{where}: the proof's deviation {dev:.3e} exceeds its tolerance {tol:.1e}")
|
|
729
|
+
if expected_tol is not None and abs(tol - expected_tol) > 1e-15:
|
|
730
|
+
problems.append(f"{where}: tolerance {tol:.1e} is not the profile's {expected_tol:.1e} for {stem_dtype}")
|
|
731
|
+
if list(proof.get("shape") or []) != list(key):
|
|
732
|
+
problems.append(f"{where}: the proof's shape is not the entry's key")
|
|
733
|
+
try:
|
|
734
|
+
_dt.datetime.fromisoformat(str(proof["date"]).replace("Z", "+00:00"))
|
|
735
|
+
except ValueError:
|
|
736
|
+
problems.append(f"{where}: the date does not parse")
|
|
737
|
+
unit = entry.get("unit")
|
|
738
|
+
if unit is not None:
|
|
739
|
+
tile = unit.get("tile") if isinstance(unit, dict) else None
|
|
740
|
+
if not isinstance(tile, dict) or not all(isinstance(tile.get(f), int) for f in _UNIT_TILE_FIELDS):
|
|
741
|
+
problems.append(f"{where}: a matrix-unit choice without its tile ({', '.join(_UNIT_TILE_FIELDS)})")
|
|
742
|
+
else:
|
|
743
|
+
try:
|
|
744
|
+
udev, ums, fms = float(unit["deviation"]), float(unit["best_ms"]), float(unit["fma_best_ms"])
|
|
745
|
+
except (KeyError, TypeError, ValueError):
|
|
746
|
+
problems.append(f"{where}: a matrix-unit choice without its deviation and both timings")
|
|
747
|
+
else:
|
|
748
|
+
if not (udev == udev) or udev < 0 or udev > tol:
|
|
749
|
+
problems.append(f"{where}: the matrix unit's deviation {udev:.3e} exceeds the tolerance {tol:.1e}")
|
|
750
|
+
if not ums < fms:
|
|
751
|
+
problems.append(f"{where}: the matrix unit chosen at {ums:.4f} ms against the FMA kernel's {fms:.4f}")
|
|
752
|
+
for ex in entry.get("excluded") or []:
|
|
753
|
+
try:
|
|
754
|
+
if float(ex.get("deviation")) <= tol:
|
|
755
|
+
problems.append(f"{where}: an excluded setting carries a deviation within the tolerance")
|
|
756
|
+
except (TypeError, ValueError, AttributeError):
|
|
757
|
+
problems.append(f"{where}: an excluded setting without a deviation")
|
|
758
|
+
|
|
759
|
+
|
|
760
|
+
def validate(doc: Any, path: Path, tuner=None) -> List[str]:
|
|
761
|
+
"""Every reason this file cannot be trusted; empty when its proof re-reads.
|
|
762
|
+
|
|
763
|
+
Checked: the format, that vendor/profile/kernel/dtype inside match the path,
|
|
764
|
+
that each entry has a config and a complete proof, that the proof's
|
|
765
|
+
deviation does not exceed its tolerance, that the tolerance is the
|
|
766
|
+
profile's for that dtype, that every excluded setting carries a deviation
|
|
767
|
+
ABOVE the tolerance, that the key parses and (with the tuner) has the
|
|
768
|
+
kernel's arity and the file's dtype, that the date parses; and that every
|
|
769
|
+
variant is filed under the memory class its own proof names."""
|
|
770
|
+
problems: List[str] = []
|
|
771
|
+
fmt = doc.get("format") if isinstance(doc, dict) else None
|
|
772
|
+
if fmt not in _FORMATS:
|
|
773
|
+
return [f"format is not one of {list(_FORMATS)!r}"]
|
|
774
|
+
vendor, profile = path.parent.parent.name, path.parent.name
|
|
775
|
+
stem_kernel, stem_dtype = path.stem.rsplit(".", 1) if "." in path.stem else (path.stem, "")
|
|
776
|
+
if doc.get("vendor") != vendor or doc.get("profile") != profile:
|
|
777
|
+
problems.append(f"vendor/profile inside ({doc.get('vendor')}/{doc.get('profile')}) differ from the path ({vendor}/{profile})")
|
|
778
|
+
if kernel_short(str(doc.get("kernel", ""))) != stem_kernel:
|
|
779
|
+
problems.append(f"kernel inside ({doc.get('kernel')}) differs from the file name ({stem_kernel})")
|
|
780
|
+
if str(doc.get("dtype")) != stem_dtype:
|
|
781
|
+
problems.append(f"dtype inside ({doc.get('dtype')}) differs from the file name ({stem_dtype})")
|
|
782
|
+
entries = doc.get("entries")
|
|
783
|
+
if not isinstance(entries, dict) or not entries:
|
|
784
|
+
problems.append("no entries")
|
|
785
|
+
return problems
|
|
786
|
+
expected_tol = _tolerance_for(vendor, profile, stem_dtype)
|
|
787
|
+
# the kernel's parameter spaces: an entry naming parameters no config of the kernel has is not servable (the Apple
|
|
788
|
+
# depthwise entries of the old BLOCK_HW/BLOCK_C tile passed this gate against the BC/TH/TW kernel, 2026-11-03)
|
|
789
|
+
spaces = {frozenset(c.kwargs) for c in getattr(tuner, "configs", None) or []}
|
|
790
|
+
for ktext, entry in entries.items():
|
|
791
|
+
where = f"entry {ktext[:80]}"
|
|
792
|
+
key = parse_key(ktext)
|
|
793
|
+
if key is None:
|
|
794
|
+
problems.append(f"{where}: the key does not parse as a shape key")
|
|
795
|
+
continue
|
|
796
|
+
if tuner is not None:
|
|
797
|
+
arity = len(getattr(tuner, "keys", []) or [])
|
|
798
|
+
if len(key) < arity:
|
|
799
|
+
problems.append(f"{where}: the key has {len(key)} fields, the kernel's key has {arity}")
|
|
800
|
+
if output_dtype(tuner, key) != stem_dtype:
|
|
801
|
+
problems.append(f"{where}: the key's output dtype is {output_dtype(tuner, key)}, the file's is {stem_dtype}")
|
|
802
|
+
_validate_certification(where, entry, fmt, expected_tol, key, stem_dtype, problems)
|
|
803
|
+
_validate_parameter_names(where, entry, spaces, problems)
|
|
804
|
+
if not isinstance(entry, dict):
|
|
805
|
+
continue
|
|
806
|
+
variants = entry.get("variants")
|
|
807
|
+
if variants is None:
|
|
808
|
+
continue
|
|
809
|
+
if not isinstance(variants, dict):
|
|
810
|
+
problems.append(f"{where}: variants is not an object")
|
|
811
|
+
continue
|
|
812
|
+
for slot, var in variants.items():
|
|
813
|
+
vwhere = f"{where} variant {slot}"
|
|
814
|
+
_validate_certification(vwhere, var, fmt, expected_tol, key, stem_dtype, problems)
|
|
815
|
+
_validate_parameter_names(vwhere, var, spaces, problems)
|
|
816
|
+
vcls = proof_memory_gb(var.get("proof")) if isinstance(var, dict) else None
|
|
817
|
+
if vcls is None:
|
|
818
|
+
problems.append(f"{vwhere}: its proof does not say which card's memory it was made on")
|
|
819
|
+
elif _variant_slot(vcls) != str(slot):
|
|
820
|
+
problems.append(f"{vwhere}: filed under {slot}, its proof was made on a {vcls} GB card")
|
|
821
|
+
return problems
|
|
822
|
+
|
|
823
|
+
|
|
824
|
+
def _validate_parameter_names(where: str, cert: Any, spaces: set, problems: List[str]) -> None:
|
|
825
|
+
names = frozenset(((cert.get("config") or {}).get("kwargs") or {})) if isinstance(cert, dict) else None
|
|
826
|
+
if spaces and names is not None and names not in spaces:
|
|
827
|
+
problems.append(f"{where}: its config parameters {sorted(names)} are not the kernel's "
|
|
828
|
+
f"{' or '.join(str(sorted(s)) for s in sorted(spaces, key=sorted))}")
|
|
829
|
+
|
|
830
|
+
|
|
831
|
+
def files(root: Optional[Path] = None) -> Iterator[Path]:
|
|
832
|
+
base = root or directory()
|
|
833
|
+
if not base.exists():
|
|
834
|
+
return iter(())
|
|
835
|
+
return iter(sorted(base.glob("*/*/*.json")))
|
|
836
|
+
|
|
837
|
+
|
|
838
|
+
# ---------------------------------------------------------------------------
|
|
839
|
+
# lookup at load
|
|
840
|
+
# ---------------------------------------------------------------------------
|
|
841
|
+
def _load(vendor: str, profile: str, kernel_qual: str, dtype: str, tuner=None) -> Optional[Dict[str, Dict]]:
|
|
842
|
+
ident = (vendor, profile, kernel_qual, dtype)
|
|
843
|
+
if ident in _LOADED:
|
|
844
|
+
return _LOADED[ident]
|
|
845
|
+
path = file_for(vendor, profile, kernel_qual, dtype)
|
|
846
|
+
entries: Optional[Dict[str, Dict]] = None
|
|
847
|
+
if path.exists():
|
|
848
|
+
try:
|
|
849
|
+
doc = json.loads(path.read_text(encoding="utf-8"))
|
|
850
|
+
except (OSError, ValueError) as exc:
|
|
851
|
+
doc = None
|
|
852
|
+
problems = [f"unreadable: {exc}"]
|
|
853
|
+
else:
|
|
854
|
+
problems = validate(doc, path, tuner)
|
|
855
|
+
if problems:
|
|
856
|
+
if str(path) not in _REFUSED:
|
|
857
|
+
_REFUSED[str(path)] = problems[0]
|
|
858
|
+
print(f"[autotune] certified file REFUSED {path}: {problems[0]}"
|
|
859
|
+
+ (f" (+{len(problems) - 1} more)" if len(problems) > 1 else "")
|
|
860
|
+
+ " — its keys sweep at runtime", flush=True)
|
|
861
|
+
elif isinstance(doc, dict):
|
|
862
|
+
entries = dict(doc["entries"])
|
|
863
|
+
_LOADED[ident] = entries
|
|
864
|
+
return entries
|
|
865
|
+
|
|
866
|
+
|
|
867
|
+
def lookup(kernel_qual: str, tuner, key: tuple, ignore_switch: bool = False,
|
|
868
|
+
memory_gb: Optional[int] = None, any_class: bool = False) -> Optional[Dict[str, Any]]:
|
|
869
|
+
"""The certification for this profile, kernel, dtype, shape AND the memory
|
|
870
|
+
class of the executing card, or None. An entry proven on another class is
|
|
871
|
+
not served (`entry_for_memory_gb`); `memory_gb` None means the card
|
|
872
|
+
is unknown, and unknown is served nothing. `any_class`: the raw entry
|
|
873
|
+
whatever its class — for a report that must name what exists, never to
|
|
874
|
+
serve. `ignore_switch`: read the directory even when
|
|
875
|
+
NBX_AUTOTUNE_CERTIFIED=off — the switch stops APPLYING a certification,
|
|
876
|
+
never REPORTING against one."""
|
|
877
|
+
if not enabled() and not ignore_switch:
|
|
878
|
+
return None
|
|
879
|
+
ident = active_profile()
|
|
880
|
+
if ident is None:
|
|
881
|
+
return None
|
|
882
|
+
entries = _load(ident[0], ident[1], kernel_qual, output_dtype(tuner, key), tuner)
|
|
883
|
+
if not entries:
|
|
884
|
+
return None
|
|
885
|
+
entry = entries.get(key_repr(key))
|
|
886
|
+
if any_class:
|
|
887
|
+
return entry
|
|
888
|
+
cert = entry_for_memory_gb(entry, memory_gb)
|
|
889
|
+
if cert is None:
|
|
890
|
+
return None
|
|
891
|
+
# THE GENERATOR, asked here for the same reason the memory class is: a
|
|
892
|
+
# setting is a property of the compiler that produced it as much as of the
|
|
893
|
+
# shape. `NBX_AUTOTUNE_ANY_GENERATOR=1` is the one opening, named so it
|
|
894
|
+
# reads as deliberate — it serves a setting proven under another compiler,
|
|
895
|
+
# which the owner's 2026-09-16 note allows as still CORRECT while no longer
|
|
896
|
+
# necessarily the FASTEST.
|
|
897
|
+
import os as _os_gen
|
|
898
|
+
if _os_gen.environ.get("NBX_AUTOTUNE_ANY_GENERATOR") == "1":
|
|
899
|
+
return cert
|
|
900
|
+
gen = running_generator()
|
|
901
|
+
if gen is None:
|
|
902
|
+
return cert # cannot ask: do not refuse
|
|
903
|
+
matched = entry_for_generator(cert, gen)
|
|
904
|
+
if matched is not None:
|
|
905
|
+
return matched
|
|
906
|
+
was = proof_backend(cert.get("proof"))
|
|
907
|
+
if was is None:
|
|
908
|
+
return cert # the proof does not say: not a mismatch
|
|
909
|
+
n = _GEN_REFUSED.get(kernel_qual, 0) + 1
|
|
910
|
+
_GEN_REFUSED[kernel_qual] = n
|
|
911
|
+
if n == 1: # once per kernel, not once per shape
|
|
912
|
+
print(f"[autotune] {kernel_qual}: the directory's settings were certified "
|
|
913
|
+
f"under {was} and this engine runs {gen}. They are not served: a "
|
|
914
|
+
f"configuration is a property of the compiler that produced it. "
|
|
915
|
+
f"These shapes sweep at runtime and land in the local replay cache. "
|
|
916
|
+
f"`neurobrix autotune certify --profile <p>` re-proves them; "
|
|
917
|
+
f"NBX_AUTOTUNE_ANY_GENERATOR=1 serves them anyway.", flush=True)
|
|
918
|
+
return None
|
|
919
|
+
|
|
920
|
+
|
|
921
|
+
# A promotion flag that widens a pointer's tile on load: the kernel computes
|
|
922
|
+
# that operand in fp32 whatever its memory dtype, so the key that names the
|
|
923
|
+
# COMPUTATION carries fp32 for it — the directory's entries were certified
|
|
924
|
+
# with the operand widened in memory (the copy the promotion replaced), and
|
|
925
|
+
# the same setting applies. PROMOTE_B is NOT here: it was keyed by the memory
|
|
926
|
+
# dtype from the first certification (`fp32,fp16,fp32`), and stays so.
|
|
927
|
+
_KEYED_AS_COMPUTED = {"PROMOTE_A": "a_ptr", "PROMOTE_BIAS": "bias_ptr"}
|
|
928
|
+
|
|
929
|
+
|
|
930
|
+
def _promotions_by_policy(tuner, tags: List[str]) -> Dict[str, bool]:
|
|
931
|
+
"""Which operands the wrappers widen on load for a key, read from the dtype
|
|
932
|
+
tags under the hardware profile's rule — the same rule `mm`/`addmm` apply:
|
|
933
|
+
on a card without native bf16 a narrow activation computes in fp32, and a
|
|
934
|
+
narrow bias too whenever the activation computes in fp32. Used where no
|
|
935
|
+
call is at hand (the local replay cache's keys at load)."""
|
|
936
|
+
names = [n for n in getattr(tuner, "arg_names", []) if n.endswith("_ptr") or n.endswith("_pointer")]
|
|
937
|
+
if "PROMOTE_A" not in getattr(tuner, "arg_names", []):
|
|
938
|
+
return {}
|
|
939
|
+
try:
|
|
940
|
+
from nbxtensor.ops.wrappers import _NBX_HAS_NATIVE_BF16 as native_bf16
|
|
941
|
+
except Exception:
|
|
942
|
+
return {}
|
|
943
|
+
def tag(ptr):
|
|
944
|
+
return str(tags[names.index(ptr)]).lower() if ptr in names and names.index(ptr) < len(tags) else ""
|
|
945
|
+
a_narrow = tag("a_ptr") in ("fp16", "bf16")
|
|
946
|
+
promote_a = bool(a_narrow and not native_bf16)
|
|
947
|
+
a_fp32 = promote_a or tag("a_ptr") == "fp32"
|
|
948
|
+
promote_bias = bool(a_fp32 and tag("bias_ptr") in ("fp16", "bf16"))
|
|
949
|
+
return {"PROMOTE_A": promote_a, "PROMOTE_BIAS": promote_bias}
|
|
950
|
+
|
|
951
|
+
|
|
952
|
+
def computed_key(tuner, key: tuple, kwargs: Optional[Dict[str, Any]] = None) -> Optional[tuple]:
|
|
953
|
+
"""The key with every pointer a set promotion flag widens on load tagged
|
|
954
|
+
fp32 — the dtype the kernel computes it in — or None when no tag changes.
|
|
955
|
+
`kwargs` are the call's; None reads the flags from the profile's rule."""
|
|
956
|
+
names = [n for n in getattr(tuner, "arg_names", []) if n.endswith("_ptr") or n.endswith("_pointer")]
|
|
957
|
+
nkeys = len(list(getattr(tuner, "keys", []) or []))
|
|
958
|
+
head, tags = list(key[:nkeys]), list(key[nkeys:])
|
|
959
|
+
if kwargs is None:
|
|
960
|
+
kwargs = _promotions_by_policy(tuner, tags)
|
|
961
|
+
changed = False
|
|
962
|
+
for flag, ptr in _KEYED_AS_COMPUTED.items():
|
|
963
|
+
if kwargs.get(flag) and ptr in names:
|
|
964
|
+
i = names.index(ptr)
|
|
965
|
+
if i < len(tags) and str(tags[i]).lower() in ("fp16", "bf16"):
|
|
966
|
+
tags[i] = "fp32"
|
|
967
|
+
changed = True
|
|
968
|
+
return tuple(head + tags) if changed else None
|
|
969
|
+
|
|
970
|
+
|
|
971
|
+
def apply(kernel_qual: str, tuner, key: tuple, lookup_key: Optional[tuple] = None,
|
|
972
|
+
memory_gb: Optional[int] = None) -> bool:
|
|
973
|
+
"""Put the certified config in the tuner's cache for `key` — no sweep, no
|
|
974
|
+
screen. False when the directory has nothing for it ON THIS CARD's memory
|
|
975
|
+
class. `lookup_key`: the key the directory is read at when it differs from
|
|
976
|
+
the tuner's (a promoted operand keyed by its computed dtype,
|
|
977
|
+
`computed_key`). `memory_gb`: the executing card's, from
|
|
978
|
+
`executing_memory_gb(args)` at a launch site; None here means "the
|
|
979
|
+
class every device of the profile in force shares", which is None — served
|
|
980
|
+
nothing — on a heterogeneous rig where the launch site did not say."""
|
|
981
|
+
if memory_gb is None:
|
|
982
|
+
memory_gb = executing_memory_gb()
|
|
983
|
+
entry = lookup(kernel_qual, tuner, lookup_key or key, memory_gb=memory_gb)
|
|
984
|
+
if entry is None:
|
|
985
|
+
return False
|
|
986
|
+
import triton
|
|
987
|
+
cfg = entry["config"]
|
|
988
|
+
tuner.cache[key] = triton.Config(dict(cfg["kwargs"]), num_warps=int(cfg["num_warps"]),
|
|
989
|
+
num_stages=int(cfg["num_stages"]), num_ctas=int(cfg.get("num_ctas") or 1),
|
|
990
|
+
maxnreg=cfg.get("maxnreg"))
|
|
991
|
+
_SERVED["certified"] += 1
|
|
992
|
+
return True
|
|
993
|
+
|
|
994
|
+
|
|
995
|
+
def override_seeded(tuners=None) -> int:
|
|
996
|
+
"""The directory first, even on a warm machine: every key the local replay
|
|
997
|
+
cache seeded into a tuner that the directory certifies takes the certified
|
|
998
|
+
config instead (no sweep, no screen). Returns how many were overridden —
|
|
999
|
+
counted as certified, no longer as local. Without this the seed ran before
|
|
1000
|
+
the lookup and a warm machine never applied a certified setting (Flex.1 on
|
|
1001
|
+
2026-09-07: 2,717 entries in the directory, 0 served, 5,547 from the cache)."""
|
|
1002
|
+
if not enabled():
|
|
1003
|
+
return 0
|
|
1004
|
+
if tuners is None:
|
|
1005
|
+
from nbxtensor.ops.autotune_cache import _autotuners
|
|
1006
|
+
tuners = list(_autotuners())
|
|
1007
|
+
# The card is not known at seed time. When every device of the profile in
|
|
1008
|
+
# force shares one memory class (a pinned card, a homogeneous rig) the
|
|
1009
|
+
# class is that one and the directory is applied here. On a heterogeneous
|
|
1010
|
+
# rig it is None: a seeded key the directory certifies for SOME class is
|
|
1011
|
+
# then EVICTED, its local config remembered, so the launch site — which
|
|
1012
|
+
# knows the tensor's card — decides: the certified setting for that
|
|
1013
|
+
# card's class, or the local config put back (`reseed_evicted`).
|
|
1014
|
+
mcls = executing_memory_gb()
|
|
1015
|
+
n = 0
|
|
1016
|
+
for qual, tuner in tuners:
|
|
1017
|
+
cache = getattr(tuner, "cache", None)
|
|
1018
|
+
if not cache:
|
|
1019
|
+
continue
|
|
1020
|
+
for key in list(cache.keys()):
|
|
1021
|
+
if not isinstance(key, tuple):
|
|
1022
|
+
continue
|
|
1023
|
+
twin = None
|
|
1024
|
+
try:
|
|
1025
|
+
entry = lookup(qual, tuner, key, any_class=True)
|
|
1026
|
+
if entry is None:
|
|
1027
|
+
twin = computed_key(tuner, key) # a widened-on-load operand, by the profile's rule
|
|
1028
|
+
entry = lookup(qual, tuner, twin, any_class=True) if twin else None
|
|
1029
|
+
except Exception:
|
|
1030
|
+
entry = None
|
|
1031
|
+
if entry is None:
|
|
1032
|
+
continue
|
|
1033
|
+
local = cache.pop(key, None)
|
|
1034
|
+
if mcls is not None:
|
|
1035
|
+
if apply(qual, tuner, key, lookup_key=twin, memory_gb=mcls):
|
|
1036
|
+
n += 1
|
|
1037
|
+
elif local is not None:
|
|
1038
|
+
cache[key] = local # certified elsewhere, not for this card: the local config stays
|
|
1039
|
+
elif local is not None:
|
|
1040
|
+
_EVICTED[(qual, key)] = (local, twin)
|
|
1041
|
+
_SERVED["local"] = max(0, _SERVED["local"] - n)
|
|
1042
|
+
return n
|
|
1043
|
+
|
|
1044
|
+
|
|
1045
|
+
#: (kernel, key) -> (local triton.Config, twin key): seeds evicted by
|
|
1046
|
+
#: `override_seeded` on a rig whose card was unknown at seed time.
|
|
1047
|
+
_EVICTED: Dict[Tuple[str, tuple], Tuple[Any, Optional[tuple]]] = {}
|
|
1048
|
+
|
|
1049
|
+
|
|
1050
|
+
def served_evicted(kernel_qual: str, key: tuple) -> None:
|
|
1051
|
+
"""A key evicted at seed time that the launch site served CERTIFIED: it
|
|
1052
|
+
is no longer a local key (the seed count was taken at seed time)."""
|
|
1053
|
+
if _EVICTED.pop((kernel_qual, key), None) is not None:
|
|
1054
|
+
_SERVED["local"] = max(0, _SERVED["local"] - 1)
|
|
1055
|
+
|
|
1056
|
+
|
|
1057
|
+
def reseed_evicted(kernel_qual: str, tuner, key: tuple) -> bool:
|
|
1058
|
+
"""Put back the local replay config `override_seeded` evicted for this
|
|
1059
|
+
key when the directory does not cover the executing card's class — the
|
|
1060
|
+
machine's own earlier sweep, not a sweep again. True when one was put back."""
|
|
1061
|
+
rec = _EVICTED.pop((kernel_qual, key), None)
|
|
1062
|
+
if rec is None:
|
|
1063
|
+
return False
|
|
1064
|
+
cache = getattr(tuner, "cache", None)
|
|
1065
|
+
if cache is None:
|
|
1066
|
+
return False
|
|
1067
|
+
cache[key] = rec[0]
|
|
1068
|
+
return True
|
|
1069
|
+
|
|
1070
|
+
|
|
1071
|
+
def certified_only() -> bool:
|
|
1072
|
+
"""A CONFIRMATION run (`neurobrix run --certified-only`, or NBX_AUTOTUNE_CERTIFIED_ONLY=1): every key
|
|
1073
|
+
is served from the certified directory or the run fails — never a runtime sweep, never the local
|
|
1074
|
+
replay cache (which holds earlier sweeps, not certifications). The owner's method, 2026-09-28:
|
|
1075
|
+
the census and the certifier do the autotune without running models; a confirmation run that
|
|
1076
|
+
swept would hide the census defect that let the key be missed."""
|
|
1077
|
+
return os.environ.get("NBX_AUTOTUNE_CERTIFIED_ONLY", "").strip().lower() in ("1", "true", "yes", "on")
|
|
1078
|
+
|
|
1079
|
+
|
|
1080
|
+
class KeyNotCertified(RuntimeError):
|
|
1081
|
+
"""A key a confirmation run met that the certified directory does not serve."""
|
|
1082
|
+
|
|
1083
|
+
|
|
1084
|
+
def _explain_miss(kernel_qual: str, key: tuple, memory_gb: Optional[int]) -> str:
|
|
1085
|
+
explain = miss_explainer()
|
|
1086
|
+
if explain is None:
|
|
1087
|
+
return ""
|
|
1088
|
+
try:
|
|
1089
|
+
return f" {explain(kernel_qual, key, memory_gb)}."
|
|
1090
|
+
except Exception as exc: # noqa: BLE001 — said: the refusal stands, its explanation failed
|
|
1091
|
+
return f" (the application's explanation of the miss failed: {type(exc).__name__}: {exc})."
|
|
1092
|
+
|
|
1093
|
+
|
|
1094
|
+
def refuse_missing(kernel_qual: str, tuner, key: tuple) -> None:
|
|
1095
|
+
"""The certified-only answer to a miss: raised before any bench, naming the key, the profile,
|
|
1096
|
+
why a directory entry (if any) is not served, and what the census says."""
|
|
1097
|
+
prof = active_profile()
|
|
1098
|
+
where = f"{prof[0]}/{prof[1]}" if prof else "the profile in force"
|
|
1099
|
+
why = ""
|
|
1100
|
+
here = None
|
|
1101
|
+
try:
|
|
1102
|
+
here = executing_memory_gb(list((getattr(tuner, "nargs", None) or {}).values()) or None)
|
|
1103
|
+
except Exception: # noqa: BLE001 — an unknown size is said by the explanation
|
|
1104
|
+
here = None
|
|
1105
|
+
try:
|
|
1106
|
+
raw = lookup(kernel_qual, tuner, key, ignore_switch=True, any_class=True)
|
|
1107
|
+
if raw is not None:
|
|
1108
|
+
covered = sorted(covered_memory_gbs(raw))
|
|
1109
|
+
why = (f"; the directory certifies it for {', '.join(f'{c} GB' for c in covered) or 'no known memory class'} "
|
|
1110
|
+
f"and this card is {f'{here} GB' if here is not None else 'of unknown memory'} (register 56)")
|
|
1111
|
+
except Exception as exc: # noqa: BLE001 — the reason failed to compute: said, the refusal stands
|
|
1112
|
+
why = f"; (could not read what the directory holds for it: {exc})"
|
|
1113
|
+
raise KeyNotCertified(
|
|
1114
|
+
f"CERTIFIED-ONLY: no certified setting for {kernel_short(kernel_qual)} {output_dtype(tuner, key)} "
|
|
1115
|
+
f"({describe_key(tuner, key)}) on {where}{why}.{_explain_miss(kernel_qual, key, here)} A confirmation run "
|
|
1116
|
+
f"never sweeps.")
|
|
1117
|
+
|
|
1118
|
+
|
|
1119
|
+
def announce_missing(kernel_qual: str, tuner, key: tuple) -> None:
|
|
1120
|
+
"""Said once per key, in clear: the runtime is about to sweep."""
|
|
1121
|
+
ident = (kernel_qual, key)
|
|
1122
|
+
if ident in _ANNOUNCED:
|
|
1123
|
+
return
|
|
1124
|
+
_ANNOUNCED.add(ident)
|
|
1125
|
+
_SERVED["swept"] += 1
|
|
1126
|
+
prof = active_profile()
|
|
1127
|
+
where = f"{prof[0]}/{prof[1]}" if prof else "the profile in force"
|
|
1128
|
+
try:
|
|
1129
|
+
from nbxtensor.ops.autotune_cache import _DIR as _local
|
|
1130
|
+
except Exception:
|
|
1131
|
+
_local = "the local replay cache"
|
|
1132
|
+
# Say WHY when an entry exists and is not served: proven on another card's
|
|
1133
|
+
# memory. A silence here would read as "the directory never had it".
|
|
1134
|
+
why = ""
|
|
1135
|
+
try:
|
|
1136
|
+
raw = lookup(kernel_qual, tuner, key, ignore_switch=True, any_class=True)
|
|
1137
|
+
if raw is not None:
|
|
1138
|
+
covered = sorted(covered_memory_gbs(raw))
|
|
1139
|
+
here = executing_memory_gb(list((getattr(tuner, "nargs", None) or {}).values()) or None)
|
|
1140
|
+
why = (f" — certified for {', '.join(f'{c} GB' for c in covered) or 'no known memory class'}, "
|
|
1141
|
+
f"this card is {f'{here} GB' if here is not None else 'of unknown memory'}, not served (register 56)")
|
|
1142
|
+
except Exception as exc: # the reason failed to compute: say that, never an empty reason
|
|
1143
|
+
why = f" — (could not read what the directory holds for it: {exc})"
|
|
1144
|
+
print(f"[autotune] no certified setting for {kernel_short(kernel_qual)} {output_dtype(tuner, key)} "
|
|
1145
|
+
f"({describe_key(tuner, key)}) on {where}{why}: sweeping at runtime with the consensus screen; the result is "
|
|
1146
|
+
f"kept in {_local}, never in the engine's directory — `neurobrix autotune certify --profile "
|
|
1147
|
+
f"{prof[1] if prof else '<profile>'}` certifies it", flush=True)
|
|
1148
|
+
|
|
1149
|
+
|
|
1150
|
+
def served() -> Dict[str, int]:
|
|
1151
|
+
"""How many keys this process took from the directory, how many it swept, how many came from the local replay cache."""
|
|
1152
|
+
return dict(_SERVED)
|
|
1153
|
+
|
|
1154
|
+
|
|
1155
|
+
def note_local(n: int) -> None:
|
|
1156
|
+
"""Keys the local replay cache seeded into the autotuners (a previous runtime sweep on this machine)."""
|
|
1157
|
+
_SERVED["local"] += int(n or 0)
|
|
1158
|
+
|
|
1159
|
+
|
|
1160
|
+
def reset() -> None:
|
|
1161
|
+
_LOADED.clear(); _REFUSED.clear(); _ANNOUNCED.clear(); _EVICTED.clear(); _UNIT_CHOICES.clear()
|
|
1162
|
+
_SERVED.update({"certified": 0, "swept": 0, "local": 0})
|
|
1163
|
+
|
|
1164
|
+
|
|
1165
|
+
def certified_config_for(kernel_name: str, key_fields: tuple) -> Optional[Dict[str, Any]]:
|
|
1166
|
+
"""For the screen: the certified entry of a kernel (short name) whose key
|
|
1167
|
+
starts with these fields on the profile in force, or None. Used to report
|
|
1168
|
+
a runtime exclusion that contradicts a certification."""
|
|
1169
|
+
ident = active_profile()
|
|
1170
|
+
if ident is None:
|
|
1171
|
+
return None
|
|
1172
|
+
root = directory() / ident[0] / ident[1]
|
|
1173
|
+
if not root.exists():
|
|
1174
|
+
return None
|
|
1175
|
+
for path in sorted(root.glob(f"{kernel_name}.*.json")):
|
|
1176
|
+
try:
|
|
1177
|
+
doc = json.loads(path.read_text(encoding="utf-8"))
|
|
1178
|
+
except (OSError, ValueError):
|
|
1179
|
+
continue
|
|
1180
|
+
if validate(doc, path):
|
|
1181
|
+
continue
|
|
1182
|
+
for ktext, entry in (doc.get("entries") or {}).items():
|
|
1183
|
+
key = parse_key(ktext)
|
|
1184
|
+
if key is not None and tuple(key[:len(key_fields)]) == tuple(key_fields):
|
|
1185
|
+
return {"path": str(path), "key": key, **entry}
|
|
1186
|
+
return None
|
|
1187
|
+
|
|
1188
|
+
|
|
1189
|
+
# ---------------------------------------------------------------------------
|
|
1190
|
+
# contradictions: the runtime screen against a certification
|
|
1191
|
+
# ---------------------------------------------------------------------------
|
|
1192
|
+
_CONTRADICTIONS: List[Dict[str, Any]] = []
|
|
1193
|
+
|
|
1194
|
+
|
|
1195
|
+
def contradictions() -> List[Dict[str, Any]]:
|
|
1196
|
+
"""Every runtime exclusion this process saw that contradicts a certified setting."""
|
|
1197
|
+
return list(_CONTRADICTIONS)
|
|
1198
|
+
|
|
1199
|
+
|
|
1200
|
+
def _config_matches(cfg: Dict[str, Any], config_text: str) -> bool:
|
|
1201
|
+
"""`str(triton.Config)` against a stored config dict: every kwarg and num_warps/num_stages present."""
|
|
1202
|
+
text = str(config_text)
|
|
1203
|
+
for k, v in (cfg.get("kwargs") or {}).items():
|
|
1204
|
+
if f"{k}: {v}" not in text:
|
|
1205
|
+
return False
|
|
1206
|
+
return (f"num_warps: {cfg.get('num_warps')}" in text) and (f"num_stages: {cfg.get('num_stages')}" in text)
|
|
1207
|
+
|
|
1208
|
+
|
|
1209
|
+
def report_contradictions(tuner, dropped) -> List[Dict[str, Any]]:
|
|
1210
|
+
"""Called by the screen with the configs it just excluded: any of them that
|
|
1211
|
+
the directory certifies for this profile, kernel, dtype and shape is
|
|
1212
|
+
printed as a CONTRADICTION and recorded beside the screen's exclusions.
|
|
1213
|
+
The certified setting stays in force for this run; the finding is the
|
|
1214
|
+
directory's to resolve (a re-certification, or a defect in one of the two
|
|
1215
|
+
measurements) — silently trusting either would be the actual failure."""
|
|
1216
|
+
if not dropped:
|
|
1217
|
+
return []
|
|
1218
|
+
from nbxtensor.ops.autotune_cache import key_of, _qual_of
|
|
1219
|
+
qual = _qual_of(tuner)
|
|
1220
|
+
if qual is None:
|
|
1221
|
+
return []
|
|
1222
|
+
try:
|
|
1223
|
+
key = key_of(tuner, [], dict(getattr(tuner, "nargs", None) or {}))
|
|
1224
|
+
except Exception:
|
|
1225
|
+
return []
|
|
1226
|
+
# the proof's sweep arm is where the two can disagree — against the certification
|
|
1227
|
+
# made for THIS card's memory class (a contradiction with another class's proof is not one)
|
|
1228
|
+
mcls = executing_memory_gb(list((getattr(tuner, "nargs", None) or {}).values()) or None)
|
|
1229
|
+
entry = lookup(qual, tuner, key, ignore_switch=True, memory_gb=mcls)
|
|
1230
|
+
if entry is None:
|
|
1231
|
+
raw = lookup(qual, tuner, key, ignore_switch=True, any_class=True)
|
|
1232
|
+
if raw is not None and mcls is None:
|
|
1233
|
+
# a certification exists and this card's class is unknown: neither a
|
|
1234
|
+
# contradiction nor a clean pass — said, never dropped
|
|
1235
|
+
print(f"[AUTOTUNE_SCREEN] UNADJUDICATED — {kernel_short(qual)} {output_dtype(tuner, key)} "
|
|
1236
|
+
f"({describe_key(tuner, key)}): the directory certifies this shape for "
|
|
1237
|
+
f"{', '.join(f'{c} GB' for c in sorted(covered_memory_gbs(raw))) or 'no known memory class'} and the "
|
|
1238
|
+
f"executing card's memory class is unknown, so the runtime exclusion cannot be read against it.", flush=True)
|
|
1239
|
+
return []
|
|
1240
|
+
found = []
|
|
1241
|
+
for ex in dropped:
|
|
1242
|
+
if not _config_matches(entry["config"], getattr(ex, "config", ex)):
|
|
1243
|
+
continue
|
|
1244
|
+
proof = entry.get("proof") or {}
|
|
1245
|
+
rec = {"kernel": qual, "key": key_repr(key), "dtype": output_dtype(tuner, key), "config": entry["config"],
|
|
1246
|
+
"certified": {"date": proof.get("date"), "deviation": proof.get("deviation"),
|
|
1247
|
+
"tolerance": proof.get("tolerance"), "machine": proof.get("machine")},
|
|
1248
|
+
"runtime": {"deviation": getattr(ex, "deviation", None), "tolerance": getattr(ex, "tolerance", None)},
|
|
1249
|
+
"contradiction": True}
|
|
1250
|
+
found.append(rec)
|
|
1251
|
+
_CONTRADICTIONS.append(rec)
|
|
1252
|
+
print(f"[AUTOTUNE_SCREEN] CONTRADICTION — {kernel_short(qual)} {rec['dtype']} ({describe_key(tuner, key)}): the "
|
|
1253
|
+
f"setting certified on {proof.get('date')} (deviation {proof.get('deviation')} vs the fp64 oracle, tolerance "
|
|
1254
|
+
f"{proof.get('tolerance')}) is excluded by the runtime consensus (deviation {rec['runtime']['deviation']}). "
|
|
1255
|
+
f"A finding to report: the certification and this run disagree.", flush=True)
|
|
1256
|
+
if found:
|
|
1257
|
+
try:
|
|
1258
|
+
from nbxtensor.ops.autotune_cache import record_screen_exclusions
|
|
1259
|
+
record_screen_exclusions([{**r, "config": str(r["config"])} for r in found])
|
|
1260
|
+
except Exception:
|
|
1261
|
+
pass
|
|
1262
|
+
return found
|