measure(MODEL-NEMOTRON-H-ABI-A2P): the first Nemotron ratios -- decode 0.001392x, load 2.12x faster, and the GPU idle for 93.7% of our decode (#1250) - #1251
Open
localai-bot wants to merge 20 commits into
Conversation
…-prefill discriminator The A3 gate diverges on real weights while the CPU gate is 12/12 green, so neither arm can say whether the decode step reads the state the previous step wrote. Two instruments, both off by default. `VT_NEMOTRON_H_DIAG` prints, per step, the decode/prefill split, the recurrent slot indices, the has-initial mask, and per Mamba2 layer the L2 of the state gathered in and the state written out. On the CPU fixture it reads a healthy carry, which is what makes it usable as a negative control on the device. `nemotron-h-gen --fresh-prefill` generates the same stream one token per completion from a growing prompt, so every token comes out of a prefill and nothing is carried. Same engine, same weights, same public entry point, so a stream that is right this way and wrong the normal way names the carry. This is scaffolding for the #1157 measurement, not the repair. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…slot can bisect The CPU arm now decodes this checkpoint token-exact against the oracle golden, so the divergence is on the device side and the question is which device arm. The MoE block is 23 of the model's 52 layers and its own gate exercises T=4 and T=2 only, never the T=1 a decode step is, so it is the arm with the least coverage at the shape that fails. `VT_NEMOTRON_H_DEVICE_MOE=0` routes those layers back through the host reference the CPU run proves correct, and `--both-modes` runs the decode and fresh-prefill streams over ONE engine load, which is the only affordable shape when a load is minutes long. Both default to today's behaviour, so nothing moves unless a diagnostic asks it to. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
… the decode one `NemotronHMoeBlockDevice` runs on 23 of this model's 52 layers, and its gate measured T=4 and T=2. Both are prefill shapes. Every token after the first comes out of a step carrying exactly one token, so the width the model spends its entire decode in was the width nothing measured. It is not a cosmetic gap. `MarlinMoeAlignBlockSizeSelect` and `MarlinMoeAlignSizes` branch on the token count relative to the expert count, and T=1 against 128 experts is on the other side of that branch from T=4. A width loop rather than a third copy, so the three cannot drift, with the covered count asserted afterwards: a loop that ran over nothing would otherwise report a clean pass, which is the shape this tree keeps finding. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…he failing model is a third `fa2_decode_qwen3` is DEFAULT ON for any bf16 causal pure-decode at head_dim 128. Its own comment scopes it to "only Qwen3-dense hits this", and that stopped being true when NemotronH-3.5-Lightning landed: it is head_dim 128 as well, and its decode goes through the same launcher. Every case in this file measured 16/8 and 32/8 — ngroups 2 and 4, the two Qwen3-dense gate configs. NemotronH is 32 query heads over 2 KV heads, ngroups 16, four times the widest group count the swapped presentation was ever measured at. The launcher packs ngroups as seqlen_q, so the group count is not a detail of the geometry, it is the grid. The ratio is added to all five d128 cases rather than to one, so the prefill arm, the plain-varlen decode, the group-swap decode, the swap-vs-plain near-tie and the num_splits cap all see it. Nothing else moves. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…very decode step saw the same token `ModelForwardInput::device_token_ids` is non-null exactly when the async runner's device combine has already spliced each DECODE row's sampled token into its own device buffer and left the host `token_ids` stale on purpose — not materializing it on the host is the synchronize ENG-ASYNC-SCHED W4 exists to remove. `NemotronHPagedForward` uploaded the host vector anyway, so on the default CUDA path every decode step embedded the same placeholder id. The measurement, all on the released 30B NVFP4 checkpoint and the committed oracle golden, through `include/vllm.h` and nothing else: CPU, decode 96/96, STRICT PASS, full rows 3 GB10, decode 4/24 GB10, fresh-prefill 24/24 (one token per completion: no decode step) and the per-layer trace names the mechanism rather than inferring it. At the prefill step CPU and GB10 agree to six digits on every one of the 52 layers. At the first decode step the gathered conv/SSM state is IDENTICAL on the two — the recurrent carry is exact — while layer 0's embedding row differs, and it reads 0.228135 on GB10 at BOTH decode steps although they consume different tokens. A constant embedding is a constant input id. This refutes the cause on record. #1157 reasoned that `gm.num_decodes` might classify a decode as a prefill and hand the mixer zeros; the trace reports `nd=1 np=0 init=[1]` on every decode step, and mutating that mask to 0 turns the existing A2-P CPU gate RED (1 case, 6 assertions), so the gate was never blind to it. Kimi-Linear was cut from this same divergence (kimi_linear_device.cpp:2270) and every other registered forward already honours the field. Nothing could see that this one did not: the runner sets the pointer only under VLLM_CPP_CUDA with a live device mirror, so no CPU gate can reach the branch. The host-side id range check stays on the host branch only. Validating device ids would need the D2H synchronize this path exists to delete. Closes #1157 FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
… the seam that allowed it The row's public record still said the A3 gate was pending on a toolchain, and `#1157` still carried a cause that the measurement refutes. Both are corrected against numbers rather than re-quoted. `benchmark-record.md` carries the three-arm table the diagnosis rests on — host 96/96, GB10 decode 4/24, GB10 fresh-prefill 24/24, one binary and one checkpoint — plus the per-layer trace that puts the divergence at layer 0's embedding row while the recurrent state gathered identical on both sides. `docs/STATUS.md` and `docs/BENCHMARKS.md` move the row from "gate pending" to "host gate passes, device fixed, sm_121a re-run pending a lease", inside the STATUS size ratchet. [#1217](#1217) is filed and listed under the spec's `## Owed`: the runner hands `device_token_ids` to whatever model the step routes to, its own comment claims a model that ignores it is never given one, and nothing enforces that. Two models have now been cut from the identical divergence. Not fixed here because both closes change a shared seam or checker semantics. The two diagnostic knobs are documented in `docs/ENVIRONMENT.md` rather than allowlisted as kernel-internal: `VT_NEMOTRON_H_DIAG` is how this bug was separated from the carry, and the next reader of this model should find it. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
FOLLOWING_AGENTS_PROTOCOL origin/main moved 26 files under this branch while the #1157 diagnosis was running on a leased GB10. Merged rather than rebased so the branch keeps the measurement order: the instruments, then the host and device arms they produced, then the fix those arms name. No conflict in either direction; nothing this branch owns was touched. Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
… not call itself temporary The block header said TEMPORARY while `docs/ENVIRONMENT.md` documents the knob and the next divergence on this model will be diagnosed with it. It now says what it is for instead: the runner hands a decode step a device-resident input id AND a recurrent page, and when the tokens come out wrong only the per-layer numbers say which of the two the step actually read. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
GitHub reported CONFLICTING; the merge resolves clean locally because GitHub does not run the `merge=union` driver `.gitattributes:7` sets. The union auto-merge of the index was DISCARDED anyway: main's file taken wholesale, only rows whose KEY main lacks re-appended, main's file asserted a strict PREFIX of the result, zero duplicate keys. The fix itself is unchanged by the merge and re-verified present after it. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
A speed number taken on a tree without PR #1221 measures wrong tokens: without it every decode step on the CUDA path embeds the same placeholder id, and the device leg of the A3 token gate reads 4/24 rather than 96/96. The merge is explicit and named so the measured SHA states which fix it carries; it collapses to nothing once #1221 lands on main. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
… and the two KV facts that make it comparable (#1250) There is no Nemotron number on any axis and none is claimed, and the A3 token gate now passes on the device, so one is takeable. The spec fixes what must be true before a number counts: the timing legs and the token comparison are the same run, the five CUDA feature lines must read `ENABLED for [121a]` or the result is VOID rather than slow, and the denominator is vLLM at the pin with CUDA graphs on. Two comparability facts get decided here rather than in a log. Our `gpu-memory-utilization` does not size the KV pool (#83), so the pool is stated explicitly and the oracle is given the same 8192 KV tokens. And the oracle has never run a model inside a lease, so its leg runs last, after our numbers are already persisted, under a watchdog rather than a sampler. The expected answer is a poor ratio whose cause is already named: the NVFP4 `lm_head` and the 46 FP8 mamba projections both still execute host-side, owned by A2-Q2b and A2-Q1. That is the current state, not a ceiling. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…dget and repeat a battery over one load (#1250) `nemotron-h-gen` could not say what KV pool it wanted, so it inherited the 256-block fallback the engine takes when `gpu_memory_utilization` is accepted and does not size the pool (#83). Both legs of the A3 gate logged that warning. A speed comparison whose two sides hold different KV budgets is not like-for-like on the memory axis, and a driver that cannot state its budget cannot make them match. `--num-blocks` and `--kv-cache-memory` pass straight to the two `vllm_model_params` fields that already carry it, and the resolved sizing is printed before the load rather than left to be inferred from a warning. `--repeat` reruns the whole battery over ONE engine load. A load here is minutes long, so a second timing leg is otherwise a second load, and paired legs out of one process are the only affordable same-binary A/B on this model. Every leg is compared against the golden and the verdict reads grand totals, so a battery that passed once and diverged on the second leg cannot report a pass, and no timing leg is an ungated one. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
The explicit merge of `row/MODEL-NEMOTRON-H-ABI-A2P-1157-fix` this branch carried was a stand-in for a fix that had not landed. It has: `0ea5d249f` is an ancestor of `origin/main`, so the measured base is now main itself and the diff collapses to this branch's own work. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code] # Conflicts: # examples/nemotron_h_gen/main.cpp
…overs one configuration rather than a model (#1250) #1221 landed as `0ea5d249f`, verified by content on `main` rather than by the API, so the spec's stand-in wording about measuring off the fix branch is dead and the measured base is a SHA at or after that merge. The second edit is the one that matters. A token gate is evidence about the configuration it ran, and this one ran three prompts, 32 greedy tokens each, batch 1 sequential, `max_model_len 512`, a 256-block pool. The timing legs run that configuration and nothing else, and a leg that deviates in KV sizing, batching or sampling is reported as ungated rather than allowed to borrow coverage the gate never gave it. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…and corrected two instruments the number depends on (#1250, #1253) `nvcc --version` is not the toolkit postcondition. The rc worker ships a partial CUDA 13.0 whose compiler answers while the cuBLAS development component is absent, so a harness that installs only when `nvcc` is missing installs nothing, CMake prints all five feature lines `ENABLED for [121a]`, and generate then fails with `Target "vllm" links to CUDA::cublasLt but the target was not found`. That is #1185's own unconditional-repair rule applied to a postcondition chosen wrong, which is a different defect from skipping the repair. The oracle's recorded hazard is also not what stopped it. The model run did not reboot the box -- peak host use was 28,534 MB of 122,502 MB and the watchdog never fired -- it died on `Python.h: No such file or directory` inside torch inductor, because Triton compiles `cuda_utils.c` at runtime and the worker has no `python3-dev`. It surfaces as `Engine core initialization failed ... Failed core proc(s): {}`, an empty proc set that names nothing, so it reads as an oracle limitation and is a missing dev package. The recipe was already written down for `thor:gpu0` and simply had not been applied on `dgx`. And contention has exactly one usable instrument on this box: `--query-gpu=memory.used` reads `[N/A]` on GB10, so a benchmark sampling it is blind to the state that would void it. At this lease's start the compute-apps list reported 36,396 MiB still held by the previous lease's straggler. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…ned device, with the causes and the operand pinned (#1250, #1253) No number is recorded on any axis. `dgx:gpu0` reads `unhealthy`, `out of the pool lease_expired`, with a live heartbeat and refreshing labels, and clearing a quarantined device is a human's call. The job is queued at position 1 pinned to the measured SHA and runs when the device returns to the pool. Silence would have been the wrong record here: pending a named resource is a result, and an unwritten one reads as nobody having tried. The record carries what the one lease that did start established, because all of it is load-bearing for the number when it arrives. The contention state at the window, including a previous lease's straggler holding 36,396 MiB and the fact that `--query-gpu=memory.used` reads `[N/A]` on GB10 so the compute-apps list is the only device-side instrument. The clock, which cannot be pinned from inside the worker (`-lgc` returns 4, permission denied) and so is recorded rather than controlled. The operand, hashed. And the mechanism behind the 10.3 s/token the A3 gate legs measured, taken from the per-layer trace rather than guessed: 6 of 52 layers are attention and stay on the device, 23 MoE layers run through the NVFP4 Marlin arm, and 23 Mamba2 layers download the normed hidden, run the mixer on the CPU queue and upload it, once per layer per token, after which the NVFP4 `lm_head` projects on the host. The next traceable hypothesis is A2-Q1 (#940) then A2-Q2b, measured with the same battery on the same binary so the deltas are attributable. That is a gap with a named cause, not a ceiling. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…and the GPU is idle for 93.7% of the decode (#1250, #1253) Numerator only, and no ratio is claimed. On an idle `dgx:gpu0` through `rc run`, at a tree containing #1221, with all five CUDA feature lines `ENABLED for [121a]`: engine load 280.9 s, 10.3194 s per output token, 0.09691 tok/s at batch 1, peak host 44,616 MB of 122,502. Every timing leg is a gated leg -- 96/96 `mode=decode` on both, `STRICT PASS`, 192 of 192 tokens, zero short rows -- so no number here comes from a configuration whose tokens were not compared in the same process. The same-binary A/B over one load is 1.0016 warm and the five warm prompts spread 0.245%, so the figure is stable enough that the next lever's delta will be readable. The load-bearing part is not the rate, it is why. `nvidia-smi` reported GPU utilization 0% in 2,019 of 2,155 samples across the measured window, so the GPU was busy in 6.31% of it, and `gpu_clock_state`'s own comparison gate would REFUSE that window for failing its majority-busy floor. The refusal is the result: this decode is not GPU work. It is the 23 of 52 layers that download the normed hidden, run the mixer on the CPU queue and upload it once per layer per token, plus the NVFP4 `lm_head` projecting on the host. So A2-Q1 (#940) and then A2-Q2b are the next traceable hypotheses, and each now has a prediction it has to answer rather than a story: it must raise that busy fraction. The denominator has its identity pinned and its blocker measured rather than assumed. With `python3-dev` present the pinned oracle initialized, loaded in 230.4 s and completed `torch.compile`, which is further than it has ever got inside a lease, then took 104,992 MB of host in the next step and was killed by this row's watchdog. The box did not reboot. `gpu_memory_utilization` 0.9 of ~119 GiB of unified memory is ~107 GB, which is that peak, so the fraction is the first thing to vary. And vLLM's hybrid allocator forces a 4192-token attention block against our 32, so the two sides can be matched on KV token capacity and never on block count; a future ratio has to say which. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…Nemotron ratios exist: decode 0.001392x, load 2.12x faster (#1250, #1253) The pinned oracle completed a model run inside an `rc` lease for the first time, so these stop being numerator-only. Same box, same boot, same checkpoint directory, same three pre-tokenized prompts, same 32 greedy tokens with `ignore_eos`, batch 1 sequential, two legs over one load on each side, and `enforce_eager=False` throughout so CUDA graphs were never disabled. Decode is 10.3194 s per output token against 0.014369, which is 0.001392x. Engine load is 280.9 s against 596.3, so we are 2.12x faster on the one axis we win, measured against an oracle whose torch.compile cache was already warm. Two refusals travel with those ratios rather than being netted out of them. The project's own clock gate exits 1 on this pair: both windows fail its majority-busy floor, vLLM's spread exceeds its ceiling, ours was throttled, and persistence was off on both. Same boot, both medians 2411 MHz, offset 0.0, and the tool's recorded basis of 0.7548 points of kernel time per point of clock cannot reach 718x, so the refusal is recorded and the conclusion is not. And vLLM's hybrid allocator resolves its own block size against our 32, so the KV pools differ by 78.6x and could not be matched at all; the memory axis is reported raw with that stated, and the straggler still resident in the oracle window is named rather than subtracted. The oracle also read 180/192 against its OWN committed golden, deterministically in both legs, because the memory configuration the box forced moved its resolved block size to 512. Ours read 96/96 on the same golden. The throughput comparison survives it, since max_tokens is fixed and both sides took the same number of decode steps, but a token-exact gate against this golden does not survive a change to the oracle's memory configuration, and that now bounds how the gate may be re-run. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
The measurement's own SHA is unchanged by this merge and is recorded in the spec and the benchmark record; this only brings the branch up to a moving main so the records land against the tree they describe. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
GitHub reported CONFLICTING; the merge resolves clean locally because GitHub does not run the `merge=union` driver `.gitattributes:7` sets. The union auto-merge of the index was DISCARDED anyway: main's file taken wholesale, only rows whose KEY main lacks re-appended, main's file asserted a strict PREFIX of the result, zero duplicate keys. The measurement this records is unchanged by the merge: 718.2x slower per output token, 0.4711x on engine load (we are 2.12x faster there), both legs gated 96/96 `STRICT PASS` at `mode=decode`, box verified idle, `enforce_eager=False`, all five CUDA feature lines `ENABLED for [121a]`. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
There was no Nemotron number on any axis.
docs/BENCHMARKS.mdcarriedMODEL-NEMOTRON-H-ABI-A2Pas "No speed number, by the unit's own rule". Thisbranch takes the first ones, against the pinned vLLM oracle, which completed a
model run inside an
rclease for the first time.FOLLOWING_AGENTS_PROTOCOL
The numbers
Both legs on
dgx:gpu0throughrc run, same box, same boot id, samecheckpoint directory, same three pre-tokenized prompts, same 32 greedy tokens
with
ignore_eos, batch 1 sequential, two legs over one engine load on eachside. Tree
5325b7b970b67f97a77834e907fc34fb2990b71e, which contains0ea5d249f(#1221).enforce_eager=Falsethroughout: CUDA graphs were on andwere never disabled.
Every timing leg is a gated leg. Ours read
TOKEN MATCH: 96/96 over 3 prompt(s) (full rows=3, short rows=0, mode=decode)on BOTH legs,STRICT PASS,192 of 192 tokens, so no number here comes from a configuration whose tokens were
not compared in the same process. The same-binary A/B over one load is 1.0016
warm and the five warm prompts spread 0.245%.
The build was not degraded: CUDA 13.3.73,
CFG_RC=0,BUILD_RC=0, zero compileerrors, and
fp4-mma/cutlass-nvfp4/cutlass-fp8/marlin-nvfp4/fa2each
ENABLED for [121a], counted asFEATURE_LINES_SEEN=5 DEGRADED_FEATURE_LINES=0rather than eyeballed. The box was idle:--query-compute-appsempty, 4,892 of 122,502 MB used.Two refusals that travel with the ratios
They are recorded beside the numbers rather than netted out of them.
The clock gate refuses this pair.
gpu_clock_state compareexits 1 with sixreasons: ours 6.31% busy and vLLM 31.05% busy against a 50% floor, vLLM spread
5.14% against a 5.0% ceiling, ours throttled
SwPowerCap, persistenceDisabledon both. Same boot id, both medians 2411 MHz,median_offset_pct0.0. What the refusal cannot do is explain the result: the tool's own recorded
basis is 0.7548 points of kernel time per point of clock, and the gap is 718x.
The KV pools could not be matched. vLLM's hybrid allocator resolves its own
block size — 512 here, 4192 at other settings — against our 32, so a token
capacity match is the most that was ever available and this run did not get one.
The memory axis is therefore reported raw, and the straggler still resident in
the oracle's window (PID 40514, 22,986 MiB, the EngineCore this row's own
watchdog killed) is named rather than subtracted.
What names the bottleneck, and why it is not a ceiling
nvidia-smireported GPU utilization 0% in 2,019 of 2,155 samples of ourwindow. The GPU was busy in 6.31% of it. That is the driver's own answer, not
a reading of the source, and the clock helper's refusal to attribute such a
window IS the finding: this decode is not GPU work.
It matches the mechanism exactly. Of 52 layers, 6 are attention and stay on the
device, 23 MoE layers run on the device through the NVFP4 Marlin arm, and 23
Mamba2 layers bounce — the normed hidden is downloaded, the mixer runs on the
CPU queue, the result is uploaded — once per layer per token; then
NemotronHHostLmHeadprojects on the host becausenemotron_h.cpp:1031-1034refuses the NVFP4
lm_headon a non-CPU queue.So the next traceable hypothesis is A2-Q1 (#940), the 46 FP8 W8A8 mamba
projections that are 36.6% of decode bytes, then A2-Q2b for the
lm_head. Eachnow has a prediction it must answer rather than a story: it has to RAISE that
6.31% busy fraction, measured with the same battery on the same binary so the
delta is attributable.
The oracle does not reproduce its own golden here
ORACLE TOKEN MATCH: 180/192— prompt 2 matched 26/32, identically in both legs,so it is deterministic within the configuration. Ours matched 96/96 on the same
golden. The golden was captured at the oracle's default memory configuration,
and the two knobs the box forced (
gpu_memory_utilization=0.30because 0.9 took104,992 MB of a 122,502 MB unified pool and was killed, and
max_num_batched_tokens=512) moved its resolvedblock_sizeto 512 and with itthe reduction order. The throughput comparison survives —
max_tokensis fixedwith
ignore_eos, so both sides took the same number of decode steps — but atoken-exact gate against this golden does not survive a change to the oracle's
memory configuration, and that now bounds how the gate may be re-run.
Also here
--num-blocks,--kv-cache-memoryand--repeatonnemotron-h-gen. Thedriver could not state its own KV budget and inherited the 256-block fallback
the engine takes when
gpu_memory_utilizationis accepted and does not size thepool (#83); both legs of the A3 gate logged that warning, and this run does not.
--repeatruns the battery again over one load, and the verdict reads grandtotals, so a battery that passed once and diverged on the second leg cannot
report a pass.
#1253, filed and fixed in this flow. The oracle could not run a model in a
lease for want of
python3-dev— Triton compilescuda_utils.cat runtime andthe failure surfaces four frames up as
Engine core initialization failed ... Failed core proc(s): {}, an empty proc set naming nothing. Andnvcc --versionis not the toolkit postcondition: the worker ships a partial CUDA 13.0 whose
compiler answers while cuBLAS dev is absent, so CMake printed all five feature
lines and then failed with
Target "vllm" links to CUDA::cublasLt but the target was not found. #1185's recorded hazard for the oracle — a reboot of the box inthe step after
torch.compile— did NOT occur; the watchdog held.Closes #1250.
Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [claude-code]