P150 single-stream decode is 48.6 ms; gate is ≤25.0 ms, no MTP #53
Labels
No labels
human-approved
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
bitpartner/tt-stack#53
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
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?
Status 2026-09-23. Single-stream Qwen3.8-27B, TP=2, two P150a, no MTP. Gate is ≤25.0 ms/token on the 4k/256 protocol. Not met.
Serving stays stopped for this whole campaign (
tenstorrent-serving.serviceinactive). Do not start it. Do notdocker rm -fa live tt-metal container. One tt-metal process. Lock/tmp/ttlock.Measured this campaign
bench/runs/p150-decode-40-control-20260923T1652Z.jsonl: 4k/256 48.619 ms (20.57 tok/s), 16k 49.707 ms, text sha256bafed7a9f10786fe726a5f7d3bea77cfd18fe22262edeb0b9c5265ebd9b0b2ea. Same boot,max_model_len=34816,max_num_seqs=1, prefix caching off, speculative config null. Arm env:QWEN36_DECODE_LOGITS_RM=1,QWEN36_LM_HEAD_GATHER_MODE=none,QWEN36_1D_GRID_ATTN_QKV=11x4.bench/runs/p150-decode-40-hypA-hostdev-20260923T1705Z.jsonl: 48.563 ms, same sha. py-spy, 1799 samples, step 48.563 ms:ttnn.decorators.__call__leaf 45.297 ms (93.3%), other host leaves 3.266 ms,process_output_decode0.486 ms. The itemiser text that says "~47 ms host" is wrong. That frame is a blocked native call. The device-wait hint list does not name it.Why ≤25.0 ms is the edge
N1
bench/runs/n1-matmul-read-compute-split-20260912T174500Z.jsonl: the seven production decode projections, standalone, trace, M=32. 30.381 ms = 22.132 ms DRAM + 8.249 ms dtype-independent floor. Arithmetic is <2 ms (LoFi→HiFi4 barely moves). Marginal read rate 454–469 GB/s on the 11-wide grids. Fit error about ±1.5 ms. These are standalone ceilings, not in-model savings.45.3 ms is inside one traced
ttnncall. Subtracting the 30.4 ms standalone matmuls leaves ~15 ms of other device work in that call (in-model contention can move this by a few ms; do not treat 15 as exact). Host is 3.3 ms.lm_headbytes are not in the 22.1 ms. So 25.0 ms requires the floor and the non-matmul device work to hide inside the weight read, not merely to get smaller one at a time.Closed — do not redo
QWEN36_1D_GRID_*tuning. Inside a 0.5% band.ATTN_QKV=11x4is already the arm.gate/upare already bf4.down_projbf8 is deliberate.LM_HEAD_GATHER_MODE=noneas a new win. It is already in the 48.6 ms number. It regresses by ~31 ms at B=8. Do not "turn it on".docs/WHERE-THE-BOTTLENECK-IS.mdop table (4,312 programs, layout glue, CCL share). That profile ran the composed unfused GDN path, not this binary. Do not cite those shares as production.Open children
Filed separately. Each one names a falsifier. A boot happens only after that falsifier is a single change or a single measurement, and only one boot at a time.
Any new serving number must interleave against this control: control lands 47–52 ms, sha matches
bafed7a9…b2ea, both banners present (QWEN36_LM_HEAD_GATHER_MODE=none engaged, logits-rm engaged), MTP off. Quote ms/token.MemAvailable≥ ~4 GB before boot. Keep theprocess_output_decodeshard-concat fix (gather-none returns per-device vocab/2; reading device 0 and reshaping to 248320 is the 2026-09-23 warmup crash).Children, each with its own falsifier. Code drill only until a comment names one concrete measurement. One boot at a time. Do not restart serving.
Hypothesis A is already closed in
bench/runs/p150-decode-40-hypA-hostdev-20260923T1705Z.jsonl(host 3.266 ms,ttnn.__call__45.297 ms). Do not reopen it.Supersedes the "do not boot" line. Agents may use the cards. They coordinate with the existing host lock on cfx-llm2, not by waiting for the orchestrator.
mkdir /tmp/ttlockand write the owner name to/tmp/ttlock/owner. Release only when that file is still your owner (rmthe file,rmdirthe dir).trapmust release and exit on INT/TERM/HUP/PIPE. An EXIT-only trap continues after the signal and then starts tt-metal with no lock.docker psfor a foreignvllm-ttcontainer before boot.MemAvailable≥ ~4 GB or do not boot.docker stop -t 60(the serving unit's ExecStop). Neverdocker rm -fa live tt-metal container.mkdirfails, read the owner, leave the lock alone, and go do other work. Serving stays stopped: do not starttenstorrent-serving.service.hyp-54,hyp-55,hyp-56,hyp-57. The lock is the signal; a second agent does not boot over it.Hypothesis review, 2026-09-23. Ready list: (none).
prod-decode-profile.shonvllm-tt:k2-tracy,traced_prof, B=1/4k. Blocked by three things. (a) The loop's driver is fixed todecode-serving-ab.sh. (b) The profiler derivation is pinned to the 09-13 model.py. (c) The 48.619 ms overlay model.py (6d2e5746…, the shard-concat fix) is uncommitted. Details in #55.test_decoder.py use_prefetcherA/B.Why nothing is
ready: no hypothesis has a single diff or env var that can move 48.6 ms by more than 0.5 ms without changing output. Every remaining lever sits in image-baked files (mlp.py,gdn/tp.py,layer.py) or needs a new driver.Three hazards for the loop:
decode-serving-ab.shmounts onlytp_common.py,model_config.pyandmodel.py(plusqwen36_vllm.py/model_runner.pyif present). It does not mountattention/tp.py, although the serving unit does (modules/tenstorrent-serving.nix:626). So the 48.619 ms control ran the image'sattention/tp.py, and a hypothesis that edits that file would be silently not applied. Whether the image copy differs from the overlay is unverified.model.pyhunk. A loop that "reverts only its diff" withgit checkoutonmodel.pywould drop the shard-concat fix and reproduce the warmup crash.bench/runs/decode-phase2a-resident-20260922.jsonl("B=1 48.51 ms") is not a valid number. It used a 26-token prompt, its text is degenerate ("HT#3333…"), and its sha is not the baseline. Do not cite it for resident/argmax at B=1; that lever is host tail in any case, which is closed.Performance advisory (automated review, 2026-09-24)
The campaign discipline is strong (falsifiers, one variable per boot, sha-gated control). Three framing points should be settled before more card time goes into the ≤25 ms gate.
1. The gate's metric conflicts with the ranking rule in the ledger.
docs/DIRECTIVES-LEDGER.md(0aaaaaaaaaaaaaaa,0aaaaaaaaaaaaaaaaa) says to rank work by batch makespan / TTFT at B=4/8, and says "a decode-op win is not a win until converted into an E2E number." Single-stream ms/token is none of those. I could not find an owner directive that sets ≤25.0 ms. If one exists, link it here. If not, restate the gate as a makespan delta. Conversion from measured data:bench/runs/sweep-32k-20260914T142154Z.jsonl), so the step barely grows with B, and N ms saved at B=1 is roughly N ms saved at B=8.0aaaaaaaaaaaaaaaaaa, third-party: median completion 484→232 tokens) will shrink decode's share of wall further.2. MTP is back for this model (same ledger entry, 2026-09-24). The "no MTP" constraint here predates that decision. Per-step cost and speculation compound: a cheaper verify step raises the MTP ceiling, and third-party accept_len is 2.55/4. Before optimising the no-MTP step in isolation, coordinate with #63. If MTP lands, the step to optimise becomes the K+1 verify step, which has a different op mix.
3. Evidence hygiene:
bench/runs/decode-phase2a-resident-20260922.jsonl("B=1 48.51 ms"). Your own review found it invalid (26-token prompt, degenerate"HT#3333…"text, non-baseline sha). Please cross-post that correction on #52 so the claim does not propagate into the NixOS module rationale.bench/p150-decode-40/hypotheses.md. Consider closing both on GitHub as well.Where the remaining card time buys the most here:
TTNN_OP_PROFILER=1). See my comment there.