Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
4ee2e85
hip: move code out of files near the 600-line limit, unchanged
davidtai Oct 30, 2026
f7ac354
contract: five 1x2 stream-tile gate-5 records as hipcc builds them now
davidtai Oct 30, 2026
88aa18d
flashnext hip test tool: --margins N
davidtai Oct 30, 2026
9411438
flashnext hip: n-gram tables as a resident copy at load
davidtai Oct 30, 2026
daf7ec7
flashnext hip: a 4,096-row default prompt chunk
davidtai Oct 30, 2026
12a6370
hip decode: router logits, top-k pick and route plan in one launch
davidtai Oct 30, 2026
4115368
hip draw: a wide row's top-k in parts, then a join
davidtai Oct 30, 2026
eb50c0b
hip stream tile: one row loads x before the next round's codes
davidtai Oct 30, 2026
bff7fc1
hip stream tile: column sums in one transposing butterfly
davidtai Oct 30, 2026
c0a9e62
hip stream tile: one column a lane for narrow plain launches
davidtai Oct 30, 2026
fd4797d
hip stream tile: row blocks on the grid's x
davidtai Oct 30, 2026
461d8e0
hip stream tile: a SwiGLU pair lane carries at most 8 code words
davidtai Oct 30, 2026
fc3d43b
hip stream tile: routed plain at most 4 columns a lane past one row
davidtai Oct 30, 2026
e432e36
hip stream tile: one row under 20,000 columns takes one column a lane
davidtai Oct 30, 2026
6108849
hip stream tile: a pass with one live row reads that row only
davidtai Oct 30, 2026
7821dc4
hip stream tile: routed 5 to 8 rows as a work list
davidtai Oct 30, 2026
9bcd8a6
hip stream tile: the held-codes body at 3 to 8 rows
davidtai Oct 30, 2026
72f868f
hip: prompt expert gate+up and down on one WMMA tile
davidtai Oct 30, 2026
adb89ef
hip: dense prompt products on a K-specialized WMMA tile
davidtai Oct 30, 2026
42ccbbb
hip recurrence: prompt DeltaNet at half a value head a block
davidtai Oct 30, 2026
1b6c016
hip DeltaNet: prompt conv, split, norms and gates in one launch
davidtai Oct 30, 2026
9701c7c
flashnext hip: a verify window holds its DeltaNet state; keep replays
davidtai Oct 30, 2026
dd7dbaf
hip flashnext: a prompt slice's indexer scores at 4 lanes a score
davidtai Oct 30, 2026
e2750fb
hip flashnext: a QSA layer's decode prep in one launch
davidtai Oct 30, 2026
2a12609
flashnext hip: the MoE combine inside the next HC norm
davidtai Oct 30, 2026
61529bc
hip: a prompt's picks a wave a row, its plan in three launches
davidtai Oct 30, 2026
f132d54
flashnext hip: MTP head caches only for streams that draft
davidtai Oct 30, 2026
c99b0d0
flashnext hip: sampled streams draft the target's keyed choice
davidtai Oct 30, 2026
8d9e334
flashnext hip: the depth rule prices drafts from forwards timed at load
davidtai Oct 30, 2026
4ee89c6
flashnext hip: verify windows of up to 8 rows
davidtai Oct 30, 2026
6e4727c
flashnext hip: mtp_depth=gated and Flash Next's gated chain
davidtai Oct 30, 2026
d5a1fc7
hip: a side stream in Ops with fork and join
davidtai Oct 30, 2026
37aed1a
flashnext hip: one-row decode prefetches down codes on the side stream
davidtai Oct 30, 2026
2f42c9c
docs: receipts for the Flash Next HIP series on gfx1151
davidtai Oct 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
21 changes: 21 additions & 0 deletions LICENSES/Strata-MIT.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 Niko1221 and the Strata contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
8 changes: 8 additions & 0 deletions THIRD_PARTY_NOTICES.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ Copyright © 2026 Joshua Warren and mlx-omarchy contributors.
Its arithmetic is unchanged; the native Vulkan path does not link MLX.
See [the license text](LICENSES/mlx-omarchy-MIT.txt).

## Strata HIP kernels

`zig/kernels/hip/common/tsum.hpp` ports `src/kernels/cuda/s26_tsum.cuh` from
[Strata](https://github.com/Niko1221/Strata) at 4b9e05a2, MIT License,
Copyright (c) 2026 Niko1221 and the Strata contributors.
The block layout of `zig/kernels/hip/tiles/dense_gemm.hpp` follows Strata's gfx11 WMMA kernels; its arithmetic is TensorFold's.
See [the license text](LICENSES/Strata-MIT.txt).

## Qwen Flash Next

The n-gram ID helpers translate Hugging Face transformers' `models/qwen4_exp/modeling_qwen4_exp.py` with renamed
Expand Down
36 changes: 36 additions & 0 deletions docs/kernel-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,36 @@ each row then L2-normed, against float64's ranges at their bf16 points and expf'
CUDA also checks `dense_rows`' scaled epilogue (Dory's logits, fp32(bf16(x . w)) times a per-column scale), bit-equal to its
unscaled tile's outputs rounded and scaled on the host.
HIP: the RMS norm rows, a wave a row (fp32, fp16 and bf16 out) and a block a row (fp16 and bf16 out).
HIP also checks Qwen3.8-Flash-Next's gfx11 matrix-core prompt tiles: the dense tile (`affine_wmma_dense`) at K 320,
2560, 6144 and 10240, fp32 out and bf16 out, bit-equal to the matrix tile `affine_wmma_gemm` (then `tf_cast`).
Its routed expert tiles take a prompt's plan: gate and up with silu(g) u in bf16 (`affine_wmma_moe_gu`) and down in fp32
(`affine_wmma_moe_down`), bit-equal to `affine_wmma_gemm` over the same plan (then `tf_moe_act`).
HIP also checks a prompt's MoE plan in three launches (`tf_route_count`, `tf_route_scan`, `tf_route_scatter`),
bit-equal to `tf_moe_route`, and its stated plan: the pairs stably sorted by expert, each expert's pairs cut into tiles.
HIP's MoE picks: `tf_select_rows`, bit-equal to `select_body`, and `tf_router_select_*` (router logits, picks and plan in
one launch), bit-equal to `tf_router_decode`, the pick and `tf_moe_route`, its logits' sums proven fp32. Both check the
pick's stated rule (a NaN below every number, ties to the lower id, every slot written) and carry a known finding at
gate 1: upstream's `select_body` ranks a NaN level with the top number, so slot 0 races, and a row of fewer than k
numbers leaves slots unwritten. Their bytes stay equal to upstream's.
HIP's top-k in parts (`tf_topk_part`, `tf_topk_join`) is bit-equal to `tf_topk` and checked against its key order: a
positive NaN above +Inf, a negative NaN below -Inf, -0 equal to +0, ties to the lower id.
`tf_moe_prefetch` writes nothing: gates 1 to 3 check that every buffer and guard stays as prepared, and gate 5 holds
its launch. It has no float output for gates 1 and 3 to class or bound, and no sums for gate 4.
HIP also checks Flash Next's hyper-connection kernels. On gfx11, `tf_affine_dense_hcmix` (hc up's dense K 320 tile
with the HC mix as its epilogue) is bit-equal to the tile's bf16 product then `tf_fn_hc_mix`, and its gate table
`tf_hc_gate_fill` holds, for all 65,536 bf16 g, float64's sigmoid rounded to bf16 within expf's 2^-20 and
`tf_fn_hc_mix`'s gate bit for bit.
`tf_fn_hc_mix8`, the bf16 mix at 8 outputs a thread, is bit-equal to `tf_fn_hc_mix` over fp32 and bf16 g.
`tf_fn_hc_norm_v` (bf16 streams of 2,560 in 16-byte accesses, with and without a branch) is bit-equal to
`tf_fn_hc_norm`, and the MoE combine inside the norm (`tf_fn_hc_norm_moe`, and `tf_fn_hc_norm_moe_v` a block a row)
to `tf_moe_combine` then `tf_fn_hc_norm`.
HIP also checks the DeltaNet prompt recurrence at half a value head a block (`tf_gdn_prompt_128`, its y and state),
bit-equal to the plain window scan `gated_delta_tile` on the same tokens.
Its prompt prework in one launch (`tf_gdn_prompt_prep`: conv, q and k head norms, v, gate, beta and the new conv state)
is bit-equal to the chain it replaces (`tf_conv_prefill`, `tf_copy_cols`, `tf_rms`, `tf_gdn_gate_prefill`).
A verify window that holds its state (`tf_gdn_hold_128` and `_16`: y only, the state an input left unchanged) and the
keep that replays it (`tf_gdn_keep_128` and `_16`: two layers' states after their kept rows) are each bit-equal to
`gated_delta_tile` over the same rows.

RADIO's fused bias, activation and residual (`tf_vit_bias_act`) replaces `tf_vit_bias` then
`tf_vit_pointwise`, byte for byte.
Expand All @@ -207,6 +237,12 @@ CUDA also checks Nemotron's pocket (`nemotron_ops.cu`, the learned neurons the l
shared expert): relu^2 up into act rows and down added into the shared expert's slot, a block row a row and tiled, the
down to fp32 and bf16, against float64, the neurons past the count NaN so a read of one shows (two controls: the up's
relu by `fmaxf`, and the down launched one neuron short).
HIP also checks Flash Next's index block scores over 4 heads of 128: `tf_fn_idx_scores_quad` (4 lanes a score) and
`tf_fn_idx_scores8` (8 rows a wave), each bit-equal to `tf_fn_idx_scores`. Their relu is `fmaxf(x, 0)`: a NaN head
dot scores 0 where float64 keeps the NaN, a known finding as `tf_glm_scores`'s is.
HIP also checks Flash Next's QSA decode prep, `tf_fn_qsa_prep`, bit-equal to the seven launches it replaces (`tf_qk_rope`
for q, the index q and k, `tf_kv_write_at` for v and the raw index key, `tf_copy_cols` and `tf_fn_idx_pool_at`) on 1 to
15 rows, its fp32 rows and its caches and pools each registered, the caches' earlier contents prepared.

## CUDA records per nvcc release

Expand Down
121 changes: 121 additions & 0 deletions docs/receipts/gfx1151-flashnext-hip-37aed1a0/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# gfx1151 Qwen3.8-Flash-Next HIP series: exactness, performance and memory

Receipt for [#1667](https://github.com/ashhart/TensorFold/pull/1667), the HIP Qwen3.8-Flash-Next series on gfx1151.
Measured on 2026-10-31, on an AMD Strix Halo (gfx1151) machine with 192 GiB of unified memory (184.6 GiB reported by
the kernel), ROCm 7.15 (HIP 7.15.26333), zig 0.17.0, Python 3.13.
The tested source is the tip of the series' code commits, **`37aed1a0384625d4ff35c23e7df2b502d2a8b7f6`** (`new`). The comparison is
`main` at **`94b90c9db65dcb71bbc39715901d6fb1f00407d0`** (`base`), the commit the series is based on.
The receipt commit adds only this folder. This receipt does not qualify other checkpoints, other GPUs, multi-GPU runs,
other platforms or machines with less memory.

## Checkpoint and binaries

- Checkpoint `TensorFold/Qwen3.8-Flash-Next-MLX-4bit-MTP`, revision `2b170fa6309d5d1ee380b35636075fac7945f286`.
`perf/checkpoint.json` lists each file with its size and SHA-256.
- Binary SHA-256 values are in `perf/provenance.json` and `exactness/provenance.json`. Build:
`zig build native-hip tf-flashnext-hip-test -Dhip=true -Dhip-arch=gfx1151 -Doptimize=ReleaseFast` with the ROCm
`hipcc`, include and device-library paths. Every row of this receipt ran on these two binaries.
- `perf/logs/session-01.log` to `session-06.log` are the harness logs in time order: cold prefill at 2K to 32K, at 64K
and 128K, at the native window, the decode clients, the 16K decode, and the memory A/B. `new` and `base` alternate
in each round of each session.
- One server process ran at a time, under the machine's GPU lock. Each run started a fresh server and stopped it.
No limit of the machine was raised.

## Exactness ([exactness/](exactness/))

Server: `--context 32768 --parallel 4 --prompt-cache-gib 1 --no-thinking`, `--metrics-open`.

| Check | Result |
| --- | --- |
| `zig/tests/qwen35/serve_check.py` | 15 ok, 0 fail; 1,285 of 1,688 drafted tokens accepted |
| Token-hash audit (identical request bodies, `draft` ignored) | 11 groups, 0 differ |
| `zig/tests/qwen35/serve_cancel.py` | 3 ok, 0 fail |
| `zig/tests/flashnext_hip/rewind.py`, prompt cache on against off | 0 replies differ; up to 10,240 cached tokens |
| `tf-flashnext-hip-test generate --chat --max 64 --hash --chunk C`, 10,368-token prompt | `c8f6888043c972e0` for C = 512, 2,048, 8,192 and 20,480 |

`serve_check.py` ran under `record_http.py`, which keeps each request and reply in `exactness/*-serve-check.jsonl`
and passes both on unchanged. `exactness/seam-corpus.json` names the public CPython library files of the seam prompt.

## Performance ([perf/](perf/))

`new` and `base` alternate in each round, 2 rounds. Greedy, seed 1234, thinking off, prompt cache off.
Prompts: token arrays of exactly N tokens from the top-level `.py` files of the CPython standard library
(`perf/prompts/corpus.json` names each file and its SHA-256). `perf/summary.md` has every row and every round.

- Cold prefill: `max_tokens` 1, a fresh server a run, `--context` N + 8,192 (262,144 for the 261,888-token row).
Prefill tok/s = prompt tokens / TTFT.
- 16,384-token prompt: 256 and 1,024 output tokens, 3 reps each in 2 rounds, after one unrecorded 16-token request.
Serial runs the server with `--no-drafts`; Default MTP runs the server defaults.
Decode tok/s = (output tokens - 1) / (last token time - first token time).
- `tools/bench_openai.py` and `tools/bench_concurrent.py --levels 1,4 --alone --serial`, 256 tokens, 3 reps.

| Prompt tokens | 2,048 | 8,192 | 32,768 | 65,536 | 131,072 | 261,888 |
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
| `new` prefill tok/s | 673.7 | 558.2 | 513.5 | 496.7 | 471.6 | 437.3 |
| `base` prefill tok/s | 293.2 | 261.3 | 260.4 | 259.3 | 258.0 | 244.8 |

| 16,384-token prompt, 1,024 tokens | Prefill tok/s | Decode tok/s | TTFT (s) | Wall time (s) | Spec-decode hit rate (%) |
| --- | ---: | ---: | ---: | ---: | ---: |
| `new` serial | 533.5 | 35.71 | 30.75 | 59.39 | - |
| `base` serial | 353.3 | 29.99 | 46.55 | 80.70 | - |
| `new` Default MTP | 533.5 | 49.05 | 30.94 | 51.86 | 61.8 |
| `base` Default MTP | 353.3 | 38.26 | 46.54 | 73.32 | 68.1 |

The Default MTP reply has the same token hash as the serial reply on both builds, and both builds give the same tokens.
Four concurrent streams, aggregate decode: 117.4 against 89.65 tok/s (code), 99.75 against 72.95 tok/s (chat);
concurrent equals alone and drafted equals plain on both builds.

## Memory ([perf/memory-ab/](perf/memory-ab/))

`memory_ab.sh`: a fresh `new` server at `--context 20480 --parallel 1 --prompt-cache-gib 0` for each setting,
2 rounds, one cold 16,384-token prefill each. `perf/memory-ab/summary.txt` lists the load line, the GPU peak over idle
and the RSS peak (bytes) and the TTFT of each run. Round 1:

| Setting | Load line, device | GPU peak over idle (GiB) | RSS peak (GiB) | 16K TTFT (s) |
| --- | ---: | ---: | ---: | ---: |
| `new` defaults (`prefill_step=4096`, resident n-gram rows) | 81.82 GiB | 82.73 | 104.4 | 30.74 |
| `new --policy prefill_step=20480` | 105.63 GiB | 106.5 | 104.5 | 29.69 |
| `new --policy prefill_step=1024` | 77.36 GiB | 78.27 | 102.6 | 33.90 |
| `new --policy rows=mapped`, rows not in the page cache | 81.82 GiB | 82.73 | 78.57 | 46.30 |
| `new --policy rows=mapped`, rows in the page cache | 81.82 GiB | 82.73 | 78.62 | 30.92 |
| `new --policy prefill_step=1024,rows=mapped` | 77.36 GiB | 78.27 | 78.62 | 34.13 |
| `base` | 76.58 GiB | 78.50 | 78.54 | 46.47 |

The prompt chunk sets the GPU memory; the resident copy of the 29.80 GiB of n-gram tables sets the RSS.
The first `rows=mapped` run follows a run whose copy at load dropped the rows from the page cache; the second follows it.

- Process: peak resident set of the server. Device: `mem_info_gtt_used` + `mem_info_vram_used` of the card, sampled at
1 Hz, as the peak over the idle value read before the server started. The two overlap on unified memory: do not add them.
- Each server run has a summary in `perf/runs/*.json` with its load line, and its samples in `perf/memory/`.

## Build, test and kernel contract ([checks/](checks/))

On `new`:

- `contract.log`: `SUMMARY gfx1151 83 kernels: 83 pass gates 1-4, 0 fail, 4 known findings; 5 controls: 5 red as
planted, 0 missed; 0 limit problems`.
- `limits.log`: `PASS kernel-contract-limits: every registered launch fits gfx1030, gfx1100, gfx1151 and gfx1201,
every record current, every control red`.
- `test-hip.log` (`zig build test -Dhip=true`), `hip-kernel-test.log`, `test-cuda-host.log`: exit 0.
Zig prints a passing test's stderr under a `failed command:` line.
- `perf/hash-new.log` and `perf/hash-base.log`: `tf-flashnext-hip-test generate --chat --max 64 --hash`, logits hash
`243ecea88fe50d9f` on `new` and on `base`.

`per-commit.tsv`: each of the 33 code commits, built and tested alone on an earlier `main` (`abf822585`):
`zig build native-hip tf-flashnext-hip-test`, `zig build test -Dhip=true`, `zig build hip-kernel-test`, then
`tf-flashnext-hip-test generate --chat --max 64 --hash`. Every exit code is 0 and every hash is `243ecea88fe50d9f`.
The table names the tested commit and the commit in this pull request. Their added and removed lines are the same,
with two exceptions: commits 19-33 ran before their gate-5 records were refreshed (`hip_records_*.zig` only), and
commit 5 ran with a 20,480-row default chunk, which this pull request sets to 4,096 rows.

## Not run

- Metal and CUDA device tests (no such hardware). HIP devices other than gfx1151: only the limits check.
- Sampled (temperature above 0) decode speed. Multi-GPU. Other checkpoints.
- No standard-server comparison: `mlx_lm` needs Metal.

## Publication

Each subfolder is a sanitized copy. Machine-local paths, the host name and the user name are replaced with
placeholders (`publication.json` counts the replacements per file). Request and reply values, token ids, token hashes
and telemetry are unchanged. No model weights or credentials are included. `SHA256SUMS` lists every file but itself.
Loading