Skip to content

train 1112142508: #2851 #2765 #2883 #2889 #2891 #2892 #2878 (CI only) - #3024

Closed
ashhart wants to merge 57 commits into
mainfrom
lead/train-1112142508
Closed

ashhart wants to merge 57 commits into
mainfrom
lead/train-1112142508

Conversation

@ashhart

@ashhart ashhart commented Nov 12, 2026

Copy link
Copy Markdown
Owner

CI for a landing train: #2851 #2765 #2883 #2889 #2891 #2892 #2878 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.

ashhart and others added 30 commits November 12, 2026 14:11
…client write can't hang

images_generations_test's serve() returned without closing route_fd when the
route finished before reading the whole request body. The client writes the
full body before reading, so once the socket buffer filled its write blocked
for ever. Linux's Unix socket buffer holds the bodies these tests send, so CI
never saw it; on macOS test-cuda-host hung there for 20 minutes holding a build
lock (twice on one tree).

- serve() closes route_fd on every exit (defer), as the server does with a
  finished connection; deinit no longer closes it.
- The client end gets a send timeout (10 s, far past the route's 2 s read
  timeout) and no SIGPIPE (SO_NOSIGPIPE where it exists, MSG_NOSIGNAL on send),
  so a closed route is an error, never a hang or a signal.
- open() and ask() release the server and join the route on every error path.
- New test: a request line the route refuses from the head alone, with a 4 MiB
  body, ends the client's write at once (well under the send timeout).

test-cuda-host on macOS: 385/387 steps, 4414/4451 tests, the images tests and
the new one passing; the one failure is benchmark_cli_test's attached run,
which this change does not touch.
test-gradient-serving (the serving kernels' and launch wrappers' source
hashes) and test-qwen-serving-host (the dense serving launch sequence) ran
only in the macOS nightly. A PR that changed a pinned file therefore landed
green and turned the next nightly red: 10, 11 and 12 Nov, and again now,
because #2843 (3768bc0) changed zig/src/cuda/experts.zig.

- no-gpu.yml runs both steps on every PR, after the goldens, so a change to a
  pinned file needs its re-pin in the same PR.
- gradient-serving.tsv re-pins experts.zig with #2843 named beside it: the
  grouped EXL3 experts are launched and sized at the caller's tile and pair
  count; no kernel source changed.

test-gradient-serving: 1/1 pass.
…ges, and only EINVAL falls back to WILLNEED for good
…stays on the caller, tested by the pool's call count
The test stopped the run before it started and then expected the fake
server to see no request at all. The manifest and thinking fetches race
the first stop poll, so on a busy runner one or two requests landed and
the test failed. It now checks what cancellation guarantees: exit 130,
no POST to /submissions, no upload preview, and one sample saved with
error_code "cancelled". test-benchmark-cancel-host runs it alone.

Fixes #2853.
The HIP drafter implements core/lanes Drafter for chains: absorb gathers
a stream's tap rows into the drafter's KV ring, hold runs the five-layer
frame over a block and draws a chain with the target's head. Per-stream
rings and segments, frames batched over up to 16 streams.

- host tests for chain selection and position staging
- tf-qwen35-test drafter: determinism, refusals, and acceptance against
  the plain target's greedy continuation (fails below 2.0 a round)
A frame's attention reads its own block's keys from the frame, never from
the ring: the ring holds absorbed rows only, so no key of a row's window is
overwritten before its attention. The ring is still sized at the window
and a padded block (config.ringRows), and a host test now pins that.

Frames pass no mask, so every row sees its whole block and the merge never
sums zero keys. Trees stay refused until their mask keeps each row's own key.
The HIP drafter's hold now picks its chain with core/lanes/draft_tree.zig's
walker instead of a copy of its chain, choice and drawn. The core
Candidates take the rows apart (ids, f32 logits and f32 selector
projections), as HIP's frame downloads them; CUDA's words keep their path
and arithmetic. Host tests pin HIP chains on fixed rows, greedy and sampled,
and an f32 projection between bf16 steps, before and after the switch.

The acceptance prior by depth is one table, draft_prior.zig, that the CUDA
and HIP drafters both read.
absorbFn took its first row's position from the features' offset and
ignored Absorb.start. It now absorbs from start, and refuses features whose
offset (where hip_lanes.zig's tap ring names the first row's position)
names another one, with a host test.

most_held's comment now says what its 15 is: a block of 16, the ring's
padded block, but its pending row.
…uns a seeded sampled stream

The acceptance floor of `tf-qwen35-test drafter` moves from 2.0 to 4.5 tokens a
round, between a drafter whose attention reads its key ring one slot behind and
a clean one, so that planted fault now fails the check.

A seeded sampled stream (seed 7, temperature 1) follows: the target draws each
prompt's continuation by the rule, the drafter's holds carry the same rule, each
prompt's rounds run twice and must draft the same chains, and the line prints
the acceptance and a hash of the chains.
… either order

Prompts 0 and 1 run together in frames of two, in both orders, and each
stream's chains must not depend on its slot. The line prints each stream's
acceptance beside its one-stream run.
… has followers for

When a stream's last round accepts every draft and the continuation ends
right after them, its bonus token has no follower, so the absorb now
declares as many rows as it passes.
prefillFn's dangling else ran a text stream's fill zero times when the
stream had no readout, so a startup probe of --prompt-cache-disk or
--learn-conversations failed with ProbeUnmarked. prefill.whole now runs
a readout's pieces to the end in its own block and any other stream's
fill once. The opening restore goes through #2802's restoreKept; when it
fails, the cache reset stays fatal as on main, and only then is the
restore marked failed. test-qwen35-prefill-host runs the stepped, whole
and opening tests without devices.
…e read row's 64-row tile (#2849)

tailPlan (xpu/prompt_tail.zig) asks core.prompt_tail whether a chunk's last layer is read: an unread chunk runs add_rmsnorm, mixer_in and attn_prep (its keys and values) on every row and stops; a read chunk runs attention, the gate, mixer_out, the post norm, the MLP and the final norm on the rows from ((T-1)/64)*64, and the head on the last row. Dump, probe and fidelity hooks, the A8 study and taps on the last layer keep the whole layer. tail_test compares the last row's logits and the keys with the tail on and off at ten lengths.
Skip the bf16 tail comparison when int8 KV layout and scales apply.
Keep decode attention launch arguments unchanged and name the tail test.

Refs #2849
…pth, not fifteen a round

A confidence floor makes a greedy stream draft the most and leaves the cut to the head's chances. While GLM's
chains stopped at three that drafted at most two steps past the cut; since #2257 they reach fifteen, so each greedy
round under --mtp-confidence runs fifteen head steps and verifies only the prefix the floor keeps.

Model.floor_rates keeps a greedy stream's rates' depth under a floor, as a sampled stream's already is, and the floor
still cuts what the window verifies. GLM sets it for the MTP head; DFlash2's blocks and the other families keep the
rule they had. GLM's facts drop `.gpu_tokens = false`, Model's default, so cuda_lanes.zig keeps to 600 lines with
main's changes.
ashhart and others added 27 commits November 12, 2026 14:12
Plan all nine slots with E+1 expert ids and skip the shared id
before prompt payload reads. Write routed FP32 rows in place,
without compact-pick or output copies, in calls up to 4096 rows.
Decode keeps its 1024-row compact path. Preserve its prompt
variant as a device-test reference and remove staged-output
storage from normal prompt scratch.

Add real-driver host recording and large byte/sentinel tests,
retaining the independent FP64 checks. Mark the two changed
kernel resource records pending until this source is measured.

Refs #2819
Remove superseded resource tuples, name per-rank scratch widths
and make the skip control retain a guard after a member load.
Keep the compact prompt byte reference in the CUDA test tree;
production retains the compact slot fallback.

Refs #2819
Use the accepted session transport and TF_COMM_BACKEND provenance
in one startup logger shared by the server and follower. Explain
when TF_ROCE is ignored. Exercise the source mapping and follower
startup ordering without changing inference or transport choices.

Refs #2715
Give tf_lane the reg path's 16-row grid axis and runtime sums
stride. Each block keeps the same group products, FMAs and K
reduction; cooperative and older-chip encodes retain 16 rows.
Qwen27 selects a whole-prompt key for non-reg widths behind
the existing Ultra gate. Decode and mixed stacks stay short.

Compare each width with main's frozen short kernel, including
ragged tails, producer sums, strided outputs and three controls.
Pin real quant dispatch counts with main-red host regressions.

Refs #2818
The full lane contract compiles all four tf_lane_sums sources
for M1 and M2 at a 1024-thread pipeline limit. Record those
measured values and keep the actual 256-thread launch.
Tensor-op sources remain explicitly unmeasured on these chips.
Pin the distinction in a host regression; no shader changes.

Refs #2818
Only whole-prompt layouts compile the independent row-block
header. Short layouts retain a scalar group coordinate and
their original row extent; guarded sums remain bounds-safe.
Keep older-chip sources on that short variant as well.
Compare both short and long programs against frozen main
at every maintained byte-check shape and row count.

Refs #2818
Leave other affine lane callers on their scalar 16-row source
and capacity. Only the Qwen prompt key and byte fixture request
independent row blocks. Pin the shared shader bytes, describe
the row layout accurately and cover the 16/17-row boundary.

Refs #2818
…, read at GLM's open

GLM-5.3-Flash's attention keeps no rotary positions, so only its trained length bounds the window.
TF_GLM_CONTEXT_CEILING, a .glm row, sets the window --context may ask for, up to 2^31 - 32, where the slots keep
i32 positions. GLM reads it at its open once its .glm rows are checked; unset, the ceiling and the refusal are main's.
A window past the trained length says so at startup.
…a_env.zig

cuda_native.zig had passed the 600-line limit. ceiling_switch and raisedCeiling move to cuda_env.zig, which owns the
.glm check, and the i32 test to cuda_env_test.zig; cuda_native.zig keeps the two names the CUDA open looks up.
…est file and cuda_window.zig

Main grew cuda.zig to 587 lines, so the branch's additions passed the 600-line limit again. The open-time test of
raised context ceilings moves to cuda_ceiling_test.zig, imported beside cuda_health_test.zig, and the lookup of a
family's raised ceiling and its switch to cuda_window.zig's familyCeiling; cuda.zig keeps one call.
…t.zig

Main's eb29a68 added a line to cuda_native.zig, so merged with main the file comes to 601 lines, past the 600-line
limit. The test that capacity keeps a verify window past the context, a multiple of 64, moves whole to
cuda_pages_test.zig, which tests GLM's pages at that capacity: 594 lines in cuda_native.zig here, 595 merged with main.
A fraction after --compact-at auto left auto mode on, because the
flag set compact_auto and a later fraction never cleared it, so the
server kept the first value where every other flag keeps the last.
Each occurrence now replaces both fields, which a config file's
value under a command-line override depends on, and a filtered host
step runs the test that fails without the change.
Pin eviction of redundant own history before another conversation's
newest state under pressure. Retain prompt-born provenance and prove
a same-prompt regenerate restores history when all three states fit.

Refs #2881
Give test-native a timed shard with the Metal toolchain. Keep it
nightly until the CI runner establishes its wall time.

Refs #2880
…xperts, Drama 3 and Kolibri-1 read through it
… of few rows

On gfx1151 a routed prompt holds about 80 experts of 8 rows or fewer at any
length. On the 64-row matrix tile each of their blocks reads 128 columns of
codes a stage, a page of weights every four columns, and the cost of those
reads follows the pages a block touches.

wmma_item_tile now runs gemm_kp_wave for an item of at most the launch's cut:
a lane a column's group, a column's whole row of codes read at once, no
barrier. gemm_kp_wave is a wave's passes of gemm_kp_tile, factored out so
both tiles run them; the K-parallel tiles keep their statement order and
their register and scratch use. A pass of the item tile takes 2 rows and
2 columns a lane, which stays in registers, and keeps its partials in the
block's code stages, so the tile's LDS is unchanged.

The cut is a card's: `few_rows` in the tuning row (8 on gfx1151's item64
row, none elsewhere), carried to the kernel in Routing.few by prefillLaunch.

A kernel test runs one plan through both paths at every width and compares
bytes; the contract runs the tile with the cut and a control whose passes
stop after a wave's first. The tile's record holds ROCm 10.1's hipcc. The
build now tracks tiles/matrix_item.hpp.

Refs #2795
…ks bounds (#2878 review)

The review's asks on the tile's cut for items of few rows:

- registry: `rowOf` returns null for an entry before the registry's own
  instead of subtracting first, which trapped. A toy-registry test covers
  a named entry, an unnamed one and entries before and after it.
- tuning: only `mlx.routed.prefill.item64` rows set `few_rows`, at no
  more than the tile's K-parallel passes take, and gfx1151's is set.
- launches: `prefillLaunch` over a registry of toy entries carries its
  row's cut in `route.few`, whatever the caller's route held, and none
  for a row without one. It fails when the copy is lost.

Refs #2795
@ashhart

ashhart commented Nov 12, 2026

Copy link
Copy Markdown
Owner Author

Stopped: the landing host restarted mid-CI; its PRs are back at the front of the queue and will go out in a fresh train.

@ashhart ashhart closed this Nov 12, 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.

6 participants