Repository navigation
Conversation
|
Updated the description: it now opens with the measured effect on my hardware, prompt and decode before and after against main, taken from the receipt below. No code change. |
|
Stacked with #1404, a paired step silently drops #1404's indexer split. Whichever of the two lands second needs the fix below; I'll add it to that one when it rebases.
The fix in my stacked build (main // cuda_engine.zig, Engine.prefillChunk
if (part.len > rows + 1) return prompt_pair.prefill(e, part, start, len, cancel, marks, split);
// cuda_prompt_pair.zig, prefill(..., split: bool)
fa.index_split = split; // the step's choice (TF_GLM_INDEX_SPLIT) for both chunks
fb.index_split = split;
// ... after pairBody
e.index_splits += @intFromBool(split);The twin buffers already share the first chunk's select scratch, whose Separately, and loud rather than silent: Stacked against main on three GB10 at TP=3, launched main, stacked, stacked, main;
The 128k gain is about this PR's and #1404's together (+7.6 to 8.0% and +4.6% in their receipts). The cold 136k prompt's |
|
Thanks, Bizuayeu. The pairing is carefully built:
On main
|
cf2d5d6 to
3a9caf1
Compare
Rebased onto #1404 and force-pushed; until #1404 lands its commits show here too. The four points:
On the head ( |
|
Thanks @Bizuayeu, all four points are in:
On main One small thing: lean reports one new finding, a multi-line comment at The order stays as agreed: #1404 lands first, then this one. On NVFP4 at TP=2 we'll run:
We'll settle the default from those. |
Folded into one line of 109 columns in 488074b. lean on the branch reports 433 problems, 434 before, none new. |
488074b to
ba004d8
Compare
|
Rebased onto main |
|
Hi @Bizuayeu, a status update on #1423. We read the full diff. The exactness design holds up: the twin keeps every field the side stream touches apart, What's still owed is the device run. It needs two GB10s at once (TP=2, and TP=3 if three are free). It compares Nothing is needed from you right now. |
Thank you for reading it through! I'll wait for the device run, and I'm glad to adjust the changelog line with you if it ships opt-in. |
ba004d8 to
92ada54
Compare
|
Rebased onto main |
|
A note for whichever of this and #1580 lands second: both take |
92ada54 to
3159c64
Compare
|
Rebased onto main |
|
|
|
Ran the pair with #1732's share on three GB10, which my rebase note left open. Replies match across paired, unpaired and poisoned launches and match #1732's own receipts, kept states land in the same places, and paired prompt steps keep their gain on top of the share (+8.1% to +10.0%). The numbers are in the description. |
|
Added the TP=2 run with #1732's share to the receipts. |
…s, each one's exchanges and glue under the other's sites
…and counts it once (ashhart#1404) Engine.prefillChunk reads and clears the split flag before it hands a paired step to prompt_pair.prefill, which never set it on its two forwards, so with ashhart#1404 a paired step ran the indexer whole on every rank. The step's flag now reaches both chunks: the header carries the one choice rank 0 makes at the step's start, and index_splits counts the step once. The twin buffers already share the first chunk's select scratch, whose pools hold ashhart#1404's world rows more.
…prompt pairs a step past the split's position The check called Leader.chunk without the pair argument, so compile-cuda-tests failed on the stacked tree. It now steps by Chunk.rows, pairs where it can and counts the splits a step at a time; pairing leaves the first four prompts fewer split steps, so a fifth prompt of three chunks and min_rows pairs its second step past TF_GLM_INDEX_SPLIT.
…t a time's do prompt_pair.prefill readies the index rings once, at the pair's start. Where a chunk at a time rebases them only at the second chunk (from chunk 1 of rings based at 0: after a resume from a state kept at an odd chunk, or once decoders finish mid-prompt), the pair rebased at the first: the tokens agree, but the rings' bytes and base do not. pairedAt now pairs only where both rebase at the first chunk or neither rebases, judging a resumed step by its kept state's base (restored after the choice), so one unpaired chunk realigns a prompt; prefill refuses any other pair on every rank. glm-pair-check adds a prompt of four chunks with a mark at chunk 1, counts the paired steps with the rings' base followed on the host, and compares both twins' bases.
At TP=1 a pair has no exchanges to hide and no receipt, yet the twin was allocated and the side stream ran at top priority. prompt_pair.wanted(on, world) is now on and world > 1, for both; pairedAt already pairs only with a twin. glm-pair-check needs other ranks.
… size added or taken away stops the build twin_sizes tied the fields a twin holds apart to make's size indexes by hand, beside a list of their names. twin_at now maps each field to its index: make reads those fields from it, the twin's allocation and copy take it too, and make's sizes are an array of twin_checked entries, so a future size entry fails the build until the indexes are checked again.
…nd Cancel with the engine's types, so the engine and the lanes keep room under the line limit
…er holds no twin A solo pass and a paired step both reach the other ranks as a part of two chunks, so one server cannot mix them. With the wider buffers set, the pair stands aside and makes no twin.
115bc5b to
9017024
Compare
|
Rebased onto main with #1823 in: this head ( |
What this changes
A prompt step now takes two chunks while no other stream decodes, so a cold prompt gets faster; decode and the output
bytes do not change. On my hardware (GLM-5.3-Flash XSFP4,
--parallel 4, the mean coldttft_sof six requests):1b4f2f979→ this headfibonacci-rawIt holds about 250 MiB more a rank at TP=3 and 210 MiB at TP=2 (the second chunk's rows). Details in the receipt.
A GLM prompt chunk already exchanges its partials in row pieces on a second stream (
TF_GLM_OVERLAP_PIECES), but ateach site the main stream still waits for the last pieces' exchange and glue before the next site starts. On three
GB10 at TP=3 that wait was about 10% of a 32k or a 128k prompt's time (the engine's prompt profile, its
wait_*spans), and 5% at TP=2.
With
TF_GLM_PROMPT_PAIR=1(the default) at TP=2 or more, rank 0 has a prompt step take the next chunk too when thatchunk holds 512 rows or more, no kept state starts at it, no other stream is decoding, and the index rings rebase
where a chunk at a time's would (below). The two chunks run in one pass, site
by site: the first chunk's mixer, its fills, then on the side stream its exchange, glue and sharing, while the main
stream goes on to the second chunk's mixer; each chunk waits for its own glued rows only before its next site. The
other ranks take the paired step from the ids rank 0 sends (they tell it by the part's length; nothing on the wire
changes).
TENSORFOLD_PREFILL_SHAREof 1 the rounds of the streams decoding beside a long prompt wait for each step. Pairingbeside them nearly halved their rounds (three decoders beside a 32k prompt kept 0.50 tok/s against 0.86 unpaired at
TP=3, 0.41 against 0.72 at TP=2). With a stream decoding, the lanes pass
pair = falsetoLeader.chunkand thestep is main's one chunk; a prompt alone, or several prompts filling with nothing decoding, pair.
at a layer follows the first's on the main stream, so the first chunk's caches, KDA state and conv rows of that
layer are in.
tf-cuda-test glm-pair-checkcompares paired prompts with twins a chunk a step: the first token, thenext window's logits and tokens, and every byte of the state, which holds the caches.
prompt_pair.prefillreadies the index rings (4,096 rows) once for both chunks, so a steppairs only where a chunk at a time rebases them at the same point, at the first chunk or not at all (
ringsAgree).From chunk 1 of rings based at 0 (after a resume from a state kept at an odd chunk, or once decoders finish
mid-prompt) that step goes alone and the next one pairs. A resumed step is judged by its kept state's base, which
rank 0 holds on the host, since the state comes back after the choice;
prefillrefuses any other pair on every rank.and logits) in
Buffers.initTwin, sharing the first chunk's scratch: about 250 MiB a rank at TP=3 (6,193 KV pagesagainst 6,232) and 210 MiB at TP=2 (1,807 against 1,841). Its pieces share the first chunk's side stream, so every
rank posts both chunks' exchanges in one order.
ahead of the other chunk's grids (unpaired steps such as a prompt's tail then use it too). At TP=1 a pair has no
exchanges to hide: the engine makes no twin and the side stream keeps its normal priority (
prompt_pair.wanted).TF_GLM_PROMPT_PAIR=0is main's path, priority included, and every rank must agree on the setting(
Fingerprint.pair, layout 15).under
TF_GLM_PROF. With the prompt cache on, a step is not paired when a kept state starts at its next chunk; thecache keeps a few states a prompt on the 2,048-row grid (the history's end, a rewind point, shared blocks), so most
steps pair. A paired step covers 4,096 rows: the heat bands and the cancel are checked once a step.
TF_GLM_CHUNK_SOLO=4096(glm cuda: TF_GLM_CHUNK_SOLO takes two grid chunks in one pass while no other stream decodes #1823) takes the pair's place, as its description says: a solo pass and a paired step bothreach the other ranks as a part of two chunks, so one server cannot mix them. With it set,
prompt_pair.wantedmakes notwin.
Leader.chunkasksprompt_solo.atorprompt_pair.pairedAt, and on any one server only one of them can say yes.prompt_solo.othersDecode, socuda_lanes.zigdoesn't change. For the linelimit beside glm cuda: TF_GLM_L2PF reads a decode window's next weights into L2 on a side stream, on by default on a GB10 #1580, the engine leaves the choice of a forward's pieces to
prompt_pair.piecesOf,cuda_follow.zigcalls
prompt_pair.pairedAtitself, andCancelsits incuda_engine_types.zig. The ranks' tests moved in glm cuda: a prompt chunk's indexer rows split across the ranks, their picks all-gathered #1404.counts it once. b's indexer gather runs on the main stream while a's exchanges may still be in flight on the side
stream, on the same communicator. That is safe because NCCL runs a communicator's launches in the order the host
issues them, whatever their streams (in 2.30,
ncclLaunchPreparehas each launch's stream wait on thecommunicator's device stream, and
ncclLaunchFinishmoves that stream on to the launch), and every rank issues bothin one order.
Related: #1327 caps a filling prompt's steps while replies decode; this change pairs only while none do, so the two
never act on the same step, and the lanes advance by the rows
Leader.chunkran. #1352's wait at the end of a stepit bills to decoders (
settle_step, p < 1 with a stream decoding) never meets a paired step for the same reason.Receipt
Environment: TensorFold
cf2d5d685(tree as measured) against main1b4f2f979; three GB10 (sm_121, 128 GB unifiedmemory) at TP=3 over a switchless RoCE ring, and two at TP=2; driver 580.178.04; nvcc 13.3.1; GLM-5.3-Flash XSFP4
(
Bizuayeu/GLM-5.3-Flash-XSFP4at88736f9). Each served with--parallel 4 --prompt-cache-gib 0. TP=3 launched,in order, this head, its earlier form (pairing beside decoders too), this head with
TF_GLM_PROMPT_PAIR=0, theshare twice, main, then the second of each; TP=2 main, paired, unpaired twice, paired, main, then the share. The
head (
3a9caf188) rebases onto glm cuda: a prompt chunk's indexer rows split across the ranks, their picks all-gathered #1404 and adds the review's fixes, which change no paired step of these runs(prompts from position 0, no kept states, TP=2 and 3); the stacked run with glm cuda: a prompt chunk's indexer rows split across the ranks, their picks all-gathered #1404 and glm cuda: a decode step's select gives each row a 1,024-thread block #1424 is in
glm cuda: a prompt step takes two chunks while no other stream decodes, each one's exchanges under the other's sites #1423 (comment). This head (
90170240d) is those commitsrebased onto main
7651ce64f, where glm cuda: a prompt chunk's indexer rows split across the ranks, their picks all-gathered #1404, lane host: a long prompt's step owed to decoders ends on its device #1352, lanes: --decode-share sizes a filling prompt's chunks beside live replies (#840) #1327, glm cuda: at three ranks or more, TF_GLM_ROCE_MULTI=1 sends the small all-gathers over fabric/tp's multirank RoCE #1524, glm cuda: TF_GLM_KV_SHARE keeps prompts as the pool's pages in prompt_radix's store #1732 and glm cuda: TF_GLM_CHUNK_SOLO takes two grid chunks in one pass while no other stream decodes #1823 have landed, a commit that moves linesand changes no behaviour, and a last one that gives way to
TF_GLM_CHUNK_SOLO. Besides the base,cuda_lanes.zigkeepsmain's lines,
prefillChunktakes the pair's path only where the engine has its twin, andFingerprintholdspairbeside
soloand glm cuda: at three ranks or more, TF_GLM_ROCE_MULTI=1 sends the small all-gathers over fabric/tp's multirank RoCE #1524'sroce_multias layout 15, so withTF_GLM_CHUNK_SOLOunset the GPU receipts below,from
cf2d5d685and3a9caf188, were not taken again. They predate glm cuda: TF_GLM_KV_SHARE keeps prompts as the pool's pages in prompt_radix's store #1732; the run withTF_GLM_KV_SHAREis below.Exactness on the head at TP=3 and TP=2:
glm-pair-checkpasses at both ("6 prompts, one with a mark at chunk 1, equal their twins achunk a step in the first token, the next window's logits and tokens, the rings' bases and every state byte"); with
the earlier choice, which pairs from chunk 1, the marked prompt fails at TP=3 ("the paired state differs (pos 8192/8192,
rings' base 8192/6144, …)").
glm-split-checkpasses at both too ("prompts of { 6444, 4098, 4101, 4096, 6656 } rows (6paired steps) equal their whole twins …"). No GPU case resumes into a pair; a resumed step rests on the rule above
and its kept state's base. Also at
cf2d5d685:glm-pair-checkpassed at TP=3 and TP=2 on its five prompts;glm-lanes-concurrenton decode-check prompts joined two by two passed at both (prompts filling together, and stepsbeside decoding streams);
glm-lanes-cover(its 4,388-token copy prompt pairs: copy drafts, cancels mid-prompt,kept states at marks, resumes) passed at TP=2. At TP=3 its "copy drafts, sampled: the proposer judged 0 copied rows"
failed on main's runner too, with the same lines before and after it: the sampled reply there does not repeat W,
which that line assumes (at TP=2 it judges 188 rows).
bench_concurrent.py --alone --serialat levels 1and 4: every cell equal on every build.
token_sha256is the same over every launch of each window (main, unpaired,paired, and at TP=3 the earlier form), for both decode prompts and all three phases of the 8k, 32k and 128k prompts.
Prompt speed before and after: the cold
ttft_sfrombench_cache.py --require-cold --streams 1(first, repeatedand concurrent are all cold at
--prompt-cache-gib 0), the mean of six requests, in seconds:Every paired request beat every unpaired one at every size on both TPs. At TP=3 the earlier form (pairing beside
decoders too) took 4.628, 18.353 and 77.832 s in the same chain: waiting for decoders costs a prompt alone nothing.
The share at p = 1 (three decoders beside a 32k prompt,
v13_client.py mix): at TP=3 this head kept 0.88 tok/spaired and 0.82 unpaired during the prompt (19.3 and 19.6 s); at TP=2 0.72 and 0.72 (23.5 and 23.7 s).
Decode speed before and after (
bench_openai.py --tokens 256 --reps 3 --temperatures 0, medians,fibonacci-raw):at TP=3 main 57.65 and 57.64 tok/s, paired 57.74 and 57.65, unpaired 57.55 and 57.55; at TP=2 main 44.47 and
44.44, paired 44.46 and 44.48, unpaired 44.34 and 44.33 (decode windows take no pieces).
With glm cuda: TF_GLM_KV_SHARE keeps prompts as the pool's pages in prompt_radix's store #1732's share, on
3159c6492merged with main6d3fa0161(the head's base predatesfbbc20d0f's fix tocontract_controls.cu, which the GPU runner needs): TP=3,PARALLEL=4,TF_KV_PAGES=on,TF_GLM_KV_SHARE=16,launched paired, unpaired twice, paired, then paired under
TF_KV_POISON=1. The 175 replies at temperature 0 (45resumed) give one reply per prompt across the five launches and equal glm cuda: TF_GLM_KV_SHARE keeps prompts as the pool's pages in prompt_radix's store #1732's own receipts on the same seven prompts;
bench_concurrentis 50 of 50 against alone and 20 of 20 against serial, and kept states land at the samecachedinevery launch (63,488 on a 64,030-token prompt, 126,976 on a 128,030-token one). Prompt time, unpaired then paired: 64K
cold 35.12 → 32.11 s (+9.4%), 128K resumed at 63,488 36.28 → 32.99 s (+10.0%), 136K cold 83.56 → 77.33 s (+8.1%).
glm-pair-check,glm-split-check,glm-paged-checkandglm-lanes-concurrentpass on all three ranks;glm-lanes-coverfails only "copy drafts, sampled", as main's runner does at TP=3. The twin adds 0.25 GiB (63.85against 63.60), and
MemAvailableafter load on rank 0 was 8.9 to 9.4 GiB paired, 9.4 to 9.9 unpaired.The same at TP=2, on this head merged with main
b17fb507a, two GB10 joined by one QSFP cable, same launches. The175 replies at temperature 0 (45 resumed) give one reply per prompt across the five launches and equal glm cuda: TF_GLM_KV_SHARE keeps prompts as the pool's pages in prompt_radix's store #1732's own
TP=2 receipts on the two prompts they share;
bench_concurrentis 50 of 50 against alone and 20 of 20 againstserial, and kept states land at the same
cachedas at TP=3 in every launch. Prompt time, unpaired then paired: 64Kcold 41.69 → 40.10 s, 128K from 63,488 43.32 → 41.69 s, 136K cold 102.41 → 99.42 s. The paired launch under
TF_KV_POISON=1served to its end at the paired times.glm-pair-check,glm-split-check,glm-paged-check,glm-lanes-concurrentandglm-lanes-coverpass on both ranks, and no launch waited on the heat guard.Tests run on this head (Linux x86_64):
zig build test456/456 steps (1,894 of 1,913 tests, 19 skipped) andzig build test-cuda-host276/276 steps (4,240 of 4,250 tests, 10 skipped);zig build compile-config-server7/7,zig build compile-cuda-tests10/10 (alsowith
-Dtarget=aarch64-linux-gnu),test-kv-pool33/33, the GLM host steps (test-glm-prompt-settings-host6/6,test-glm-tp3-host8/8,test-glm-pages-host,test-glm-fp8-kv-host,test-glm-prompts-host7/7),zig fmt --check, the lean check (424 problems againstmain's 424, none new), and it merges clean with main
1ddac9620. The host cases:ringsAgreepairs from chunk 2 of rings basedat 0 and not from chunk 1;
wantedmakes no twin at TP=1 or beside solo passes.GPU checks on this head, on two and on three GB10: with
TF_GLM_CHUNK_SOLOunset,glm-pair-check,glm-split-checkandglm-lanes-concurrentpassat both, and
glm-lanes-coverpasses at TP=2 and fails only "copy drafts, sampled" at TP=3, as main's runner does. With=4096,glm-solo-checkandglm-lanes-concurrentpass at both.Not run, and why: Metal and HIP, which this path does not reach; a DFlash2 drafter (not paired).
Checklist
step is one lane step of two chunks.)
"draft": false, a resumed prompt equals a fresh one, and concurrent equals solo.(Concurrent equals solo and alone in the receipt;
glm-pair-checkcompares the state's bytes, and the cover'sresumes passed at
cf2d5d685.)changes;
glm-pair-checkcompares the bytes.)SUMMARYline is in the receipt. (No kernel changes.)1b4f2f979: faster at every size on bothTPs.)
read
prompt_pair'stakesTwo,ringsAgreeandwanted, andSettings.pair, which main does not have;glm-pair-checkneeds a GPU and a checkpoint, and its prompt with a mark at chunk 1 fails with the earlier choice.)