mjolnir-cli 0.2.0__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.
- mjolnir_cli/__init__.py +8 -0
- mjolnir_cli/__main__.py +4 -0
- mjolnir_cli/accuracy.py +333 -0
- mjolnir_cli/autoresearch.py +539 -0
- mjolnir_cli/benchy.py +475 -0
- mjolnir_cli/bundled/configs/Qwen/Qwen3.8-27B/NVFP4/config.yaml +78 -0
- mjolnir_cli/bundled/configs/Qwen/Qwen3.8-27B/NVFP4_DFLASH2/config.yaml +103 -0
- mjolnir_cli/bundled/configs/Qwen/Qwen3.8-27B/NVFP4_DSPARK/config.yaml +102 -0
- mjolnir_cli/bundled/configs/Qwen/Qwen3.8-27B/NVFP4_FA2/config.yaml +84 -0
- mjolnir_cli/bundled/configs/Qwen/Qwen3.8-27B/NVFP4_FA4hd256/config.yaml +104 -0
- mjolnir_cli/bundled/configs/Qwen/Qwen3.8-27B/NVFP4_MJOLNIR/config.yaml +86 -0
- mjolnir_cli/bundled/configs/Qwen/Qwen3.8-27B/NVFP4_MJOLNIR_E2M3/config.yaml +93 -0
- mjolnir_cli/bundled/configs/Qwen/Qwen3.8-27B/NVFP4_MJOLNIR_E2M3_DFLASH2/config.yaml +123 -0
- mjolnir_cli/bundled/configs/Qwen/Qwen3.8-27B/NVFP4_MJOLNIR_E2M3_DSPARK/config.yaml +124 -0
- mjolnir_cli/bundled/configs/Qwen/Qwen3.8-27B/NVFP4_MJOLNIR_E2M3_LMH8/.env +2 -0
- mjolnir_cli/bundled/configs/Qwen/Qwen3.8-27B/NVFP4_MJOLNIR_E2M3_LMH8/config.yaml +98 -0
- mjolnir_cli/bundled/configs/Qwen/Qwen3.8-27B/NVFP4_MJOLNIR_E2M3_LMH8_VT32K/.env +4 -0
- mjolnir_cli/bundled/configs/Qwen/Qwen3.8-27B/NVFP4_MJOLNIR_E2M3_LMH8_VT32K/config.yaml +98 -0
- mjolnir_cli/bundled/configs/Qwen/Qwen3.8-27B/NVFP4_MJOLNIR_E2M3_LMH8_VT64K/.env +4 -0
- mjolnir_cli/bundled/configs/Qwen/Qwen3.8-27B/NVFP4_MJOLNIR_E2M3_LMH8_VT64K/config.yaml +102 -0
- mjolnir_cli/bundled/configs/Qwen/Qwen3.8-27B/NVFP4_MJOLNIR_E2M3_VT32K/.env +3 -0
- mjolnir_cli/bundled/configs/Qwen/Qwen3.8-27B/NVFP4_MJOLNIR_E2M3_VT32K/config.yaml +98 -0
- mjolnir_cli/bundled/configs/Qwen/Qwen3.8-27B/draft_vocab.json +1 -0
- mjolnir_cli/bundled/configs/Qwen/Qwen3.8-27B/kv_scales.json +35197 -0
- mjolnir_cli/bundled/configs/Qwen/Qwen3.8_chat_template.jinja +456 -0
- mjolnir_cli/bundled/docker/litellm/docker-compose.yaml +113 -0
- mjolnir_cli/bundled/docker/litellm/litellm.env.example +51 -0
- mjolnir_cli/bundled/docker/litellm/litellm_config.template.example.yaml +192 -0
- mjolnir_cli/bundled/docker/perses/README.md +89 -0
- mjolnir_cli/bundled/docker/perses/dashboards/global-datasource.yaml +17 -0
- mjolnir_cli/bundled/docker/perses/dashboards/performance_statistics.yaml +771 -0
- mjolnir_cli/bundled/docker/perses/dashboards/project.yaml +13 -0
- mjolnir_cli/bundled/docker/perses/dashboards/query_statistics.yaml +399 -0
- mjolnir_cli/bundled/docker/perses/docker-compose.yaml +51 -0
- mjolnir_cli/bundled/docker/perses/perses.yml +16 -0
- mjolnir_cli/bundled/docker/perses/prometheus.template.yaml +20 -0
- mjolnir_cli/bundled/scripts/hw/setup-thor.sh +1287 -0
- mjolnir_cli/cachectl.py +146 -0
- mjolnir_cli/charts.py +1687 -0
- mjolnir_cli/cli.py +1480 -0
- mjolnir_cli/clidoc.py +170 -0
- mjolnir_cli/config.py +346 -0
- mjolnir_cli/dockerctl.py +533 -0
- mjolnir_cli/drivers/fa2_driver.py +89 -0
- mjolnir_cli/drivers/fi_driver.py +134 -0
- mjolnir_cli/e2e.py +302 -0
- mjolnir_cli/fonts/InterDisplay-Bold.ttf +0 -0
- mjolnir_cli/fonts/InterDisplay-Medium.ttf +0 -0
- mjolnir_cli/fonts/InterDisplay-Regular.ttf +0 -0
- mjolnir_cli/fonts/InterDisplay-SemiBold.ttf +0 -0
- mjolnir_cli/fonts/OFL.txt +92 -0
- mjolnir_cli/gate.py +233 -0
- mjolnir_cli/history.py +159 -0
- mjolnir_cli/interactive.py +383 -0
- mjolnir_cli/legs.py +109 -0
- mjolnir_cli/litellm.py +254 -0
- mjolnir_cli/perses.py +214 -0
- mjolnir_cli/picker.py +70 -0
- mjolnir_cli/readme.py +1020 -0
- mjolnir_cli/stack.py +340 -0
- mjolnir_cli/tasks.py +122 -0
- mjolnir_cli/theme.py +281 -0
- mjolnir_cli/thor.py +108 -0
- mjolnir_cli/verify/__init__.py +2 -0
- mjolnir_cli/verify/cli.py +816 -0
- mjolnir_cli/verify/docker.py +334 -0
- mjolnir_cli/verify/kvcache.py +180 -0
- mjolnir_cli/verify/legs.py +134 -0
- mjolnir_cli/verify/ncu.py +141 -0
- mjolnir_cli/verify/profile.py +312 -0
- mjolnir_cli/verify/report.py +177 -0
- mjolnir_cli/verify/results.py +157 -0
- mjolnir_cli/verify/spec.py +279 -0
- mjolnir_cli/verify/stages.py +817 -0
- mjolnir_cli-0.2.0.dist-info/METADATA +87 -0
- mjolnir_cli-0.2.0.dist-info/RECORD +80 -0
- mjolnir_cli-0.2.0.dist-info/WHEEL +4 -0
- mjolnir_cli-0.2.0.dist-info/entry_points.txt +2 -0
- mjolnir_cli-0.2.0.dist-info/licenses/LICENSE +202 -0
- mjolnir_cli-0.2.0.dist-info/licenses/NOTICE +64 -0
|
@@ -0,0 +1,816 @@
|
|
|
1
|
+
"""``mjolnir verify`` — correctness, benchmarks and profiles of the Mjolnir
|
|
2
|
+
kernel, every run saved as structured raw data (``benchmarks/raw/verify``).
|
|
3
|
+
|
|
4
|
+
mjolnir verify correctness # kernel vs emulator matrix
|
|
5
|
+
mjolnir verify sweep # Mjolnir vs FlashInfer vs FA2 at every L (ncu)
|
|
6
|
+
mjolnir verify hndsweep|kvfmt|quant|ring|prefetch|pdl|ab|fp6prof|timing|vllm
|
|
7
|
+
mjolnir verify bench # every benchmark stage, default parameters
|
|
8
|
+
mjolnir verify all # correctness + every benchmark stage
|
|
9
|
+
mjolnir verify show [stage] # re-print the latest saved run(s)
|
|
10
|
+
mjolnir verify list # stages + saved runs
|
|
11
|
+
|
|
12
|
+
Every stage defaults to the full L axis: 128 4096 8192 16384 32768 65536
|
|
13
|
+
131072 262144 (``--ls`` overrides). A run with non-default parameters is
|
|
14
|
+
saved too, but marked ``default: false`` so it never replaces a headline
|
|
15
|
+
chart (``mjolnir plot`` reads the latest default run of each stage).
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import shutil
|
|
21
|
+
import sys
|
|
22
|
+
from typing import Optional
|
|
23
|
+
|
|
24
|
+
import typer
|
|
25
|
+
|
|
26
|
+
from mjolnir_cli.config import DEFAULT_IMAGE, load_layout, resolve
|
|
27
|
+
from mjolnir_cli.legs import KERNEL, SMOKE_MODES
|
|
28
|
+
from mjolnir_cli.verify import results, stages
|
|
29
|
+
from mjolnir_cli.verify.kvcache import DEFAULT_LEGS as KV_LEGS
|
|
30
|
+
from mjolnir_cli.verify.profile import DEFAULT_LEGS as PROFILE_LEGS
|
|
31
|
+
from mjolnir_cli.verify.spec import DEFAULT_DRAFTS, DEFAULT_LEGS as SPEC_LEGS
|
|
32
|
+
from mjolnir_cli.verify.docker import Runner, ensure_image
|
|
33
|
+
from mjolnir_cli.verify.report import console, show
|
|
34
|
+
|
|
35
|
+
app = typer.Typer(
|
|
36
|
+
help="Kernel correctness + benchmarks (Mjolnir vs FlashInfer vs FA2), saved as raw data for the charts.",
|
|
37
|
+
no_args_is_help=True,
|
|
38
|
+
context_settings={"help_option_names": ["-h", "--help"]},
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
_LS_HELP = "L values (default: 128 4096 8192 16384 32768 65536 131072 262144)"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _parse_ls(ls: Optional[str]) -> tuple[int, ...]:
|
|
45
|
+
if not ls:
|
|
46
|
+
return stages.ALL_LS
|
|
47
|
+
return tuple(int(x) for x in ls.replace(",", " ").split())
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _kernel_info(layout) -> dict:
|
|
51
|
+
info: dict = {}
|
|
52
|
+
try:
|
|
53
|
+
import mjolnir # the kernel package (workspace / installed)
|
|
54
|
+
|
|
55
|
+
info["version"] = getattr(mjolnir, "__version__", "?")
|
|
56
|
+
except ImportError:
|
|
57
|
+
pass
|
|
58
|
+
if layout.repo_root is not None:
|
|
59
|
+
src = layout.repo_root / "src" / "mjolnir" / "mjolnir" / "__init__.py"
|
|
60
|
+
if src.exists():
|
|
61
|
+
import re
|
|
62
|
+
|
|
63
|
+
m = re.search(r'__version__\s*=\s*"([^"]+)"', src.read_text())
|
|
64
|
+
if m:
|
|
65
|
+
info["version"] = m.group(1)
|
|
66
|
+
info.update(results.git_info(layout.repo_root))
|
|
67
|
+
return info
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
# docker missing / image unavailable: nothing can run, stop the whole sequence
|
|
71
|
+
PREFLIGHT_RC = 2
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def execute(
|
|
75
|
+
stage: str,
|
|
76
|
+
params: dict,
|
|
77
|
+
defaults: dict,
|
|
78
|
+
*,
|
|
79
|
+
image: Optional[str],
|
|
80
|
+
ls: Optional[str],
|
|
81
|
+
iters: int,
|
|
82
|
+
gate: bool,
|
|
83
|
+
dry_run: bool,
|
|
84
|
+
timeout: float,
|
|
85
|
+
save: bool,
|
|
86
|
+
plot: bool,
|
|
87
|
+
page: int = 128,
|
|
88
|
+
) -> tuple[int, dict | None]:
|
|
89
|
+
layout = load_layout()
|
|
90
|
+
s = resolve(None, None, image, None)
|
|
91
|
+
if shutil.which("docker") is None and not dry_run:
|
|
92
|
+
console.print("[red]docker not found on PATH[/red]")
|
|
93
|
+
return PREFLIGHT_RC, None
|
|
94
|
+
if not dry_run:
|
|
95
|
+
err = ensure_image(s.image)
|
|
96
|
+
if err:
|
|
97
|
+
console.print(f"[red]✗ {err}[/red]")
|
|
98
|
+
return PREFLIGHT_RC, None
|
|
99
|
+
run_id = results.new_run_id()
|
|
100
|
+
logdir = layout.benchmarks_dir / "logs" / f"verify-{run_id}"
|
|
101
|
+
kernel_src = None
|
|
102
|
+
if layout.repo_root is not None:
|
|
103
|
+
cand = layout.repo_root / "src" / "mjolnir"
|
|
104
|
+
if (cand / "mjolnir" / "t5_decode.py").exists():
|
|
105
|
+
kernel_src = cand
|
|
106
|
+
runner = Runner(image=s.image, logdir=logdir, kernel_src=kernel_src, timeout=timeout, dry_run=dry_run, page=page)
|
|
107
|
+
ctx = stages.Ctx(
|
|
108
|
+
runner=runner,
|
|
109
|
+
run_id=run_id,
|
|
110
|
+
logdir=logdir,
|
|
111
|
+
metrics_url=s.metrics_url,
|
|
112
|
+
gate=gate,
|
|
113
|
+
ls=_parse_ls(ls),
|
|
114
|
+
iters=iters,
|
|
115
|
+
out=lambda m: console.print(m, highlight=False, soft_wrap=True), # rows never hard-wrap
|
|
116
|
+
)
|
|
117
|
+
st = stages.STAGES[stage]
|
|
118
|
+
console.print(f"\n[bold cyan]━━ verify {stage}[/bold cyan] — {st.summary}")
|
|
119
|
+
console.print(
|
|
120
|
+
f"[dim] image {s.image}{'' if s.image == DEFAULT_IMAGE else f' (not the default {DEFAULT_IMAGE})'} · kernel {'checkout ' + str(kernel_src) if kernel_src else 'baked into the image'}"
|
|
121
|
+
f" · L {' '.join(str(x) for x in ctx.ls)} · logs {logdir}[/dim]"
|
|
122
|
+
)
|
|
123
|
+
console.print(f"[dim] FlashInfer / FA2 references run on the official image {runner.ref_image}[/dim]")
|
|
124
|
+
from mjolnir_cli import cachectl
|
|
125
|
+
|
|
126
|
+
if not dry_run:
|
|
127
|
+
cachectl.prepare_bench() # clean page cache before measuring (no-op without the grant)
|
|
128
|
+
kwargs = dict(params)
|
|
129
|
+
if stage == "vllm":
|
|
130
|
+
kwargs["repo"] = layout.require_repo("verify vllm")
|
|
131
|
+
try:
|
|
132
|
+
body = st.fn(ctx, **kwargs)
|
|
133
|
+
finally:
|
|
134
|
+
if not dry_run: # Thor keeps the page cache the stage's containers filled
|
|
135
|
+
cachectl.prepare_bench()
|
|
136
|
+
is_default = params == defaults and ctx.ls == stages.ALL_LS and iters == stages.DEFAULT_ITERS and page == 128
|
|
137
|
+
doc = results.header(
|
|
138
|
+
stage,
|
|
139
|
+
run_id,
|
|
140
|
+
image=s.image,
|
|
141
|
+
default=is_default,
|
|
142
|
+
params={
|
|
143
|
+
**body.pop("params", {}),
|
|
144
|
+
"ls": list(ctx.ls),
|
|
145
|
+
"iters": iters,
|
|
146
|
+
"page": page,
|
|
147
|
+
"hq": runner.hq,
|
|
148
|
+
"hk": runner.hk,
|
|
149
|
+
"d": runner.d,
|
|
150
|
+
},
|
|
151
|
+
kernel=_kernel_info(layout),
|
|
152
|
+
gate=ctx.gate_log,
|
|
153
|
+
)
|
|
154
|
+
doc["ref_image"] = runner.ref_image # where the FlashInfer / FA2 legs ran (stock vLLM)
|
|
155
|
+
doc.update(body)
|
|
156
|
+
doc["summary"] = stages.summarize(stage, doc)
|
|
157
|
+
if dry_run:
|
|
158
|
+
console.print("[dim] (dry run — nothing saved)[/dim]")
|
|
159
|
+
return 0, None
|
|
160
|
+
show(doc)
|
|
161
|
+
if save:
|
|
162
|
+
p = results.save(layout.verify_dir, doc)
|
|
163
|
+
console.print(f" 📝 [bold]{p}[/bold]{'' if is_default else ' (custom parameters: not a headline run)'}")
|
|
164
|
+
if plot and layout.is_repo:
|
|
165
|
+
_replot(layout)
|
|
166
|
+
failed = [c for c in doc.get("cases", []) if not c.get("passed")]
|
|
167
|
+
empty = st.kind in ("ncu",) and not any(s_.get("points") for s_ in doc.get("series", []))
|
|
168
|
+
return (1 if failed or empty else 0), doc
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _execute(*a, **kw) -> int:
|
|
172
|
+
return execute(*a, **kw)[0]
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _replot(layout) -> None:
|
|
176
|
+
try:
|
|
177
|
+
from mjolnir_cli import charts, readme
|
|
178
|
+
|
|
179
|
+
charts.render_all(layout)
|
|
180
|
+
readme.render_readme(layout)
|
|
181
|
+
except Exception as e: # noqa: BLE001 — charts are a convenience here
|
|
182
|
+
console.print(f"[yellow] (charts/README not re-rendered: {e})[/yellow]")
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
# ── shared options (each command declares them; typer needs explicit params) ──
|
|
186
|
+
IMAGE = typer.Option(None, "--image", help="vLLM image (default: the active one, see `mjolnir image`)")
|
|
187
|
+
LS = typer.Option(None, "--ls", help=_LS_HELP)
|
|
188
|
+
ITERS = typer.Option(stages.DEFAULT_ITERS, "--iters", help="profiled iterations per point (ncu stages)")
|
|
189
|
+
GATE = typer.Option(True, "--gate/--no-gate", help="wait for a clean window when the live vLLM server is up")
|
|
190
|
+
DRY = typer.Option(False, "--dry-run", help="print the docker commands, run nothing")
|
|
191
|
+
TIMEOUT = typer.Option(180.0, "--timeout", help="per-container timeout (s); a hung kernel's container is killed")
|
|
192
|
+
SAVE = typer.Option(True, "--save/--no-save", help="write the raw result JSON")
|
|
193
|
+
PLOT = typer.Option(False, "--plot/--no-plot", help="re-render charts + README after saving (checkout only)")
|
|
194
|
+
PAGE = typer.Option(128, "--page-size", help="KV page size for every leg")
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def _run(stage: str, params: dict, defaults: dict, image, ls, iters, gate, dry_run, timeout, save, plot, page=128):
|
|
198
|
+
raise typer.Exit(
|
|
199
|
+
_execute(
|
|
200
|
+
stage,
|
|
201
|
+
params,
|
|
202
|
+
defaults,
|
|
203
|
+
image=image,
|
|
204
|
+
ls=ls,
|
|
205
|
+
iters=iters,
|
|
206
|
+
gate=gate,
|
|
207
|
+
dry_run=dry_run,
|
|
208
|
+
timeout=timeout,
|
|
209
|
+
save=save,
|
|
210
|
+
plot=plot,
|
|
211
|
+
page=page,
|
|
212
|
+
)
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
@app.command()
|
|
217
|
+
def correctness(
|
|
218
|
+
dtypes: str = typer.Option("fp8 bf16", "--dtypes", help="Q dtypes to test"),
|
|
219
|
+
match: str = typer.Option("", "--match", help="only cases whose label contains this"),
|
|
220
|
+
quick: bool = typer.Option(False, "--quick", help="the quick subset (one case per group; what autoresearch runs)"),
|
|
221
|
+
fail_fast: bool = typer.Option(False, "--fail-fast"),
|
|
222
|
+
image: Optional[str] = IMAGE,
|
|
223
|
+
dry_run: bool = DRY,
|
|
224
|
+
timeout: float = TIMEOUT,
|
|
225
|
+
save: bool = SAVE,
|
|
226
|
+
):
|
|
227
|
+
"""Kernel vs the bit-faithful emulator: every layout, KV format, page size, q_len and Q dtype."""
|
|
228
|
+
p = {"dtypes": dtypes, "match": match, "fail_fast": fail_fast, "timeout": timeout, "quick": quick}
|
|
229
|
+
d = {"dtypes": "fp8 bf16", "match": "", "fail_fast": False, "timeout": 180.0, "quick": False}
|
|
230
|
+
_run("correctness", p, d, image, None, stages.DEFAULT_ITERS, False, dry_run, timeout, save, False)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
@app.command()
|
|
234
|
+
def sweep(
|
|
235
|
+
legs: str = typer.Option(KERNEL["verify.sweep"], "--legs", help="legs: fi, fi-hnd, fa2, mjolnir-fp8, mjolnir"),
|
|
236
|
+
image: Optional[str] = IMAGE,
|
|
237
|
+
ls: Optional[str] = LS,
|
|
238
|
+
iters: int = ITERS,
|
|
239
|
+
gate: bool = GATE,
|
|
240
|
+
dry_run: bool = DRY,
|
|
241
|
+
timeout: float = TIMEOUT,
|
|
242
|
+
save: bool = SAVE,
|
|
243
|
+
plot: bool = PLOT,
|
|
244
|
+
page: int = PAGE,
|
|
245
|
+
batch: int = typer.Option(1, "--batch", help="sequences per step, each of L tokens (serving: 1-4)"),
|
|
246
|
+
q_len: int = typer.Option(1, "--q-len", help="query tokens per sequence (MTP=3 verification: 4)"),
|
|
247
|
+
):
|
|
248
|
+
"""The headline: Mjolnir vs FlashInfer vs FlashAttention 2 at every L (ncu, cold L2, base clocks).
|
|
249
|
+
|
|
250
|
+
`--batch 4 --q-len 4 --legs fi,mjolnir-fp8` measures the shape vLLM serves
|
|
251
|
+
with MTP=3 at 4 concurrent requests (saved as a non-default run)."""
|
|
252
|
+
_run(
|
|
253
|
+
"sweep",
|
|
254
|
+
{"legs": legs, "batch": batch, "q_len": q_len},
|
|
255
|
+
{"legs": KERNEL["verify.sweep"], "batch": 1, "q_len": 1},
|
|
256
|
+
image,
|
|
257
|
+
ls,
|
|
258
|
+
iters,
|
|
259
|
+
gate,
|
|
260
|
+
dry_run,
|
|
261
|
+
timeout,
|
|
262
|
+
save,
|
|
263
|
+
plot,
|
|
264
|
+
page,
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
@app.command()
|
|
269
|
+
def hndsweep(
|
|
270
|
+
legs: str = typer.Option(KERNEL["verify.hndsweep"], "--legs"),
|
|
271
|
+
image: Optional[str] = IMAGE,
|
|
272
|
+
ls: Optional[str] = LS,
|
|
273
|
+
iters: int = ITERS,
|
|
274
|
+
gate: bool = GATE,
|
|
275
|
+
dry_run: bool = DRY,
|
|
276
|
+
timeout: float = TIMEOUT,
|
|
277
|
+
save: bool = SAVE,
|
|
278
|
+
plot: bool = PLOT,
|
|
279
|
+
page: int = PAGE,
|
|
280
|
+
):
|
|
281
|
+
"""FlashInfer on NHD and HND pages vs Mjolnir (fp8 KV) on HND: the fair layout comparison."""
|
|
282
|
+
_run(
|
|
283
|
+
"hndsweep",
|
|
284
|
+
{"legs": legs},
|
|
285
|
+
{"legs": KERNEL["verify.hndsweep"]},
|
|
286
|
+
image,
|
|
287
|
+
ls,
|
|
288
|
+
iters,
|
|
289
|
+
gate,
|
|
290
|
+
dry_run,
|
|
291
|
+
timeout,
|
|
292
|
+
save,
|
|
293
|
+
plot,
|
|
294
|
+
page,
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
@app.command()
|
|
299
|
+
def kvfmt(
|
|
300
|
+
variants: str = typer.Option(stages.KVFMT_DEFAULT, "--variants", help="name:flags|name:flags"),
|
|
301
|
+
refs: str = typer.Option(KERNEL["verify.kvfmt.refs"], "--refs", help="baseline legs"),
|
|
302
|
+
wall_iters: int = typer.Option(200, "--wall-iters", help="harness wall-clock iterations (0 = skip)"),
|
|
303
|
+
image: Optional[str] = IMAGE,
|
|
304
|
+
ls: Optional[str] = LS,
|
|
305
|
+
iters: int = ITERS,
|
|
306
|
+
gate: bool = GATE,
|
|
307
|
+
dry_run: bool = DRY,
|
|
308
|
+
timeout: float = TIMEOUT,
|
|
309
|
+
save: bool = SAVE,
|
|
310
|
+
plot: bool = PLOT,
|
|
311
|
+
):
|
|
312
|
+
"""KV layouts x formats: fp8 / fp6 (e2m3, e3m2) / fp4 (e2m1) / nvfp4 on NHD / interleaved / HND."""
|
|
313
|
+
_run(
|
|
314
|
+
"kvfmt",
|
|
315
|
+
{"variants": variants, "refs": refs, "wall_iters": wall_iters},
|
|
316
|
+
{"variants": stages.KVFMT_DEFAULT, "refs": KERNEL["verify.kvfmt.refs"], "wall_iters": 200},
|
|
317
|
+
image,
|
|
318
|
+
ls,
|
|
319
|
+
iters,
|
|
320
|
+
gate,
|
|
321
|
+
dry_run,
|
|
322
|
+
timeout,
|
|
323
|
+
save,
|
|
324
|
+
plot,
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
@app.command()
|
|
329
|
+
def quant(
|
|
330
|
+
formats: str = typer.Option(stages.QUANT_FORMATS, "--formats"),
|
|
331
|
+
batches: int = typer.Option(2, "--batches"),
|
|
332
|
+
image: Optional[str] = IMAGE,
|
|
333
|
+
ls: Optional[str] = LS,
|
|
334
|
+
dry_run: bool = DRY,
|
|
335
|
+
timeout: float = TIMEOUT,
|
|
336
|
+
save: bool = SAVE,
|
|
337
|
+
plot: bool = PLOT,
|
|
338
|
+
):
|
|
339
|
+
"""Accuracy per KV format: relative L2 error vs attention over unquantized KV."""
|
|
340
|
+
_run(
|
|
341
|
+
"quant",
|
|
342
|
+
{"formats": formats, "batches": batches},
|
|
343
|
+
{"formats": stages.QUANT_FORMATS, "batches": 2},
|
|
344
|
+
image,
|
|
345
|
+
ls,
|
|
346
|
+
stages.DEFAULT_ITERS,
|
|
347
|
+
False,
|
|
348
|
+
dry_run,
|
|
349
|
+
timeout,
|
|
350
|
+
save,
|
|
351
|
+
plot,
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
@app.command()
|
|
356
|
+
def ring(
|
|
357
|
+
depths: str = typer.Option(stages.RING_DEFAULT, "--stages", help="--kv_stages values"),
|
|
358
|
+
base: str = typer.Option("", "--base", help="harness flags every variant gets"),
|
|
359
|
+
refs: str = typer.Option("fi", "--refs"),
|
|
360
|
+
image: Optional[str] = IMAGE,
|
|
361
|
+
ls: Optional[str] = LS,
|
|
362
|
+
iters: int = ITERS,
|
|
363
|
+
gate: bool = GATE,
|
|
364
|
+
dry_run: bool = DRY,
|
|
365
|
+
timeout: float = TIMEOUT,
|
|
366
|
+
save: bool = SAVE,
|
|
367
|
+
plot: bool = PLOT,
|
|
368
|
+
):
|
|
369
|
+
"""KV ring depth (TMA → MMA stages) at every L."""
|
|
370
|
+
_run(
|
|
371
|
+
"ring",
|
|
372
|
+
{"stages": depths, "base": base, "refs": refs},
|
|
373
|
+
{"stages": stages.RING_DEFAULT, "base": "", "refs": "fi"},
|
|
374
|
+
image,
|
|
375
|
+
ls,
|
|
376
|
+
iters,
|
|
377
|
+
gate,
|
|
378
|
+
dry_run,
|
|
379
|
+
timeout,
|
|
380
|
+
save,
|
|
381
|
+
plot,
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
@app.command()
|
|
386
|
+
def prefetch(
|
|
387
|
+
distances: str = typer.Option("0 1 2 4", "--distances", help="--l2_prefetch tiles ahead"),
|
|
388
|
+
base: str = typer.Option("--kv_format fp8", "--base"),
|
|
389
|
+
refs: str = typer.Option("fi", "--refs"),
|
|
390
|
+
image: Optional[str] = IMAGE,
|
|
391
|
+
ls: Optional[str] = LS,
|
|
392
|
+
iters: int = ITERS,
|
|
393
|
+
gate: bool = GATE,
|
|
394
|
+
dry_run: bool = DRY,
|
|
395
|
+
timeout: float = TIMEOUT,
|
|
396
|
+
save: bool = SAVE,
|
|
397
|
+
plot: bool = PLOT,
|
|
398
|
+
):
|
|
399
|
+
"""TMA L2-prefetch distance sweep."""
|
|
400
|
+
_run(
|
|
401
|
+
"prefetch",
|
|
402
|
+
{"distances": distances, "base": base, "refs": refs},
|
|
403
|
+
{"distances": "0 1 2 4", "base": "--kv_format fp8", "refs": "fi"},
|
|
404
|
+
image,
|
|
405
|
+
ls,
|
|
406
|
+
iters,
|
|
407
|
+
gate,
|
|
408
|
+
dry_run,
|
|
409
|
+
timeout,
|
|
410
|
+
save,
|
|
411
|
+
plot,
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
@app.command()
|
|
416
|
+
def pdl(
|
|
417
|
+
batches: str = typer.Option("1 8", "--batches"),
|
|
418
|
+
dtypes: str = typer.Option("fp8", "--dtypes"),
|
|
419
|
+
wall_iters: int = typer.Option(200, "--wall-iters"),
|
|
420
|
+
image: Optional[str] = IMAGE,
|
|
421
|
+
ls: Optional[str] = LS,
|
|
422
|
+
gate: bool = GATE,
|
|
423
|
+
dry_run: bool = DRY,
|
|
424
|
+
timeout: float = TIMEOUT,
|
|
425
|
+
save: bool = SAVE,
|
|
426
|
+
plot: bool = PLOT,
|
|
427
|
+
):
|
|
428
|
+
"""Programmatic dependent launch on/off (wall clock)."""
|
|
429
|
+
_run(
|
|
430
|
+
"pdl",
|
|
431
|
+
{"batches": batches, "dtypes": dtypes, "iters": wall_iters},
|
|
432
|
+
{"batches": "1 8", "dtypes": "fp8", "iters": 200},
|
|
433
|
+
image,
|
|
434
|
+
ls,
|
|
435
|
+
stages.DEFAULT_ITERS,
|
|
436
|
+
gate,
|
|
437
|
+
dry_run,
|
|
438
|
+
timeout,
|
|
439
|
+
save,
|
|
440
|
+
plot,
|
|
441
|
+
)
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
@app.command()
|
|
445
|
+
def ab(
|
|
446
|
+
variants: str = typer.Option(stages.AB_DEFAULT, "--variants", help="name:flags|name:flags"),
|
|
447
|
+
refs: str = typer.Option("fi", "--refs"),
|
|
448
|
+
wall_iters: int = typer.Option(200, "--wall-iters"),
|
|
449
|
+
image: Optional[str] = IMAGE,
|
|
450
|
+
ls: Optional[str] = LS,
|
|
451
|
+
iters: int = ITERS,
|
|
452
|
+
gate: bool = GATE,
|
|
453
|
+
dry_run: bool = DRY,
|
|
454
|
+
timeout: float = TIMEOUT,
|
|
455
|
+
save: bool = SAVE,
|
|
456
|
+
plot: bool = PLOT,
|
|
457
|
+
):
|
|
458
|
+
"""A/B any harness flag sets (ncu + wall clock). Default: evict-first x PDL."""
|
|
459
|
+
_run(
|
|
460
|
+
"ab",
|
|
461
|
+
{"variants": variants, "refs": refs, "wall_iters": wall_iters},
|
|
462
|
+
{"variants": stages.AB_DEFAULT, "refs": "fi", "wall_iters": 200},
|
|
463
|
+
image,
|
|
464
|
+
ls,
|
|
465
|
+
iters,
|
|
466
|
+
gate,
|
|
467
|
+
dry_run,
|
|
468
|
+
timeout,
|
|
469
|
+
save,
|
|
470
|
+
plot,
|
|
471
|
+
)
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
@app.command()
|
|
475
|
+
def fp6prof(
|
|
476
|
+
variants: str = typer.Option(stages.PROF_DEFAULT, "--variants"),
|
|
477
|
+
count: int = typer.Option(3, "--count", help="profiled launches per point"),
|
|
478
|
+
section_set: str = typer.Option("full", "--set"),
|
|
479
|
+
image: Optional[str] = IMAGE,
|
|
480
|
+
ls: Optional[str] = LS,
|
|
481
|
+
gate: bool = GATE,
|
|
482
|
+
dry_run: bool = DRY,
|
|
483
|
+
timeout: float = TIMEOUT,
|
|
484
|
+
save: bool = SAVE,
|
|
485
|
+
plot: bool = PLOT,
|
|
486
|
+
):
|
|
487
|
+
"""Full ncu profiles per KV format (bytes vs requests vs time, warp stalls)."""
|
|
488
|
+
_run(
|
|
489
|
+
"fp6prof",
|
|
490
|
+
{"variants": variants, "count": count, "section_set": section_set},
|
|
491
|
+
{"variants": stages.PROF_DEFAULT, "count": 3, "section_set": "full"},
|
|
492
|
+
image,
|
|
493
|
+
ls,
|
|
494
|
+
stages.DEFAULT_ITERS,
|
|
495
|
+
gate,
|
|
496
|
+
dry_run,
|
|
497
|
+
timeout,
|
|
498
|
+
save,
|
|
499
|
+
plot,
|
|
500
|
+
)
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
@app.command()
|
|
504
|
+
def timing(
|
|
505
|
+
dtypes: str = typer.Option("fp8 bf16", "--dtypes"),
|
|
506
|
+
ctas: str = typer.Option("1 2", "--ctas"),
|
|
507
|
+
wall_iters: int = typer.Option(50, "--wall-iters"),
|
|
508
|
+
image: Optional[str] = IMAGE,
|
|
509
|
+
ls: Optional[str] = LS,
|
|
510
|
+
gate: bool = GATE,
|
|
511
|
+
dry_run: bool = DRY,
|
|
512
|
+
timeout: float = TIMEOUT,
|
|
513
|
+
save: bool = SAVE,
|
|
514
|
+
):
|
|
515
|
+
"""Harness-timed medians, 1 vs 2 CTAs/SM, fp8 and bf16 Q (no ncu)."""
|
|
516
|
+
_run(
|
|
517
|
+
"timing",
|
|
518
|
+
{"dtypes": dtypes, "ctas": ctas, "iters": wall_iters},
|
|
519
|
+
{"dtypes": "fp8 bf16", "ctas": "1 2", "iters": 50},
|
|
520
|
+
image,
|
|
521
|
+
ls,
|
|
522
|
+
stages.DEFAULT_ITERS,
|
|
523
|
+
gate,
|
|
524
|
+
dry_run,
|
|
525
|
+
timeout,
|
|
526
|
+
save,
|
|
527
|
+
False,
|
|
528
|
+
)
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
@app.command("profile")
|
|
532
|
+
def profile(
|
|
533
|
+
legs: str = typer.Option(
|
|
534
|
+
PROFILE_LEGS,
|
|
535
|
+
"--legs",
|
|
536
|
+
help="side:config, comma-separated (mjolnir:NVFP4 = FlashInfer on the Mjolnir image)",
|
|
537
|
+
),
|
|
538
|
+
drafts: int = typer.Option(3, "--drafts", help="num_speculative_tokens (0 = the config's own)"),
|
|
539
|
+
context_words: int = typer.Option(6000, "--context-words", help="prompt size (~1.3 tokens/word; 0 = short)"),
|
|
540
|
+
concurrency: int = typer.Option(1, "--concurrency"),
|
|
541
|
+
requests: int = typer.Option(2, "--requests", help="request rounds inside the profiled window"),
|
|
542
|
+
max_tokens: int = typer.Option(128, "--max-tokens"),
|
|
543
|
+
model: Optional[str] = typer.Option(None, "--model"),
|
|
544
|
+
restart: bool = typer.Option(False, "--restart", help="stop a running vLLM server first"),
|
|
545
|
+
image: Optional[str] = IMAGE,
|
|
546
|
+
dry_run: bool = DRY,
|
|
547
|
+
timeout: float = typer.Option(1800.0, "--timeout"),
|
|
548
|
+
save: bool = SAVE,
|
|
549
|
+
):
|
|
550
|
+
"""Profile a decode workload per config (torch profiler): graph replays, eager kernels, GPU idle, top kernels."""
|
|
551
|
+
p = {
|
|
552
|
+
"legs": legs,
|
|
553
|
+
"drafts": drafts,
|
|
554
|
+
"context_words": context_words,
|
|
555
|
+
"concurrency": concurrency,
|
|
556
|
+
"requests": requests,
|
|
557
|
+
"max_tokens": max_tokens,
|
|
558
|
+
"model": model,
|
|
559
|
+
"restart": restart,
|
|
560
|
+
"timeout": timeout,
|
|
561
|
+
}
|
|
562
|
+
_run(
|
|
563
|
+
"profile",
|
|
564
|
+
p,
|
|
565
|
+
{**p, "legs": PROFILE_LEGS},
|
|
566
|
+
image,
|
|
567
|
+
None,
|
|
568
|
+
stages.DEFAULT_ITERS,
|
|
569
|
+
False,
|
|
570
|
+
dry_run,
|
|
571
|
+
timeout,
|
|
572
|
+
save,
|
|
573
|
+
False,
|
|
574
|
+
)
|
|
575
|
+
|
|
576
|
+
|
|
577
|
+
@app.command("spec")
|
|
578
|
+
def spec(
|
|
579
|
+
legs: str = typer.Option(
|
|
580
|
+
SPEC_LEGS,
|
|
581
|
+
"--legs",
|
|
582
|
+
help="side:config, comma-separated; official = the stock vLLM image, mjolnir = --image",
|
|
583
|
+
),
|
|
584
|
+
drafts: str = typer.Option(DEFAULT_DRAFTS, "--drafts", help="num_speculative_tokens values to serve"),
|
|
585
|
+
concurrency: str = typer.Option("1 4", "--concurrency", help="requests in flight per block"),
|
|
586
|
+
max_tokens: int = typer.Option(256, "--max-tokens", help="generated tokens per request"),
|
|
587
|
+
long_words: int = typer.Option(6000, "--long-words", help="the long-context prompt's size (0 = none)"),
|
|
588
|
+
model: Optional[str] = typer.Option(None, "--model", help="model (default: the active one)"),
|
|
589
|
+
restart: bool = typer.Option(
|
|
590
|
+
False, "--restart", help="stop a running vLLM server (default: refuse — the GPU must be yours)"
|
|
591
|
+
),
|
|
592
|
+
image: Optional[str] = IMAGE,
|
|
593
|
+
dry_run: bool = DRY,
|
|
594
|
+
timeout: float = typer.Option(1800.0, "--timeout", help="per-point server start timeout (s)"),
|
|
595
|
+
save: bool = SAVE,
|
|
596
|
+
plot: bool = PLOT,
|
|
597
|
+
):
|
|
598
|
+
"""MTP per config and draft count: tokens/step, per-position acceptance, decode t/s (restarts the server per point)."""
|
|
599
|
+
defaults = {
|
|
600
|
+
"legs": SPEC_LEGS,
|
|
601
|
+
"drafts": DEFAULT_DRAFTS,
|
|
602
|
+
"concurrency": "1 4",
|
|
603
|
+
"max_tokens": 256,
|
|
604
|
+
"long_words": 6000,
|
|
605
|
+
"model": None,
|
|
606
|
+
"restart": restart,
|
|
607
|
+
"timeout": 1800.0,
|
|
608
|
+
}
|
|
609
|
+
_run(
|
|
610
|
+
"spec",
|
|
611
|
+
{
|
|
612
|
+
"legs": legs,
|
|
613
|
+
"drafts": drafts,
|
|
614
|
+
"concurrency": concurrency,
|
|
615
|
+
"max_tokens": max_tokens,
|
|
616
|
+
"long_words": long_words,
|
|
617
|
+
"model": model,
|
|
618
|
+
"restart": restart,
|
|
619
|
+
"timeout": timeout,
|
|
620
|
+
},
|
|
621
|
+
defaults,
|
|
622
|
+
image,
|
|
623
|
+
None,
|
|
624
|
+
stages.DEFAULT_ITERS,
|
|
625
|
+
False,
|
|
626
|
+
dry_run,
|
|
627
|
+
timeout,
|
|
628
|
+
save,
|
|
629
|
+
plot,
|
|
630
|
+
)
|
|
631
|
+
|
|
632
|
+
|
|
633
|
+
@app.command("kv-cache-size")
|
|
634
|
+
def kv_cache_size(
|
|
635
|
+
legs: str = typer.Option(
|
|
636
|
+
KV_LEGS,
|
|
637
|
+
"--legs",
|
|
638
|
+
help="side:config, comma-separated; official = the stock vLLM image, mjolnir = --image",
|
|
639
|
+
),
|
|
640
|
+
model: Optional[str] = typer.Option(None, "--model", help="model (default: the active one)"),
|
|
641
|
+
restart: bool = typer.Option(
|
|
642
|
+
False, "--restart", help="stop a running vLLM server (default: refuse — the GPU must be yours)"
|
|
643
|
+
),
|
|
644
|
+
image: Optional[str] = IMAGE,
|
|
645
|
+
dry_run: bool = DRY,
|
|
646
|
+
timeout: float = typer.Option(1800.0, "--timeout", help="per-leg wait for the KV sizing (s)"),
|
|
647
|
+
save: bool = SAVE,
|
|
648
|
+
plot: bool = PLOT,
|
|
649
|
+
):
|
|
650
|
+
"""KV cache capacity per config: serves each leg (restarting the server), reads vLLM's KV sizing, stops it."""
|
|
651
|
+
_run(
|
|
652
|
+
"kv-cache-size",
|
|
653
|
+
{"legs": legs, "model": model, "restart": restart, "timeout": timeout},
|
|
654
|
+
{
|
|
655
|
+
"legs": KV_LEGS,
|
|
656
|
+
"model": None,
|
|
657
|
+
"restart": restart,
|
|
658
|
+
"timeout": 1800.0,
|
|
659
|
+
},
|
|
660
|
+
image,
|
|
661
|
+
None,
|
|
662
|
+
stages.DEFAULT_ITERS,
|
|
663
|
+
False,
|
|
664
|
+
dry_run,
|
|
665
|
+
timeout,
|
|
666
|
+
save,
|
|
667
|
+
plot,
|
|
668
|
+
)
|
|
669
|
+
|
|
670
|
+
|
|
671
|
+
@app.command()
|
|
672
|
+
def vllm(
|
|
673
|
+
smoke_modes: str = typer.Option(
|
|
674
|
+
SMOKE_MODES,
|
|
675
|
+
"--smoke-modes",
|
|
676
|
+
help="graph | eager | e2m3 (fp6 KV) | lmh8 (fp8 lm_head) | spec<N>; '' = none",
|
|
677
|
+
),
|
|
678
|
+
model: str = typer.Option("Qwen/Qwen3-0.6B", "--model"),
|
|
679
|
+
tokens: int = typer.Option(128, "--tokens"),
|
|
680
|
+
image: Optional[str] = IMAGE,
|
|
681
|
+
dry_run: bool = DRY,
|
|
682
|
+
timeout: float = typer.Option(1800.0, "--timeout"),
|
|
683
|
+
save: bool = SAVE,
|
|
684
|
+
):
|
|
685
|
+
"""The vLLM plugin: runner tests + greedy MJOLNIR-vs-FLASHINFER smoke runs (checkout only)."""
|
|
686
|
+
_run(
|
|
687
|
+
"vllm",
|
|
688
|
+
{"smoke_modes": smoke_modes, "model": model, "tokens": tokens, "timeout": timeout},
|
|
689
|
+
{"smoke_modes": SMOKE_MODES, "model": "Qwen/Qwen3-0.6B", "tokens": 128, "timeout": 1800.0},
|
|
690
|
+
image,
|
|
691
|
+
None,
|
|
692
|
+
stages.DEFAULT_ITERS,
|
|
693
|
+
False,
|
|
694
|
+
dry_run,
|
|
695
|
+
timeout,
|
|
696
|
+
save,
|
|
697
|
+
False,
|
|
698
|
+
)
|
|
699
|
+
|
|
700
|
+
|
|
701
|
+
def _from(order: list[str], start: str | None) -> list[str]:
|
|
702
|
+
if not start:
|
|
703
|
+
return list(order)
|
|
704
|
+
if start not in order:
|
|
705
|
+
raise typer.BadParameter(f"--from must be one of: {', '.join(order)}")
|
|
706
|
+
return order[order.index(start) :]
|
|
707
|
+
|
|
708
|
+
|
|
709
|
+
def _sequence(names: list[str], image, gate, dry_run, plot) -> int:
|
|
710
|
+
rc = 0
|
|
711
|
+
for name in names:
|
|
712
|
+
defaults = _DEFAULTS[name]
|
|
713
|
+
try:
|
|
714
|
+
r = _execute(
|
|
715
|
+
name,
|
|
716
|
+
dict(defaults),
|
|
717
|
+
dict(defaults),
|
|
718
|
+
image=image,
|
|
719
|
+
ls=None,
|
|
720
|
+
iters=stages.DEFAULT_ITERS,
|
|
721
|
+
gate=gate,
|
|
722
|
+
dry_run=dry_run,
|
|
723
|
+
timeout=180.0,
|
|
724
|
+
save=True,
|
|
725
|
+
plot=False,
|
|
726
|
+
)
|
|
727
|
+
except Exception as e: # noqa: BLE001 — one broken stage must not cost the rest of the suite
|
|
728
|
+
console.print(f"[red] verify {name} crashed: {type(e).__name__}: {e} — continuing[/red]")
|
|
729
|
+
r = 1
|
|
730
|
+
if r == PREFLIGHT_RC:
|
|
731
|
+
return r # no image / no docker: every later stage would fail the same way
|
|
732
|
+
if r != 0:
|
|
733
|
+
console.print(f"[red] verify {name} exit {r} — continuing[/red]")
|
|
734
|
+
rc = r
|
|
735
|
+
if plot and not dry_run:
|
|
736
|
+
_replot(load_layout())
|
|
737
|
+
return rc
|
|
738
|
+
|
|
739
|
+
|
|
740
|
+
_DEFAULTS: dict[str, dict] = {
|
|
741
|
+
"correctness": {"dtypes": "fp8 bf16", "match": "", "fail_fast": False, "timeout": 180.0, "quick": False},
|
|
742
|
+
"sweep": {"legs": KERNEL["verify.sweep"]},
|
|
743
|
+
"hndsweep": {"legs": KERNEL["verify.hndsweep"]},
|
|
744
|
+
"kvfmt": {"variants": stages.KVFMT_DEFAULT, "refs": KERNEL["verify.kvfmt.refs"], "wall_iters": 200},
|
|
745
|
+
"quant": {"formats": stages.QUANT_FORMATS, "batches": 2},
|
|
746
|
+
"ring": {"stages": stages.RING_DEFAULT, "base": "", "refs": "fi"},
|
|
747
|
+
"prefetch": {"distances": "0 1 2 4", "base": "--kv_format fp8", "refs": "fi"},
|
|
748
|
+
"pdl": {"batches": "1 8", "dtypes": "fp8", "iters": 200},
|
|
749
|
+
"ab": {"variants": stages.AB_DEFAULT, "refs": "fi", "wall_iters": 200},
|
|
750
|
+
"fp6prof": {"variants": stages.PROF_DEFAULT, "count": 3, "section_set": "full"},
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
|
|
754
|
+
@app.command()
|
|
755
|
+
def bench(
|
|
756
|
+
image: Optional[str] = IMAGE,
|
|
757
|
+
gate: bool = GATE,
|
|
758
|
+
dry_run: bool = DRY,
|
|
759
|
+
plot: bool = typer.Option(True, "--plot/--no-plot"),
|
|
760
|
+
start: Optional[str] = typer.Option(None, "--from", help="resume the sequence at this stage"),
|
|
761
|
+
):
|
|
762
|
+
"""Every benchmark stage with default parameters (one headline run each), then re-render the charts."""
|
|
763
|
+
raise typer.Exit(_sequence(_from(stages.BENCH_ORDER, start), image, gate, dry_run, plot))
|
|
764
|
+
|
|
765
|
+
|
|
766
|
+
@app.command("all")
|
|
767
|
+
def all_(
|
|
768
|
+
image: Optional[str] = IMAGE,
|
|
769
|
+
gate: bool = GATE,
|
|
770
|
+
dry_run: bool = DRY,
|
|
771
|
+
plot: bool = typer.Option(True, "--plot/--no-plot"),
|
|
772
|
+
start: Optional[str] = typer.Option(None, "--from", help="resume the sequence at this stage"),
|
|
773
|
+
):
|
|
774
|
+
"""Correctness + every benchmark stage, then re-render the charts."""
|
|
775
|
+
raise typer.Exit(_sequence(_from(stages.ALL_ORDER, start), image, gate, dry_run, plot))
|
|
776
|
+
|
|
777
|
+
|
|
778
|
+
@app.command("show")
|
|
779
|
+
def show_cmd(
|
|
780
|
+
stage: Optional[str] = typer.Argument(None, help="stage (default: every stage with a saved run)"),
|
|
781
|
+
any_run: bool = typer.Option(False, "--any", help="latest run of any parameters, not just default runs"),
|
|
782
|
+
):
|
|
783
|
+
"""Re-print the latest saved run of a stage (or every stage)."""
|
|
784
|
+
layout = load_layout()
|
|
785
|
+
names = [stage] if stage else list(stages.STAGES)
|
|
786
|
+
found = False
|
|
787
|
+
for n in names:
|
|
788
|
+
doc = results.latest(layout.verify_dir, n, default_only=not any_run)
|
|
789
|
+
if doc:
|
|
790
|
+
found = True
|
|
791
|
+
show(doc)
|
|
792
|
+
if not found:
|
|
793
|
+
console.print(f"[yellow]no saved verify runs under {layout.verify_dir} — run: mjolnir verify sweep[/yellow]")
|
|
794
|
+
|
|
795
|
+
|
|
796
|
+
@app.command("list")
|
|
797
|
+
def list_cmd():
|
|
798
|
+
"""Stages and their saved runs."""
|
|
799
|
+
layout = load_layout()
|
|
800
|
+
for name, st in stages.STAGES.items():
|
|
801
|
+
rs = results.runs(layout.verify_dir, name)
|
|
802
|
+
last = rs[-1]["run_id"] if rs else "—"
|
|
803
|
+
n_def = sum(1 for r in rs if r.get("default"))
|
|
804
|
+
console.print(
|
|
805
|
+
f" [bold]{name:<12}[/bold] [dim]{st.kind:<11}[/dim] runs {len(rs):>3} (default {n_def:>2}) "
|
|
806
|
+
f"last {last:<16} {st.summary}"
|
|
807
|
+
)
|
|
808
|
+
console.print(f"[dim] raw results: {layout.verify_dir}[/dim]")
|
|
809
|
+
|
|
810
|
+
|
|
811
|
+
def main(): # pragma: no cover
|
|
812
|
+
app()
|
|
813
|
+
|
|
814
|
+
|
|
815
|
+
if __name__ == "__main__": # pragma: no cover
|
|
816
|
+
sys.exit(main())
|