Skip to content

GB10 sm_121 latency kit versus NAK - #254

Merged
aien-spark merged 15 commits into
mainfrom
cursor/sm121-nak-latency-da60
Nov 6, 2026
Merged

aien-spark merged 15 commits into
mainfrom
cursor/sm121-nak-latency-da60

Conversation

@aien-spark

@aien-spark aien-spark commented Nov 1, 2026 •

Copy link
Copy Markdown
Collaborator

Purpose and scope

Host-built latency kit for fixed stalls on the GB10. Kernels are CUDA++ machine code.

The headline chip receipt is evidence/LATMEAS-GB10-20261101-oracle/. sm121_vs_NAK.md in that directory is the table filled from that raw.json. It was measured at fb91560, the merge of 7ec0184 and PR #251 6a93cb5, on 2026-11-01T18:47:06Z. stall_required is 4, 4, 5, 13, 4, 3, 20, spread 0. At each passing stall and above, lanes_matched_min is 32.

Head for the next chip run is 23f266313e95f8bae2ce8880e914537685b61bf1. That commit merges origin/main, so SM 0xa04 admission comes from #246 (dba5821) and this PR no longer depends on #251 to open the device. 23f2663 also records the launch command and its exit code, treats untracked files as a dirty tree, removes the host self-test's kernel dump, and labels programmed stall 1 cycles. It does not change emitted kernel bytes or the stall sweep.

The predicate row's 70 cycles are cycle_median_stall1: the raw clock at programmed stall 1 (tests/gpu_latmeas_test.c lines 1965 and 1987), not stall 13 minus one, and not overhead-subtracted. At stall 1 the guard still sees the previous true P0, the poison branch leaves, and the clocks cover that exit, so 70 is below the 1261-cycle empty loop. The pass criterion is the oracle sweep (reported_threshold, line 1223, from pass[][] at line 1884). The clock at stall 13 is cycle_median 2714 (lines 1946 and 1981), also raw. Neither clock decides the stall. raw.json for pred matches those four numbers.

Command

On the Spark, at 23f2663:

sh tools/latmeas/run_spark.sh latmeas-out

Build and make doctor stay outside the lock. Only the launch is inside flock /tmp/aien-gb10.lock, wrapped in timeout 600. The binary prints the bound: 1408 launches, 71 s upper bound (50 ms each). The receipt records that command and its exit code.

Evidence on this commit

The oracle receipt is a receipt for fb91560, not for 23f2663. Chip gates for 23f2663 are NOT RUN until that rerun. Host gates for 23f2663 are the fresh-clone comment: #254 (comment) . That comment is not a commit, so the chip SHA stays 23f2663.

  • make test — PASS, 73 targets, exit 0. Fresh clone of 23f2663.
  • make test-native-artifact — PASS, 47 checks, exit 0.
  • make zero-cuda-gate — PASS, exit 0.
  • build/hello_gpu --sim — PASS, exit 0.
  • make doctor — NOT READY, exit 2. Expected on this VM (no NVIDIA module, no /dev/nvidia*).
  • GB10 — NOT RUN for 23f2663. PASS for fb91560 only, in evidence/LATMEAS-GB10-20261101-oracle/.

What merge still needs

Draft. Ruleset main-verified-promotion does not require checks. CODEOWNERS.proposed is inactive. docs/REVIEWING.md section 1: evidence for the reviewed head, a fresh-clone host-gate receipt, approval by someone other than the author, gh pr merge --match-head-commit.

Open in Web Open in Cursor 

Sweep re-check (2026-11-04, DRA-130)

CI: only drift fails (https://github.com/aien-dev/CUDApp/actions/runs/41910517716/job/126856525966). The #251 dependency is gone (see the head paragraph). GB10 at this head: NOT_RUN.

The drift FAIL (45 of ~920 GPU paths vs omega-mv d3d1b1bc7) is inherited from main. It clears when #266 merges.

Dependent-chain kernels, a NAK diff, and a Spark runner that holds
flock /tmp/aien-gb10.lock only around the launch (timeout 600).
Measured cells stay empty until that run; the host self-test does
not invent them.

Co-authored-by: aien-spark <[email protected]>
Comment thread tests/gpu_latmeas_test.c Fixed
Comment thread tests/gpu_latmeas_test.c Fixed
Comment thread tests/gpu_latmeas_test.c Fixed
Comment thread tests/gpu_latmeas_test.c Fixed
Comment thread tests/gpu_latmeas_test.c Fixed
Comment thread tests/gpu_latmeas_test.c Fixed
cursoragent and others added 8 commits November 1, 2026 17:48
The fixed-latency chains read a destination armed to a sentinel the
producer does not read, so a stale value fails the oracle. Stall-1
SASS is checked for that pair with no scoreboard and no yield. Cycle
medians at stall 1 are recorded even when every stall matches. GB10
open still depends on the 0xa04 admission; this does not add it.

Co-authored-by: aien-spark <[email protected]>
sm121dis of the overhead kernel showed IADD of the counter with stall 1
(control 0x000fc200) immediately before ISETP, and @!P0 BRA back to the
body. The compare then samples the previous counter, so the stored trip
count misses the overhead check and every exact oracle fails through
stall 15. HMMA has no loop. On the predicate chain, stall 1 leaves the
previous true P0 for @p0 BRA, so the poison move exits on the first pass
and the clocks cover that exit.

The host simulator hides GPR writes for 5 cycles and predicate writes
for 14. Overhead must store the trip count, the top of the sweep must
match the fresh oracle, stall 1 must match the stale oracle, and putting
stall 1 back on the increment must fail the counter check.

Co-authored-by: aien-spark <[email protected]>
The launch is cb5bd70, the merge of 03cdcc5 and PR #251 6a93cb5.
raw.json, the filled table, and sm121dis --pairs of each threshold
and the stall under it are the chip output. UNITS.md compares that
stall field with NAK's deps.delay at Mesa 01bab930.

Co-authored-by: aien-spark <[email protected]>
Each lane stores the two clocks, four result words, and its lane id.
HMMA's four words are FADD of d0..d3 against zero, compared with the
host dot of sixteen bf16 ones, not with the stall-36 launch. The host
simulator requires the same match on all 32 lanes. This has not been
launched.

Co-authored-by: aien-spark <[email protected]>
c94c2c9 is the merge of 4f3a4d0 and PR #251. stall_required matches
the earlier receipt. raw.json still has one lane-0 word and a null
reference, so the file does not itself show the 32-lane or d0..d3 check.

Co-authored-by: aien-spark <[email protected]>
raw.json now has the four oracle words, lanes_matched_min, a lane fail
mask, and the first mismatching run, lane, and word. run_spark.sh writes
RECEIPT.txt and no longer prints a 90 s bound beside the binary's 71 s.

Co-authored-by: aien-spark <[email protected]>
fb91560 is 7ec0184 merged with PR #251. Passing stalls have
lanes_matched_min 32. Every recorded first_mismatch is word 2, so the
file does not by itself show a later word failing.

Co-authored-by: aien-spark <[email protected]>
word_mismatch_max counts lanes that miss words 2..5. The receipt uses
PCI 10de:2e12 when proc Model is Unknown, which is the doctor's GB10 row.

Co-authored-by: aien-spark <[email protected]>
Comment thread tests/gpu_latmeas_test.c Fixed
@aien-spark

Copy link
Copy Markdown
Collaborator Author

PR desk (2026-11-01): the device-open prerequisite this PR depends on has landed on main through #246 (merge dba5821, GB10 SM 0xa04 admitted with its PCI identity). Rebase onto main to drop the #251 dependency for that part.

The path passed to fopen is the case id and a stall from that table.
The scanned token is only compared. Emit and the stall sweep are unchanged.

Co-authored-by: aien-spark <[email protected]>

@aien-spark aien-spark left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

draft, so this is an early look. reviewed at 4a5184b (head moved from 0c81899 mid-review; the case-table load looks like it should clear the codeql path alert, but analyze (c-cpp) and host tests were still running when i looked).

  • none of the three evidence dirs are for this head. -oracle is fb91560, -allanes is c94c2c9, the base one is cb5bd70, and all of them merge in #251. #246 already landed the device-open prereq, so rebase on main, drop the #251 dependency (body line 35 still says it needs it) and re-run on the chip at the head.
  • make test and make test-native-artifact are NOT RUN. no fresh-clone receipt yet, which REVIEWING §1 asks for.
  • -oracle raw.json sha matches its RECEIPT.txt. -allanes only has RECEIPT.md, no RECEIPT.txt.
  • benchmarks-mv has no baseline to compare these against.
  • these are minimums from one warp on an idle SM, stall-only, and hmma has no scoreboard. don't feed them into K_FIXED / fixed_pair_class without a run under contention.

fix first (early, draft)

Comment thread tools/latmeas/run_spark.sh Outdated
if [ -f "$out/raw.json" ]; then
python3 tools/latmeas/fill_template.py --results "$out/raw.json" --md "$out/sm121_vs_NAK.md"
fi
write_receipt "$out"

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

write_receipt doesn't record the launch command or its rc, and REVIEWING §2.1 wants both. git diff --quiet also skips untracked files, so a dirty tree can still come out "clean".

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The receipt now records the launch command and its exit code. write_receipt prints launch_command and launch_rc for the exact flock /tmp/aien-gb10.lock timeout 600 ./build/gpu_latmeas_test --launch ... line. Dirty detection uses git status --porcelain, so an untracked file marks the tree dirty. The --receipt-self-test path checks both the command and rc, and a temporary git repo with an untracked file. This is 23f2663.

| ALU -> ALU (GPR RAW) | 4 + 1 = 5 | latencies/sm100/reg_raw.csv:2 row alu column alu = 4; sm120_instr_latencies.rs:457 GPR raw adds max(adjust, 1); IAdd3 is Alu at line 44 | 4 | -1 | 11 runs; stall spread 0; cycles at that stall median 2541 spread 0; stall-1 cycles median 2477 spread 0; loop overhead median 1261 | tests/gpu_latmeas_test.c (alu_alu) |
| FMA -> FMA (GPR RAW) | 4 + 1 = 5 | latencies/sm100/reg_raw.csv:5 row fma column fma = 4; sm120_instr_latencies.rs:59 FFma is Fma; line 457 adds 1 | 4 | -1 | 11 runs; stall spread 0; cycles at that stall median 2541 spread 0; stall-1 cycles median 2477 spread 0; loop overhead median 1261 | tests/gpu_latmeas_test.c (fma_fma) |
| Mixed ALU <-> FMA (GPR RAW) | 5 + 1 = 6 | latencies/sm100/reg_raw.csv:2 row alu column fma = 5 and line 5 row fma column alu = 5; sm120_instr_latencies.rs:457 adds 1 | 5 | -1 | 11 runs; stall spread 0; cycles at that stall median 4362 spread 0; stall-1 cycles median 4170 spread 0; loop overhead median 1261 | tests/gpu_latmeas_test.c (mixed) |
| Predicate write -> guard | 13 + 1 = 14 | latencies/sm100/pred_raw.csv:11 row redirected_fp64 column dualalu = 13; sm120_instr_latencies.rs:474 predicate raw(write, None) uses reader RedirectedFp64 and adds 1; ISetP is Dualalu at line 49 | 13 | -1 | 11 runs; stall spread 0; cycles at that stall median 2714 spread 0; stall-1 cycles median 70 spread 0; loop overhead median 1261 | tests/gpu_latmeas_test.c (pred) |

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pred at stall 1 reads 70 cycles, which is under the 1261 loop overhead. at that stall the guard reads stale and the loop just exits, so this column is measuring a control-flow change, not latency. it's also labeled "stall-1", which reads like S-1, but it's cyc[..][0] (stall=1). either drop it or label it and flag failing stalls.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not a bad stall measurement. cycle_median_stall1 70 is the raw clock at programmed stall 1 (cyc[][][0], tests/gpu_latmeas_test.c lines 1965 and 1987). It is not stall 13 minus one, and nothing subtracts overhead_cycle_median (line 1995). At stall 1 the predicate guard still sees the previous true P0, the poison branch leaves the loop, and the clocks cover that exit, so 70 is under the 1261-cycle empty loop. The pass bit is the oracle sweep: stall_required 13 is reported_threshold (line 1223) of pass[][] set at line 1884 when every lane matches. The clock at that stall is cycle_median 2714 (lines 1946 and 1981), also raw, and it is above the overhead. Neither clock decides the stall. evidence/LATMEAS-GB10-20261101-oracle/raw.json pred is stall_required 13, cycle_median 2714, cycle_median_stall1 70, overhead_cycle_median 1261. The table label is now "programmed stall 1 cycles", and the note under the table cites those lines. 23f2663.

Comment thread tests/gpu_latmeas_test.c
LCHECK(build(&CASES[6], 1, 0, 0, code, sizeof code, &len, &gpr) == 0, "hmma stall 1 encode");
LCHECK(lat_sim(lane0) == 0, "hmma stall 1 sim");
LCHECK(lanes_report(g_sim_mem, o, &m) == 0 && m.word >= 2 && m.word <= 5, "hmma stall 1 fails a destination word");
char tmpl[] = "/tmp/latmeas-load-XXXXXX";

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mkdtemp under /tmp and nothing removes it. every self-test run leaves a full kernel dump plus manifest behind. unlink the files and rmdir at the end.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The host self-test now unlinks every file in the mkdtemp directory and rmdirs it (tests/gpu_latmeas_test.c, the load-check block). 23f2663.

cursoragent and others added 2 commits November 2, 2026 00:03
Brings in the GB10 admission from #246 (dba5821) so nvrm_open accepts
SM 0xa04 on PCI 10de:2e12. Kernel bytes and stall logic are untouched.
Co-authored-by: aien-spark <[email protected]>
The receipt prints the exact flock launch and its exit code. git status
--porcelain marks a tree dirty when it has untracked files. Programmed
stall 1 cycles stay in the table as a raw clock, not as the pass bit:
the stall is the oracle sweep, and neither clock column subtracts the
loop overhead. The host self-test removes the kernel dump it creates.

Co-authored-by: aien-spark <[email protected]>
@cursor cursor Bot changed the title Add a GB10 latency kit to compare fixed stalls with NAK GB10 sm_121 latency kit versus NAK Nov 2, 2026
@cursor

cursor Bot commented Nov 2, 2026

Copy link
Copy Markdown

Fresh-clone host gates for 23f266313e95f8bae2ce8880e914537685b61bf1. Cloned from github.com/aien-dev/CUDApp, detached checkout of that SHA, git status --porcelain empty (0 dirty lines). 2026-11-02T00:16:05Z. Linux 6.12.94+ x86_64, gcc 13.3.0, GNU Make 4.3. CUDAPP_MEM_RESERVE_MIB=0 for this VM only: MemAvailable under-reports because of the hypervisor balloon, and the default 4096 MiB reserve then refuses the Qwen-shaped upload. The platform test unsets the variable and still checks the 4096 default. This is not a Spark setting.

This comment is the receipt. It is not committed on the branch, so the head stays 23f2663 for the GB10 rerun.

Gate Result Exit
make -j4 test PASS. Last line: CUDA++ HOST TESTS PASS (73 targets) 0
make -j4 test-native-artifact PASS. test_gpu_artifact_export: 47 checks, 0 failed 0
make -j4 zero-cuda-gate PASS. hello_gpu needs libm and libc; gpu_matmul_api_test needs libc. Self-test rejected a CUDA probe 0
./build/hello_gpu --sim PASS. 1048576 elements, 0 wrong. machine-code sha256 9e28bd9c46f80584d77238a752e99cecddbc8d622ffbe320885ed2f499e1f075 0
make doctor NOT READY, expected on this VM. No NVIDIA module, no /dev/nvidiactl, /dev/nvidia0, or /dev/nvidia-uvm, no NVIDIA GPU in sysfs. Multitarget table NOT_RUN (pinned omc not on this clone). GNU make exits 2 when that recipe fails 2
GB10 chip NOT RUN

Host results are not hardware qualification.

@aien-spark
aien-spark marked this pull request as ready for review November 6, 2026 00:28
@aien-spark
aien-spark merged commit 98d179a into main Nov 6, 2026
22 checks passed
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.

3 participants