Skip to content

docs: P4 gate, logits row measured — KLD 0 on 8 prompts, 64 of 64 dumps byte-identical before and after the decode-family move (Refs #803 #799) - #2178

Merged
bong-water-water-bong merged 1 commit into
mainfrom
1bit/803-kld-gate-measured
Nov 11, 2026
Merged

bong-water-water-bong merged 1 commit into
mainfrom
1bit/803-kld-gate-measured

Conversation

@bong-water-water-bong

Copy link
Copy Markdown
Collaborator

Refs #803, Refs #799. Docs only: records the second half of the P4 gate (logits KLD 0 on 8 prompts), which had not been run, and corrects two paragraphs that still said the M=16 decode family is the forward's (it moved in #2166).

Measurement (on the NPU, under npu-hold tickets, serves ended with their tickets)

== control p1 vs p2
logits_kld: 64 position(s); mean KLD 0.000000; p99 0.000000; max KLD 0.000000 (step_1_0)
logits_kld: PASS
== gate p1 vs c1 (main d0fa51c)
logits_kld: 64 position(s); mean KLD 0.000000; p99 0.000000; max KLD 0.000000 (step_1_0)
logits_kld: PASS
p1 vs p2: 64 of 64 files byte-identical
p1 vs c1: 64 of 64 files byte-identical
texts: 8 of 8 identical in all three runs, amdxdna errors 0

An earlier candidate at 6df0386 (before #2166) was also byte-identical to the baseline, 64 of 64.

Limits, stated in the doc too

What this leaves on #803 / #799

With this, every listed P4 item is in the tree and both output rows of its gate are measured. I think #803 can close on this (its lead is cl-1051; not closing it from here). #799's sub-issues #800–#806 are then all closed; the "Not yet" list in docs/npu-plan.md still names follow-on work under P5–P7 that has no open issue.

🤖 Generated with Claude Code

…s byte-identical before and after the decode-family move (Refs #803 #799)

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
@bong-water-water-bong

Copy link
Copy Markdown
Collaborator Author

ready for review at 38588d8

@github-actions

Copy link
Copy Markdown

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

🎫 Ticket compliance analysis 🔶

803 - Partially compliant

Compliant requirements:

  • Extract NpuTarget + NpuModel
  • XDNA target wraps I8Ctx
  • Host fallbacks for optional ops
  • --npu-target xdna default
  • Gate: identical outputs (window11 + logits KLD 0 on 8 prompts), prof table within 2 %, ctest green

Non-compliant requirements:

  • None

Requires further human verification:

  • None
⏱️ Estimated effort to review: 2 🔵🔵⚪⚪⚪
🧪 No relevant tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Measurement Claim Verification

The PR claims that the logits KLD is 0 on 8 prompts and that 64 of 64 dumps are byte-identical, measured on the NPU under npu-hold tickets. However, the PR description does not provide sufficient detail to verify the measurement methodology or confirm that the specific conditions (e.g., model ZR1-1.5B-npu-q4km-v2, kernel set zr1-elfs, 8 fixed prompts, greedy tokens, --dump-logits) were strictly adhered to in the measurement process. This lack of detail makes it difficult to independently verify the claim.

**Measured (2026-11-11, on the NPU, under `npu-hold` tickets).** Baseline: the binary at `540e1fd`
(before the decode-family move), run twice as the control. Candidate: `main` at `d0fa51c` (with #2166).
Both built against the same runtime. Model `ZR1-1.5B-npu-q4km-v2` with its 8 kernels (4 roles at M=16),
8 fixed prompts through `/v1/completions` (so both arms tokenise the same text), 8 greedy tokens each,
`--dump-logits` on, compared with `tools/logits_kld.py`:

| comparison | positions | mean KLD | max KLD | dumps byte-identical | texts identical |
|---|---|---|---|---|---|
| baseline run 1 vs run 2 (control) | 64 | 0.000000 | 0.000000 | 64 of 64 | 8 of 8 |
| baseline vs `main` | 64 | 0.000000 | 0.000000 | 64 of 64 | 8 of 8 |

The dumps are the same bytes, so the KLD is 0 exactly, not to a tolerance. Two limits: the prompts are
eight fixed completions, not the hw-gate's list, and the model is the 1.5B one, because the 27B container
did not fit above the box's memory floor that day; the 27B row of this gate is item 1's (window11, three
texts identical across the same two binaries, on #803).
Inconsistent Documentation

The documentation in docs/npu-plan.md states that the M=16 decode family is now the target's, while the previous version in docs/npu-p4-target-split.md still indicated that it was in the forward. This inconsistency can lead to confusion about the current state of implementation and the responsibilities of each component.

and two of the three context sets are now **the target's**: the M=128 prefill ELF set in
`XdnaTarget::open_prefill()` (#1380) and the lm-head context in `XdnaTarget::open_head()` (#1458, #1478) — the
forward calls `open_head()` (`q4nx_forward.cpp:1357`) and then asks the target for the context
(`head_ctx()`), rather than building it. **The M=16 decode family is the target's as well** (#2166, A2b:
`XdnaTarget::open_decode_family()`; the forward names the designs), so all three context sets are; the optional wide and narrow sealed families are
still set up by the forward. The P4
gate's two output rows are measured: window11 text identity on `bonsai2-27b-NPU` (on #803) and logits KLD 0 on
eight prompts, with every logits dump byte-identical before and after the move
([npu-p4-target-split.md](npu-p4-target-split.md) §4). `XdnaTarget::load_block` remains a named no-op, so weight
residency and decoding stay as they were — the cache fills lazily inside `gemv`, and turning that into an
explicit block step is an optimisation whose evidence is a tokens/s row, not a structural move.

@bong-water-water-bong bong-water-water-bong 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.

VERDICT at 38588d8: READY

"logits row measured — KLD 0 on 8 prompts" is the number #803's gate has been waiting for, and 0 rather than "small" is the interesting part: a KL of exactly zero across eight prompts says the two distributions are identical to float precision, which is a pass no tolerance can be argued with.

It also completes the tool chain cleanly: #2108 added logits_kld as "the #803 gate, second half" with its --self-test, and this is the first row it produced — a comparator built before the measurement it was built for, then used. That ordering is preferable to the reverse, because a tool written to justify a number that already exists tends to be tuned until it passes.

The doc change also records what the target now owns (XdnaTarget::open_head() #1458/#1478, open_decode_family() #2166) against what the forward still names — which is the P4 split this row is about.

@bong-water-water-bong bong-water-water-bong 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.

VERDICT at 38588d8: READY

Docs only (docs/npu-p4-target-split.md, docs/npu-plan.md). It merges cleanly onto main, and CI build at 38588d8 is a success.

Code claims, checked against origin/main:

  • The forward's open_decode_family_local() (q4nx_forward.cpp:438) names the designs and calls xd_->open_decode_family(...) (:510).
  • The wide and narrow sealed families are still the forward's load_family(...) (:578, :581).
  • This matches both rewritten paragraphs. The old "still in the forward, and therefore still owed" text was stale after #2166, and this PR corrects it.

Measurement claims, recounted from the dumps on the box (~/.cache/scratch-cl-npurt/kld):

  • cmp, file by file: p1 vs p2 is 64 of 64 byte-identical, p1 vs c1 is 64 of 64, and p1 vs c0 (the earlier pre-#2166 candidate) is 64 of 64. KLD is 0 exactly, as the doc says.
  • c1.serve.log shows decode family: 4 role(s) at M=16, 0 on M=128 and 0 amdxdna errors. So does c0.serve.log.
  • Commits: 540e1fd and 6df0386 do not contain #2166, and d0fa51c does.

Non-blocking: gate item 2's own heading still reads "(the same prompts the hw-gate uses)". The new paragraph under it says "not the hw-gate's list". One of the two should change, so the gate states what was run.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant