Skip to content

train 1114021234: #2885 #2722 #2961 (CI only) - #3290

Closed
ashhart wants to merge 31 commits into
mainfrom
lead/train-1114021234
Closed

ashhart wants to merge 31 commits into
mainfrom
lead/train-1114021234

Conversation

@ashhart

@ashhart ashhart commented Nov 14, 2026

Copy link
Copy Markdown
Owner

CI for a landing train: #2885 #2722 #2961 stacked on main in that order. Nothing merges from this PR: each PR above lands on its own once this tip is green, and this PR is then closed.

gilby and others added 30 commits November 14, 2026 01:49
…logprobs past ~100 tokens no longer hangs

Spread.post queued one ncclBroadcast per row per rank in one NCCL group; at TP=3 a
128-row prompt-logprob pass (381 broadcasts) never left ncclGroupEnd. Each rank's
columns are already one [rows][span] block: broadcast it once into a staging buffer
and place it into the full rows with one cuMemcpy2DAsync. Same bytes, world
broadcasts whatever the rows.

Fixes #1559
Prepare prompt scoring buffers before rank room is measured and pin one broadcast per rank at every gather height.

Refs #1559.
…dow_rows and the fake driver answers the integrated-GPU check

Main raised GLM's window_rows from 8 to 32, so the expected staging is vocab x 2 bytes x window_rows rather than the
8-row figure, and main's DeviceBuffer.download now asks cuda/download_stage.zig whether the GPU is integrated: the
test's fake driver answers cuCtxGetDevice and cuDeviceGetAttribute as a discrete GPU, so its downloads copy directly
as before. The PR's code is unchanged.
dots.mocr's image tower rotates its queries and keys by the patch's
row and column with FP32 cosines and sines, and adds the patch
embedding's bias after the projection has already rounded to BF16.
Two CUDA kernels now do this in Transformers' rounding: the 2-D
rotary in place over the q|k|v rows, and the patch bias as
bf16(y + b). Each has a planted control (BF16 cosines and sines; one
rounding of the FP32 sum) and a contract against an FP64 reference
at up to 19,520 rows and positions to 3,393, with every BF16 input
pattern and tie for the bias. Their launch records come from nvcc
13.0 images for the five SMs, which 13.2 and 13.3 compile the same.
The comment beside the dots_mocr entry in the CUDA kernel list named
only the text q|k|v bias and RoPE, though dots_mocr.cu now also
builds the vision tower's 2-D rotary and patch bias launches, so the
comment names all three.
The dense engine read text prompts only, so a recipe with an image
tower had nowhere to put its rows. A recipe may now declare a Tower,
checked at compile time against the contract in recipe.zig; each
lane owns a buffer of the request's image tokens, the first one at
open and the others at attach, and these bytes join every stream's
bytes and fixed part so the page planner shrinks an automatic count.
The lane encodes its images in order when it begins, with a cancel
check before each, and each prompt chunk copies the image rows that
it cuts over its embedding rows. Native(R) is now one shared core
with two variants: the text-only one keeps the old decl set
exactly, and the tower one adds the vision options, field and bytes
that the host reads. Host tests drive the tower variant with a fake
tower, also through the host's own offer and options code, and pin
that the image span refusal names --vision-image-tokens, the flag
the CLI accepts.
Only the words of DenseTowerWidth were tested, so dropping the width
check in openTower would pass the host suite. openTower is now
public, and a host test opens the fake tower against a decoder one
row wider and expects the refusal, closing the tower first if it
opens, then opens it at the decoder's width and without --vision.
No test drove a chunk that cuts an image through the backend, so a
chunk that passed no injects or a forward pass that skipped the
splice would stay green. The fake device now records its
device-to-device copies, and a host test runs two chunks cut inside
an image on a quiet recipe (no layer work): each must copy that
image's rows from the lane's feature rows to the chunk's embedding
rows, at the right row, offset and length.
The engine sizes each lane's feature rows by the request's image
tokens and copies image.tokens rows an image from them, so the Tower
contract now says that encode writes exactly image.tokens rows of
the tower's width at out.
Three trailing comments in images_test.zig ran past 120 columns;
each now sits on its own line above the code it describes.
A serve config file needs a reader, and Zig's standard library has
none for TOML. This one takes a strict subset of TOML 1.0, top-level
key = value lines with strings, decimal numbers, booleans and arrays
of them, and refuses the rest by line, so every file it accepts is
valid TOML with the same meaning and tables stay free for later use.
A launch of ten to twenty flags could live only in a shell script,
where vLLM and SGLang take a config file. serve --config FILE.toml
now turns the file's keys into flags ahead of the command line's,
so the one parser runs and the command line wins; each key is
checked against the flag table first, so an unknown key, a wrong
type or an unserved flag is refused with the file and its line.
Operators need the key rule, the precedence and its three
consequences before they move a launch into a file. This adds the
flag's row to the README table, a page with an example, the value
types, what the reader refuses and how errors name the file, and
the changelog line that also records the --compact-at fix.
The engine now lists --config in its capabilities, so the launcher
took it as an extra flag while it checks only the tokens it sees,
and a file could turn on --cortheon, a learning flag or a key that
Settings manages. The flag joins the managed set, with tests for
both spellings, and the config page states that an integer loses
its leading plus and that the launcher refuses the flag.
The maintainer accepted serve --config with four conditions, since
the file chooses what is served and can hold a key. A relative path
now starts at the file's folder, a file that other users can write
is refused before it is read, a pair of switches has one key
(thinking = false), and arrays hold strings only; the flag table
marks which flags name a path.
The maintainer's review of serve --config found that a refused
value named no line, that a command-line key left the file's key in
force, and that the file was checked by path and then read by path.
Each value now runs its flag's own check before the merge, a
repeatable flag on the command line replaces the file's list, the
file is opened once without following a link and read through that
handle, an api-key file must be unreadable by others, and a path
flag refuses a tilde.
…a_forward.zig has room for TF_GLM_DSEL

A move only: row_step, max_pieces, ranges and Pieces keep their text in
cuda_forward_pieces.zig, beside the pieced exchanges that use them, and
cuda_forward.zig re-exports them, so fw.Pieces, fw.ranges and the rest keep
working. cuda_forward.zig was at the 600-line limit; it is 545 now.
…GLM_DSEL)

MiaAI-Lab's patch 0067 (glm-dsa-decode-select-many-ctas, in
MiaAI-Lab/GLM-5.3-Flash-DGX-Sparks-1104 0049d7c, Apache-2.0), moved onto
main 8be9c40. For decode windows (rows <= Scratch.bits_rows, np >= 4096)
tf_glm_select_rows_wide (one 1,024-thread CTA a row) gives way to two
launches in glm_index_decode.cuh: tf_glm_dsel_scan marks each span's pools
above and at a sampled floor on many CTAs, and tf_glm_dsel_merge picks a
row's K pools from those words (the ties at the floor, the candidates
through tf_glm_select_rows' radix select, or the full-row select). Every
path returns tf_glm_select_rows' bytes. TF_GLM_DSEL=0 keeps today's select
(unset or 1: on; anything else is refused). Decode buffers' Scratch.ls
grows to hold the bit words (about 0.8 MiB a set at 864K); prefill buffers
keep bits_rows 0.

The patch was cut on Mia's stack: the context naming absorb_mma and
glm_glue_p2p/num (not on main) is left out, the rest is as written.
Checks: test-glm-index-host (the switch and the scan's grid) and
test-glm-index-emulate (the kernels as host C++, all four paths, against a
plain top-K); glm-index-check (GPU) compares the device paths.
…e select's comments are one line each

TF_GLM_DSEL is a glm row of platform/environment.zig (a flag: 0 or 1, default
on), in docs/environment.md and in cuda_env_test.zig's switches, so a malformed
value is refused by name with the other GLM settings when the model opens, and
cuda_sparse.zig's own parser goes. The decode select's comments in
glm_index_decode.cuh, cuda_sparse.zig, cuda_glue.zig and glm_index_check.zig are
one line each within 120 columns; the merge's paths are named at its branches.
lean_check.py finds nothing new against main.
… nvcc 13.0's and 13.3's records

contract_glm_dsel.zig registers tf_glm_dsel_scan and tf_glm_dsel_merge as a replacement of tf_glm_select_rows on
the wide select's scores, host rule and checks: each row's K pools come out of cuda_sparse.decodeSelect, their bytes
must equal the 128-thread kernel's on the same scores, then every row's picks the host's rule. It runs at the wide
select's three shapes, where cuda_sparse launches the decode select instead, and at a 1M window's bucket (8 rows of
260,096 pools), in the Index group beside the index scores and the wide select. Its planted control, the same
launches told past two spans that the bucket is one span (4,096 pools) short, goes red at the shapes gate. Gate 5's
records come from kernel-contract-limits over sm_80 to sm_121 fatbins of nvcc 13.0.88 and 13.3.73; 13.3 differs for
the scan on sm_120 and sm_121 (45 registers, 48 on 13.0). nvcc 13.2 was not at hand. The docs name both launches
and the scan in 13.3's set.
…st test, the CHANGELOG line, and one-line comments above

cuda_state.zig's selectLs gives a Buffers' selectTokens list bytes (`ls`) and
the rows of decode select bit words they hold (`bits_rows`), and make() takes
both from it. A host test checks, at a 4K and a 1M window, that a decode's ls
holds bitsBytes(bits_rows, np_max) with bits_rows min(seg, wide_rows), and that
a prompt's buffers keep bits_rows 0. The comments commit 3 had put at the ends
of long lines (cuda_glue.zig's Set, cuda_sparse.zig's decodeSelect path
argument, Scratch, dsel_target and dselScanCtas' 0, and glm_index_check.zig's
planted scores) are one-line comments above. CHANGELOG.md has an Unreleased
line for #2722 with the credit.
stage.zig stages device memory through tcp.zig behind the same Collective VTable (Kind.tcp). A call only queues on its
stream: the stream writes the call's ready word, then waits for its done word, 64-bit words in pinned host memory. A
proxy thread sees ready, copies the input into a pinned slab on a stream of its own, moves it with tcp.zig, copies the
result back and sets done. The caller's stream gets no copies of ours: with copies queued on it around the wait, two
ranks of one process hung or read stale bytes on ROCm 10.0. A call larger than Options.part_bytes (default 4 MiB,
checked alike when the ranks open) moves in parts, and the frame header's spare word now names the part, so one call in
two parts never pairs with two whole calls.
A failure sets every done word, so no stream waits on it, and shows at the next call or at check. A capturing stream is
refused, and allGatherV moves whole strides.

stage_hip.zig is the HIP device: hipStreamWriteValue64 and hipStreamWaitValue64 on mapped coherent host memory, refused
by name on a GPU without them, and the proxy's copies on a non-blocking stream of its own. stage_test.zig runs
host_test's contract bodies over a device whose copies are memcpy, whole and in 64-byte parts, plus part, size, capture
and device-failure refusals; stage_later_test.zig uses streams that run later, so calls return before anything moves
and an abort still lets a held stream drain; stage_hip_test.zig checks the same on a GPU, two ranks of one device over
loopback, and skips without HIP. `tf-tp-test tcp-hip` runs tcp-host's checks and timings from HIP memory. Nothing
selects it yet, and no other backend changes.
@ashhart

ashhart commented Nov 14, 2026

Copy link
Copy Markdown
Owner Author

Restacking onto main: this train's CI was cancelled by the old 60-minute job limit (raised to 90 by #3300), and main has moved since.

@ashhart ashhart closed this Nov 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants