Confirm an arm from the server, and check the host before the run #13
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/tenstorrent-and-arm-identification"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What a Tenstorrent Blackhole host needs from this harness that it did not yet
have — plus the one lesson that is not Tenstorrent-specific at all and matters
most.
Everything here was checked against infbench's actual code first. The survey in
docs/FIELD-NOTES.mdhad already identified several of these as "to port";this implements (B) and (T), extends (N) to a runtime guard, and leaves the
rest of that list untouched. Several items on the original gap list turned out
to be already handled, in some cases better than in tt-stack — listed at the
bottom rather than duplicated under a new name.
Backed by a measured incident (citations are files in
tt-stack)Arm self-identification —
src/infbench/arm.py, wired into stage 1.A declared arm is confirmed against the server's own output (an engine log, the
*_infometrics gauges,/v1/models) and fails closed when its banner isabsent. Incidents: vLLM's
GPU KV cache sizeis a token count derived frommax_model_len x max_num_seqsand is identical between bf16 and bf8 KV, soevery bf8 result before
patches/tt-metal/0016had no runtime evidence of itsarm (
CLAUDE.md, Gate A entry); a control that reused a patched tree inheriteda different flag default and inflated a delta from +4.5% to +20.2%
(
bench/ab/argmax-16k-ab.sh, which now fails closed on a missing banner); andtwice in one session a flag failed to reach a container through an explicit env
list.
forbidscatches the reverse case — a control that became the treatment —which is invisible from the treatment side alone.
Host memory floor —
preflight.memory_check. Measured: with zerocontainers, zero device processes and the module at refcount 0,
rmmod+modprobereclaimed 0 MB while a reboot reclaimed 4.2 GiB(
bench/runs/host-pinned-leak-20260914T063000Z.jsonl). Two boots attemptedbelow ~3 GB available that day hard froze the host. The failure message says
a driver reload is not the fix.
host_mem_before/host_mem_afterare bothrecorded, because a run that ends with less than it started is the signature of
pinned pages outliving a killed process.
Exclusive host lock —
preflight.HostLock. That host runs exactly onett-metal process, and it is not per card:
/dev/tenstorrent/1does notavoid
"another process is already holding the sysmem NOC address space". Thelock is
mkdir-based with owner and pid inside and never steals a staleone. The bash version's trap — a trap handler returns to where it was
interrupted unless it exits, so the lock was released on SIGTERM and
containers launched unlocked — is recorded in the module docstring.
Foreign-container check + recovery text —
tenstorrent.py. A job we didnot start holds no lock of ours. And
docker rm -fon a live tt-metalcontainer wedges the cards so that the next open reads as a ttnn/firmware
version mismatch — a known-good image failed identically right afterwards. The
harness never resets a board and never stops a container; it prints
docker stop -t 90andsudo tt-smi -r, for the same reasontt-flashis notpackaged in tt-stack.
Tokenizer-based prompt sizing —
calibrate. Now prefers the server's own/tokenize(same chat template as the completion path, exact, no generation)and falls back to the existing two-probe solve. A remembered chars-per-token
ratio has been wrong twice on this project, most recently 3.26 assumed against
a measured 4.55.
Time-to-last-token per stream — stage 4. Makespan was already the
throughput denominator; the distribution was not reported. Owner directive
2026-09-13 makes TTLT first-class, and the reason is measured: at B=8/8k decode
improved 14.1% while makespan improved 11.0%, because prefill was 74-79% of
wall (
bench/runs/sweep-32k-20260914T142154Z.jsonl).Proposals — the principle is measured, this shape of it is not
src/infbench/sensitivity.py. The principle is paid for four times overin tt-stack: a
text_sha256identical under every configuration; a stallcounter whose window opened only after the last prefill; a py-spy classifier
that bucketed by the whole stack string and returned the opposite verdict; a
cache bench that could not tell a cache hit from intra-batch head sharing
(
docs/INSTRUMENT-INTEGRITY.md, already cited under (N) in FIELD-NOTES). TheAPI here — a negative-control arm and a sensitivity arm, with a 5% default
separation floor and a WITHHELD verdict — is my proposal, not a ported design.
The 5% floor and the 4 GiB memory floor are judgement calls. The floor
under the first is the 0.33% run-to-run noise measured on a serving host; 5% is
an order of magnitude above it and otherwise unvalidated. The second is set
above the ~3 GB at which the host actually froze, not at a measured safe point.
The decode-depth sensitivity arm in stage 4 applies the guard to an
existing ladder. A flat reading is flagged rather than published as a
depth-flat engine; the rows already carry
depth_drift_pctandprefill_leaked_into_decode, which are the two explanations to check first.This has not been run against real hardware.
What infbench already handled — and in places better than we do
Checked before implementing; none of it was duplicated.
/health→/metrics→/v1/models→ a real generated token, withready_timeout_s = 600and a comment naming the ~510 s Tenstorrent load. Thegateway-returns-400-for-a-minute case that voided two of our runs is caught
by the generation rung.
PerfRecordrefuses to exist without passing gates and anArrivalModel. The second is something we have no equivalent of at all,and it is a real hole on our side: our A/B records do not say how load
arrived.
usageblock, withtps_sourcerecordingwhen they fell back to counted SSE chunks — the failure that overstated our
decode ~3x once is already labelled here.
max(ttft), not the mean, and the TTFTstaircase is recorded in launch order precisely so a serialised-prefill
pattern is visible.
corpus_seed,grow_from_session,cold_claim_validagainst the engine's own cached fraction, and a nominal /computed prefill split. Stricter than our cache bench.
record is the median repeat, never a per-field average. We should adopt
this framing.
overlay_hashfor bind-mounted patchedfiles, which is exactly the hole
vllm-tt-serve.shopens in an image digest.sharper distinction we lacked: divergent phrasing with the answer intact is
a different finding from a wrong answer.
Notes for review
nix flake checkgreen (pytest + ruff + gitleaks). Every newcheck has a fixture that makes it fail, per the (N) convention — including
the empty-evidence case, which is the one that matters.
[arm]and[preflight]are both inert unless configured; default behaviouris unchanged except for the two added stage-4 fields and one report line.
/tokenizecan shiftoverhead_tokensby a fewtokens against the generation-probe path.
Calibration.keyrounds overhead tothe nearest 64, so most baselines will not churn — but a baseline sitting on a
boundary could be orphaned, which is the intended refusal rather than a
silent comparison.
only.
🤖 Generated with Claude Code
View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.Merge
Merge the changes and update on Forgejo.Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.