Repository navigation
batch-mtp: work with a layer split, +15% two-stream decode, one group under --batch-groups auto - #1636
Closed
noon-at-cgn wants to merge 5 commits into
Closed
batch-mtp: work with a layer split, +15% two-stream decode, one group under --batch-groups auto#1636noon-at-cgn wants to merge 5 commits into
--batch-groups auto#1636noon-at-cgn wants to merge 5 commits into
Conversation
… GPU, grouped slot rows through every stage) - generate.cpp: --batch-mtp accepted with a layer split on separate GPUs (refused with --batch-groups); slot drafters load from the last stage's slot sessions under the last stage's device guard and bind to its weights/head; their residual buffers, row copy (async on the drafter's stream) and draft-KV copies run on the drafter's device; every stage verifier gets the 8-row window size and the batch graph limit; slot-drafter logits are not charged to CUDA0. - verify.cpp: stage_batch's layout rules move to include/strata/core/batch_rows.hpp (CPU test batch_rows_test); grouped rows are allowed on a stage that hands rows on, the hand-off bound is kVerifyMaxT rows. - docs/BATCHING.md, --batch-mtp help text.
…roup, said once) --batch-groups auto became the default for a layer split with --batch >= 2, but the --batch-mtp gate ran before the groups were resolved: it saw 1 group, kept batch-MTP on, and the groups were then raised to 2 - pipelined groups do not run the slots' MTP drafts (Niko1221#1413). The resolution is now a pure helper (include/strata/program/batch_groups.hpp, CPU test src/program/batch_groups_test.cpp): with --batch-mtp and no --batch-groups the default is one group and the log says why; --batch-groups N (N > 1) or --batch-groups auto given on the command line pipelines the groups and turns --batch-mtp off, as before.
…docs say how it meets --batch-groups The 0.1.41 default --batch-groups auto, the pipelined path's lack of slot drafts and the helper-cache refusal are written down in docs/BATCHING.md (the limits list and the --batch-mtp paragraph).
….41 build and the earlier 0.1.40.3 runs), with what was not measured
This was referenced Oct 9, 2026
batch: the adaptive expert tier keeps adapting in batch windows (
--adapt-async 1 beside slots)
#1637
Closed
noon-at-cgn
marked this pull request as ready for review
October 9, 2026 01:43
noon-at-cgn
added a commit
to noon-at-cgn/Strata
that referenced
this pull request
Oct 9, 2026
Niko1221#1637 and the four older ones), 0.1.41 batch-MTP, adaptive-tier and pool measurements
--batch-mtp on a layer split: two concurrent streams +15% aggregate on 2x RTX 3080 (89.2 against 77.3 tok/s), and with --batch-mtp the 0.1.41 --batch-groups auto default keeps one group--batch-groups auto
Owner
|
Ported (authors kept) as opt-in. Tested on 2x R9700 (IQ3_S, all experts in VRAM, --batch 4, 4 clients): the answers with --batch-mtp are byte-identical to the serial one-group windows and to the pipelined default (serve_load --compare, ALL IDENTICAL, both). Speed against the 0.1.41 default (pipelined groups), 5 interleaved pairs under the exclusive box lock: total 80.2 -> 62.7 tok/s (-21.9%, 0 of 5 pairs faster), latency p50 6.35 -> 7.99 s. So on this card pair the pipelined default is clearly better, the opposite of your 3080 result (your doc already says the pipelined groups can win; with all experts in VRAM they win here). It stays opt-in, and we will add a line to the docs that it pays when the experts do not all fit. |
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.
Title
Issue: Related: #1253 (ilumn, "Enable serial multi-GPU batch MTP": the same design, open, based on 82f46a8, includes #1242). #1413 (
--batch-groups G>1+--batch-mtpis silently inert). #1242.Summary
Why it matters: on a layer split
--batch-mtpis refused ("it is for one GPU (no layer split or helper) for now"), so two people using a two-GPU machine get one token per window each, while the flag gives +31% to +39% on one GPU (docs/BATCHING.md). This change lets--batch-mtprun on a layer split: each slot's drafter lives on the last stage's GPU (where the solo drafter and the head are), and a window carries two rows per slot (its token and its proposal) through every stage. On 0.1.41 it also needs one more fix: a layer split with--batch2 or more now pipelines the slots in groups by default (--batch-groups auto), pipelined groups run no slot drafts (#1413), and the--batch-mtpgate ran before that default was resolved, so--batch-mtpstayed on while the groups became 2 and the drafts never ran. With--batch-mtpand no--batch-groups,autonow resolves to one group and the log says why.--batch-groups N(N above 1) or--batch-groups autogiven on the command line is honoured and turns--batch-mtpoff, as upstream does.23,parallel 2,--spec 4, 2x RTX 3080 20 GB (220 W cap), Xeon E5-2696 v4, UD-Q4_K_XL--batch-mtp, one group (3 restarts, 60 measurements)--batch-mtp, one serial group (2 restarts, 40)--batch-mtp,--batch-groupsunset = the 0.1.41 default, two pipelined groups of one slot (1 restart, 20)--batch-mtp--batch-mtp(2 runs per arm)Where upstream's default is better: a new prompt of about 48k tokens arriving while one stream decodes was read at 2508 tok/s with the pipelined groups and 1217 tok/s in serial windows, and the decoding stream finished sooner (25.0 against 18.0 tok/s over its whole run).
--batch-mtpdoes not change that. So--batch-mtppays where concurrent decodes dominate; with long reads arriving beside decodes the 0.1.41 pipelined default can be the better choice. The first two 0.1.41 arms ran the asynchronous adaptive tier in batch windows, which is #1637 (the stack's next change; upstream main refuses it with batch slots); the pipelined path never adapts in windows (its tier runs in the solo path only), and that arm is our build started with the flags 0.1.41 resolves to, not an upstream binary.What changed
src/program/generate.cpp:--batch-mtpis accepted with a layer split whose stages are on separate GPUs (still refused when two stages share a GPU, with--batch-groupsabove 1, and, new, with helper-GPU expert caches or--remote-expert-opton a split, which have not been run). The slot drafters load from the last stage's slot sessions under that stage's device guard and bind to its weights and head; their residual buffers, the row copy (asynchronous, on the drafter's own stream) and the draft-K/V copies at admission and slot restore run on the drafter's device. Every stage's verifier gets the eight-row window size and the batch graph limit. The slot drafters' logits are not charged to CUDA0's cache sizing. The log says where the drafters are and what each costs (--batch-mtp: 2 slot drafters on CUDA1 (50 MiB of private K/V state and buffers each ...)); thestrata batch:summary says how many proposals were accepted.src/core/verify.cpp,include/strata/core/batch_rows.hpp(new): the row-layout rules ofstage_batchmove into a pure function; grouped slot rows are allowed on a stage that hands rows on, and the hand-off bound iskVerifyMaxTrows (it was the slot count, which a slot's two rows exceed). CPU testtests/core/batch_rows_test.cpp.include/strata/program/batch_groups.hpp(new, pure) andsrc/program/batch_groups_test.cpp(CPU): how many groups--batchruns with (the 0.1.41 default,auto, an explicit number) and the one case where the default gives way (--batch-mtp).generate.cppcalls it before the--batch-mtpgate, so the gate sees the resolved number. Log line:--batch-groups auto: 1 group of 2 slots - --batch-mtp does not run in pipelined groups; --batch-groups 2 would pipeline them and turn it off.docs/BATCHING.md: the layer-split paragraph, the--batch-groups autorow, the limits list, the measurements above.Extra Notes
#1253 (ilumn / Alex, open, conflicts with main, based on 82f46a8, includes #1242): the same design, so please read both. The same: slot drafters on the last stage's GPU bound to that stage's weights and head, serial windows (
--batch-groups 1), eight-row windows through every stage with a bounded batch graph cache,--batch-mtpstill refused when two stages share a GPU. What differs:OnDeviceguards in the destructors ofVerifierandMtpDrafterand inMtpDrafter::idle, and takes the last stage's bind bytes (the drafters' logits and residual rows,late_bind) out of that stage's expert-cache room. This change has neither (it relies on--vram-reserve-mib); I did not copy them, since they are theirs.--pipeline-windows, helper caches,--remote-expert-optand peer devices in the gate. Here--pipeline-windowsis already off with batch slots,--peer-devicecannot be combined with a layer split, and helper caches on a split are refused.batch_rows.hpp); in Enable serial multi-GPU batch MTP #1253 they stay inline instage_batch.tools/batch_mtp_split_test.py: staggered admission, solo promotion, seeded and greedy, mixed images) and measurements on 2x RTX 5090 with an IQ3_XXS model; this change has no GPU harness of its own, only the measurements above and a text-equality check (below).--batch-groups 1. Rebased on 0.1.41 its gate (o.batch_groups > 1, evaluated beforeautois resolved) would see 1 and keep batch MTP on while the groups become 2. The gate fix here is what makes--batch-mtpwork on 0.1.41 without that flag.They cannot both be applied as they are: they conflict in
generate.cpp(gate and drafter sites),verify.cpp(stage_batch) anddocs/BATCHING.md. If Enable serial multi-GPU batch MTP #1253 goes first, what stays here is the gate fix and its test,batch_rows.hppand its test, the accepted-proposals log line and the asynchronous row copy, and I will rebase onto it. If this goes first, Enable serial multi-GPU batch MTP #1253 still brings its harness, the destructor guards and thelate_bindaccounting. The gate fix needs neither. Enable serial multi-GPU batch MTP #1253's own table shows batch MTP slower than pipelined batching on 2x RTX 5090 (54.7 against 61.0 tok/s at 4000 experts per stage, 148.0 against 176.7 at 11000), and blange48's report on it found no gain on a 4-stage split (120/125/149/173/200 against 123/125/148/174/202 serial and 121/154/218/258/378 pipelined at 1/2/3/4/8 clients). Our two-stage result is for two stages and two slots. Related, not changed here: serve --batch: a layer split reads a prompt in STRATA_SPLIT_PIECE-token runs, decided at every boundary #1504 (long prompt reads on a layer split beside a decoding slot) concerns the read speed that the pipelined default wins in the table above.Text equality: with the same expert cache on both arms (the slot drafters take VRAM, 4213 slots on CUDA1 in both after adjusting the reserve),
--pcie-frac 0 --adapt-every 0 --suffix-draft 0, the greedy text with--batch-mtpequalled the text without it for 5 short prompts in all 6 comparisons (one restart per arm; the 25k-token prompt was not repeatable between two identical streams of the same arm). With different caches (4246 against 4213 slots) even the solo path differs on 5 of 6 prompts, so an unadjusted on/off comparison is confounded. This is text equality, not a bit-exactness proof, and says nothing about sampled output.Not tested: HIP, SYCL, three or more stages (Enable serial multi-GPU batch MTP #1253's report found no gain at four), more than two slots, sampled decoding, images with
--batch-mtpon a split (no per-slot image positions here: that is Isolate concurrent image positions and batch-MTP draft paths #1242), GPU parity of the split path beyond the text check above,--batch-mtpwith helper caches (refused), any upstream-main binary (all 0.1.41 numbers are from our build). The new gate and log lines were read in the start log of the production engine on strata-w10 (this change plus the rest of our stack): the base config and--batch-groups 2(which printed--batch-mtp is off: it does not combine with --batch-groups (pipelined slot groups) yet); there was no CPU-only start of the engine.Tests (CPU,
CUDA_VISIBLE_DEVICES=-1):batch_rows_testandbatch_groups_test(14 checks) pass;cteston the whole tree: 52 pass, 56 fail with no GPU (the same set on this branch and batch: the adaptive expert tier keeps adapting in batch windows (--adapt-async 1beside slots) #1637). On GPU 0 with about 1 GiB free:verify_parity,verify_batch_parity,rope_parity,qsa_parity,kv_stream_parity,kv_hybrid_paritypass;gdn_rec_paritystops withcudaMalloc: out of memory(another process holds that GPU's memory).python -m unittest discover -s serve: 593 tests OK (11 skipped).Context: this change is part of the setup measured in the community benchmark bench: community report, 2x RTX 3080 20 GB, UD-Q4_K_XL, 2 x 262K lanes in a 512K pool, 3.0K tok/s prompt, 86 tok/s decode #1640.