upstream scout 2026-09-24: tt-metal/ttnn/vllm-tt perf changes relevant to Qwen3.8 on 2xP150 (top-5 + pin-bump hazards) #64
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#64
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?
This is desk research only. Nothing was run on cfx-llm2. Every number below is either THIRD-PARTY (from the PR itself, on other hardware) or ESTIMATE (my arithmetic). None of them is measured on our 2x P150a TP=2 box. Our own memory note says extrapolations like these have been wrong by 49-105%, so treat each one as a reason to measure, not as a result.
Scope: Qwen3.8-27B / ThinkingCap-27B-FP8 (48 GDN + 16 full-attention layers), 1x2 P150a mesh, TP=2, vLLM. The metric is batch makespan and TTFT at B=4/8 with <=32k in / <=1k out. Prefill is 74-79% of wall at those shapes (sweep-32k, 2026-09-14).
TL;DR: top 5 to act on (ranked by expected makespan/TTFT effect ÷ effort)
ttnn.experimental.all_gather_minimal_matmul_async(ourtp_common.all_gather_matmul_prefillcalls it), so this can be ported onto the bind-mountedpatches/prod-local-metal/tree with no image rebuild. It needs newout.colparcache tensors (partial COLD).in0_block_wmay span several activation shardsin0_block_w=1, per_core_N=10(bench/runs/n1n2-20260912/n2-raw.jsonl), i.e. about 272 serialized multicast hops. That puts it far below the PR's ~100-tiles-per-hop crossover, so hops are mostly exposed. Decode could drop 3-10 ms/token at B=8, which is makespan -1..-4% (more at 4k/8k)in0_block_w). Measure one down-proj shape with a microbenchmark before building anything. This is not the closed "grid tuning" line:QWEN36_1D_GRID_*moved core counts, while this changes the hop count, a knob that did not exist before 09-14.chunk_gated_delta_rulepatches/tt-metal/0004-chunk-gdn-phased-cb-alignmenttouches the samechunk_gdn_phased*files, so expect conflicts. The fused path's cost model is calibrated on an 11x10 grid (QB2), and P150 also appears to be 11x10 (#57507 counts 22 cores in 2 rows). Take #53790 now; wait for #57440 to merge.0020-qwen36-sdpa-streaming-kernel-for-chunked-prefill. Unverified for our shape (GQA 12q/2kv per device, d=256, paged chunked SDPA). Wait for merge, then A/B against 0020.113099f0(we carry96f3f041), plus the plugin spec-decode contract (vllm-tt-plugin#118/#128 merged; #125/#127/#130/#131 open) and the Gemma4 serving pattern #56048Two cheap items that can ride along with any rebuild:
to_layout(ROW_MAJOR) -> ttnn.argmax(dim=-1)). THIRD-PARTY: 1.64x median, measured on Wormhole. ESTIMATE: 0.3-1 ms/token, and it only applies if the op takes the multicore path.num_workers_per_dram_bank=2(#54242, already in the prod image; #55080 shows the recipe). This is a config-only A/B. The expected gain is low, because our readers already measure 454-469 GB/s (~90%) and that is the utilisation the PR reaches.If we bump the pin past 2026-09-22, we must also carry #57507 (open). #57053 changed the reduce-scatter worker heuristic.
qwen36 tp_common.matmul_reduce_scatter_prefillnow TT_FATALs on a P150 (36 workers chosen, only 22 cores below the(0,8)offset) at M >= 288 tokens per chunk. That is our exact prefill path.Our pins (as found)
vllm-tt:k2(forkdhavli/tt-metalcfx/qwen36-p300-tp2@3b31bad4, wheel0.79.0.dev20260903+cfx.g3b31bad4)d2f4b3af(2026-09-02) (merge-base via GitHub compare)patches/prod-local-metal/+patches/prod-local-plugin/tt-metal/tt-metal-full=/home/orchestra/ttm-build/src@76983d0060053f1f(2026-09-12) + #55548 @96f3f04124576047lazy FP8patches/tt-metal/0001-00230d22394cc127c17d(2026-08-14)model_runner.py84fbb665(2026-08-12)At the time of writing, main is 525 commits past
60053f1fand roughly 1,100 commits past the production base.Correction to
docs/UPSTREAM-WATCH-PLUGIN-METAL.md§1. That section says743890db(#55343, the prerequisite for plugin#126) is not an ancestor of our tree./home/orchestra/ttm-build/srcis a shallow clone (git rev-parse --is-shallow-repositoryreturnstrue), somerge-base --is-ancestorgives false negatives there. The GitHub compare API says743890db,722b1eb6(#55689) and172f60a8(#54992) are in60053f1f. They are genuinely absent from the 09-02 production base.Merged upstream items that matter
"Local" means the 09-12 Nix tree; "prod" means the 09-02 image base.
[6144,2560]bfp8 x48 ≈ +764 MiB/device (the reason we HELD #56759)in0_block_wfor DRAM-sharded matmulin0_block_win qwen36mlp.py/attention/gdn configssupports_async_decodeGenerator.decode_forwardno longer adopts stale device tokens when async is offexp_approx_mode=Falsepreviously still used the approximate expexp_approx_mode=Falsenum_devices-derived slice widthmodels/demos/blackhole/qwen36rms_allgather(Galaxy path)Open PRs to watch or cherry-pick
core_grid_offsetQWEN_GDN_PATHoverrideQWEN_GDN_TINVpinmcast && NV>=4to_layout+argmaxtail entirely; names qwen36 explicitlytext_demo.py -k accuracy_512, top-1/top-5 vs HF (98.63%/100% on P150x4)113099f0into the MTP treemax_model_len-sized ranges? The sweep holdsmax_model_len=34816 fixedvllm-tt-plugin (our pin
c127c17d, 2026-08-14)[B,1+K]block, #130 verify-then-propose, #131 on-device drafter. This is the vLLM side ThinkingCap MTP needs.model_runner.pywill conflict with #118/#119/#135 inscheduler.py/model_runner.py.tenstorrent/vllm and tt-inference-server
mainwas last pushed 2026-09-16. I found nodevbranch and no Qwen/TT activity. The plugin is the live integration point, so there is nothing to take here.impl=quetzalrow (#5147; a generated implementation with max_context 8192 and concurrency 1) plus the existingqwen36_blackholerow.--spec-decode-metrics-url(#4978) are useful when we instrument MTP acceptance.Recommended action
A selective path, not a wholesale pin bump. The production image is on a 09-02 base with grafted open PRs, and main has since landed a qwen36 MoE refactor, Metal 2.0 ports of the matmul/SDPA/paged-cache factories, and a P150-breaking RS heuristic change. A bump means rebasing all 23 metal patches plus 5 prod-local files, and it moves ttsim with it. Order:
patches/prod-local-metal/.../gdn/tp.py+tp_common.py, behindQWEN36_GDN_OUT_COLPAR(default on for max_model_len <= 34816, off for the parked long shapes). Build theout.colparcache once. Also port #56471 (warm boot). Audit #54565 and #55689 against our generator and 0018.in0_block_win {1, 2, 4, 8} on a #55444 build. Run thenum_workers_per_dram_bank=2A/B on the current image (config only). Promote #55444 only if the op time moves.113099f0. Use #53430 for variable-row KV commit, the #56048 adaptive block-output pattern, and the plugin spec contract (#118/#128, then #125-#131).Validation plan, for every adopted item, in this order:
test_gdn_tp.py(#57377 addstest_gdn_out_agmm_vs_mmrs+ skew determinism),test_chunk_gated_delta_rule.pywith the new HV=48/TP=2 geometry (#57368), and the SDPA prefill/decode unit tests at our shape (12q/2kv, d256, bf8 KV).bench/runs/batch-equiv-ref-*, bucketing off). #57377 and #57445 are not bit-exact: they are PCC-class numerics changes, so expect the hash to move. Use the accuracy gate (Gate Abench/kv-accuracy.pyat 16k/32k plus GSM8K / infbench stage 3) instead of demanding sha identity. #53790, #53627 and fused-vs-phased are claimed bit-exact, so require sha identity for those.QWEN_SDPA_BF8banner) so every record proves its arm.bench/ab/sweep-32k.sh, 4k/8k/16k/32k x B={4,8}, current optimized arm vs arm + lever, one source tree differing only by env,max_model_len34816. Rank by makespan, and report TTFT and decode ms/token alongside. Report PP tok/s and TG ms/token in every status (owner rule). For decode-side items (#55444, #53627), the B=4 control decode must stay faster than the B=8 baseline (existing tell).bench/ab/cache-speed-16k-32k.shfor prefill-side items, because prefix caching changes which prefill tokens are actually computed.bench/runs/*.jsonlwith the upstream PR number and commit in the config.Risks
bh_quietbox_2, or 4x p300c QB2). Only #53587 (stale) targets 1x2. TP=2 has twice the heads per chip (BH=24), so every kernel cost model (fused GDN geometry, SDPA chain gates) runs outside its calibrated points.rms_allgatheris ever used.git merge-baseon/home/orchestra/ttm-build/srcfor "is X in our tree" questions; use the GitHub compare API (see skillin-tree.sh).Sources
flake.nix/flake.lock,patches/{tt-metal,prod-local-metal,prod-local-plugin,vllm-tt-plugin,upstream}/,patches/PROD-SERVING-SOURCES.md,docs/UPSTREAM-WATCH-PLUGIN-METAL.md,docs/CXX-BUILD.md,docker/Dockerfile.vllm-tt-k2*,docs/DIRECTIVES-LEDGER.md,bench/runs/n1n2-20260912/n2-raw.jsonl,docs/TRACY-PREFILL-PROFILE.md.repos/tenstorrent/tt-metal/commits?sha=main&since=2026-08-28(1,104 commits, 326 after keyword filter), path-scoped commit lists formodels/demos/blackhole/qwen36,…/transformer/sdpa,…/operations/experimental; open-PR keyword search (228 PRs);compare/<c>...<base>for ancestry; PR bodies for every item above.agy research (2026-09-24)
Findings & Memory Accounting (#64 item 1, #41):
• PR #57377 ported: GDN prefill out-proj switched to column-parallel via
all_gather_minimal_matmul_asyncbehindQWEN36_GDN_OUTPROJ_COLPAR=1(default 0/OFF).• Memory cost: Shards [6144, 2560] bfp8_b per device across 48 layers = 764.99 MiB/device duplicate weights (decode retains row-sharded copy) + 50.33 MiB shared DRAM ping-pong buffer on
tt_ccl.• KV Budget Impact: At the standard <=32k benchmark (KV cache <1.1 GiB/device), +765 MiB is safe. On parked 128k/256k target shapes (16 GiB KV + 12.55 GiB weights + 1 GiB trace = ~29.5 / 30.8 GiB usable), +765 MiB consumes ~59% of the remaining ~1.3 GiB margin and competes with the prefix cache pool (1,017,275 tokens,
prefix-traced-dram-oom-20260918T1540Z.jsonl).• Local Alternative vs PR #57377: On our 1x2 P150a mesh,
_fuse_out_mmrs_prefillis ALREADY FALSE due to Patch 0007 deadlock guard (args.num_devices == 2 and 1 in _mesh_shape). Current baseline runs_row_proj(computing full 5120 cols locally, 2x matmul FLOPs of col-par) +tt_all_reduce. Column-parallel (PR #57377) directly yields fractured [1, T, 2560] residual tokens with zero post-projection CCL.• Recommendation: A/B PR #57377 (
QWEN36_GDN_OUTPROJ_COLPAR=1) FIRST on <=32k sweep where DRAM is ample; keep the zero-DRAM local reshape as fallback if 128k ladder hits DRAM exhaustion.What Changed (branch
agy/issue-64, commite288998):•
patches/prod-local-metal/models/demos/blackhole/qwen36/tt/gdn/tp.py: gatedtw["out_colpar"]\ transfer and AGMM out-proj behindQWEN36_GDN_OUTPROJ_COLPAR=1; added self-identifying bannerQWEN36_GDN_OUTPROJ_COLPAR engaged. •patches/prod-local-metal/models/demos/blackhole/qwen36/tt/tp_common.py: addedagmm_gather_bufferping-pong buffer and wiredpersistent_output_buffer. •modules/tenstorrent-serving.nix: addedgdnOutprojColparoption, assertion, and bind-mount forgdn/tp.py. •bench/ab/sweep-32k.sh: addedgdn-colpararm with fail-closed banner verification; auto-mountsgdn/tp.py. •patches/PROD-SERVING-SOURCES.md`: updated vendored files documentation.Hardware Measurement Next:
• Command:
PATCHED_DIR=patches/prod-local-metal/models/demos/blackhole/qwen36/tt ARMS="optimized gdn-colpar" OWNER=sweep32k bash bench/ab/sweep-32k.sh• Expected Result: THIRD-PARTY (PR #57377, TP=4 p300c): TTFT -9.0% at 32k, -10% at 16k. ESTIMATE for TP=2: prefill -4..-9%, makespan -3..-7% at B=8/32k.
• Go/No-go: Makespan & TTFT win vs
optimizedat B=4,8 across 16k/32k, greedy output equivalence preserved, DRAM headroom confirmed safe.Open Questions:
Cross-ref: #41
agy continuation (2026-09-25)
1. Simulation Verification (2-Chip Mesh, TP=2)
bench/probes/test_gdn_colpar_sim.pyvalidated on 2-chip simulated Blackhole (scripts/sim-run.sh --chips 2) at Qwen3.8-27B GDN shapes (dim=5120,value_dim=6144).MEASURED(bench/runs/gdn-colpar-sim-20260925T072336Z.jsonl):[1, 1, T, 2560]per-device tensors that assemble cleanly to[1, 1, T, 5120].2. DRAM & L1 Accounting for B=8, 32k Prefill
tw["out_colpar"]: adds +765.0 MiB/device (bfloat8_bacross 48 layers; duplicated because decode retains row-sharded weights).tpc.agmm_gather_bufferreserves 2 x [1, 1, 2048, 6144]bfloat16= +48.0 MiB/device in DRAM.l1_small_size=24576: Semaphores allocate on CCL reserved cores; AGMM streams tile blocks through standard Tensix CBs. Zero collisions observed or expected.3. Changes Pushed
agy/issue-64, commit65971a9:patches/prod-local-metal/.../gdn/tp.py: guarded import for delta rule decode packed op.flake.nix&scripts/sim-run.sh: unblocked local runtime (libexpat symbol fix & writableTT_METAL_HOMEcache).bench/probes/test_gdn_colpar_sim.py&bench/runs/gdn-colpar-sim-20260925T072336Z.jsonl: 2-chip simulation test & evidence record.4. Tightened Hardware Runbook & Go/No-Go
PATCHED_DIR=patches/prod-local-metal/models/demos/blackhole/qwen36/tt ARMS="optimized gdn-colpar" OWNER=sweep32k bash bench/ab/sweep-32k.shQWEN36_GDN_OUTPROJ_COLPAR engaged(fail-closed check built into sweep script).