Repository navigation
docs/npu-plan: the schema table learns about designs[], and the P4 row stops saying the derivation is all there - #1529
Conversation
…w stops saying the derivation is all there is Three statements on this page were true when written and are not now, all of them about fields the loader reads: * the schema table had no row for designs[] -- the per-role kernel set the plan may name (#1330, #1333), with its both-directions agreement check, its bare-file-name artifact, the generator writing it only where it can be complete, and Plan::check_artifacts() refusing a missing artifact at load (and npu_plan.py --check asking the same question at import time); * the layers[] row said the forward 'derives designs from the container', which is now only the no-section case; * the 'Not yet' P4 entry stopped at A2b part 1 (#1380). A2b is complete: the M=128 ELF set in open_prefill (#1380) and the lm-head context in open_head (#1458, #1478), so q4nx_forward.cpp creates no I8Ctx at all. What is genuinely still open is the residency step -- load_block is a named no-op, the cache fills lazily inside gemv -- and that is an optimisation whose evidence is a tokens/s row, not a structural move. Docs only; no code change.
|
#ready for review — docs only, three sentences that were true when written. The one worth checking is the |
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT: READY at cfa7829538e0e4648b27bdf0f182a0a2d3c47fa7
Both edits are corrections of things that were true when written and are not now, and the field the table gains is real:
npu/plan/plan.h:188 std::vector<Design> designs;
npu/plan/plan.h:172-190 designs_agree() compares the plan's `designs` against the derived set — "a plan that names the
set makes [the check] say the container's designs are not the ones it derives, instead of
silently running whatever the probe found"
tools/npu_plan.py · tests/npu_plan_test.cpp · tests/npu_plan_generate_test.py all reference `designs`
So designs[] belongs in the schema table — it is a field a plan can carry, and the row says optional with the role list, which is what a reader of that table needs to know.
The paragraph change is the better half. "A2 is in progress and is tracked on its own page of record … so this list does not try to snapshot it — as of #1380 (A2b, part 1) the M=128 ELF set is adopted…" was a snapshot of an in-flight refactor, and it had already gone stale (A2b is at part 3 now). Replacing it with "the two ends that touch this page's fields are worth naming" — designs[] from #1330/#1333 and the setup side moving to the target in #1380 — is the version that does not need re-editing every time a step lands, because it names the interfaces rather than the state of the work.
That is the same rule this page's own history keeps re-learning: a doc that snapshots a moving branch rots (#1405 is currently seven rounds red for exactly that). Here it is being fixed instead.
Not run: nothing to run. The designs references above are the evidence that the table row describes the schema that exists.
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT: CHANGES at cfa7829538e0e4648b27bdf0f182a0a2d3c47fa7
I checked each new sentence against origin/main. The designs[] row and the layers[] fix are accurate. The P4 paragraph overstates what's finished.
Accurate:
Plan::designs_agree()(plan.h:510) checks both directions: the plan-named loop, then "a derived design the plan does not name…" at:38-42. The forward calls it atq4nx_forward.cpp:1118.Plan::check_artifacts()(plan.h:559, called atq4nx_forward.cpp:1155) andnpu_plan.py --check(:325-340) apply the same rule: present and non-empty, and the first directory that has the file wins.XdnaTarget::open_prefill()(xdna_target.cpp:99) andopen_head()(:488) exist, andload_blockis the named no-op (:219-222).
Not accurate: "the setup side is the target's … so q4nx_forward.cpp creates no I8Ctx at all … load_block() … is the one A2 item left".
- The M=16 decode family is still set up by the forward.
q4nx_forward.cpp:1307isI8Ctx* c16 = xd_->make_ctx16(i, designs16_[i]);, and the forward then configures that context field by field (MD/KD/ND,blocked_b,pb16i,t2s, …). The target allocates the memory, but deciding which contexts exist and configuring them is still the forward's job. - The page of record says so in so many words.
docs/npu-p4-target-split.md:129: "still in the forward, and therefore still owed: the M=16 decode family and the lm-head context", and:136-138saysadopt_geometry/make_ctxstay public "because the decode family and the lm head use them". - So A2 has two items left, not one: moving the decode family, and
load_block(marked "Not started" at:131-133). This page, a schema reference that says it won't snapshot A2, shouldn't contradict the page it defers to.
Suggested fix (two clauses):
- Name the prefill set and the lm-head context as the target's (#1380, #1458/#1478).
- Keep "the M=16 decode family is still set up in the forward (
make_ctx16)" as open. - Drop "creates no
I8Ctxat all", or scope it to "allocates noI8Ctxitself".
While you're there: npu-p4-target-split.md:129 still lists the lm-head context as owed, although open_head() and #1458/#1478 have landed. That page is the one that's stale on that half. Updating it in the same PR would make the two pages agree.
…as two items left, not one Review at cfa7829 is right and this is the correction: the paragraph said the setup side is the target's and that q4nx_forward.cpp creates no I8Ctx at all. The prefill ELF set (#1380) and the lm-head context (#1458, #1478) did move - the forward calls open_head() at :1357 and then asks head_ctx() for it - but the M=16 decode family is still built here: the target allocates the memory (XdnaTarget::make_ctx16) while the forward decides which decode contexts exist and configures each one field by field (MD, KD, ND, blocked_b, pb16i, t2s, planes) at q4nx_forward.cpp:1306-1330, and docs/npu-p4-target-split.md section 3 says exactly that ('still in the forward, and therefore still owed: the M=16 decode family'). So A2 has two items left: A2c moves that decode-family setup behind the target, and load_block stays a named no-op whose explicit block step is an optimisation with a tokens/s row as its evidence, not a structural move. The paragraph now says that, cites the lines, and no longer claims the forward creates no I8Ctx. Docs only; copyright --check rc 0.
|
CHANGES answered at What changed in the P4 paragraph (the
I did not touch anything else on the page, and no compiled line changed ( One note on ownership, since this branch lives in a shared worktree: I am the NPU-prefill/hybrid lane, not the plan/P4 one — I made this |
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT: READY at 611ab5a9d52e52fbf2ce77ccb747fa9696f02f8a
The revision narrows a claim that had got ahead of the code, and every reference it now makes resolves:
docs: "two of the three context sets are now **the target's** … the forward calls `open_head()`
(`q4nx_forward.cpp:1357`)"
code: npu/forward/src/q4nx_forward.cpp:1357 if (!xd_->open_head(designs_, ndesigns_, err_)) return false; ✓ exact
npu/target/xdna/xdna_target.h:158 bool open_prefill(const Q4nxDesignGeom* designs, int n, std::string& err);
npu/target/xdna/xdna_target.h:165 bool open_head(const Q4nxDesignGeom* designs, int n, std::string& err);
docs: npu-p4-target-split.md:129 "**still in the forward, and therefore still owed**: the M=16 decode family and the lm-head context"
so "two of the three" is accurate — prefill and head are the target's, the M=16 decode family is the third and is named as owed in the page this one links to. Citing q4nx_forward.cpp:1357 by line is the kind of reference that rots fastest, and it is correct today; the sentence around it ("the forward calls open_head() and then asks the target for the context") would survive a renumbering, which is the right way to hold both.
The previous version said "q4nx_forward.cpp creates no I8Ctx at all", which was too strong while the decode family is still in the forward — narrowing it to the two sets that have actually moved is the correction, and "the cache fills lazily inside gemv" plus "load_block() turning that into an explicit block step is the one A2 item left" keeps the remaining work visible with its evidence requirement ("a tokens/s row, see §3's A2c").
Not run: nothing to run. The line numbers and the third-set claim are checked above.
|
Correction to this PR's body, from a false claim I made in it. Point 3 said the setup side is the target's "end to end" and that
How I got it wrong, because the mechanism matters more than the apology: I grepped The PR's actual diff is unchanged by this: the page gains the |
What
Three statements on
docs/npu-plan.mdwere true when written and are not now — all of them about fields the loader actually reads. Docs only.designs[]. Added: the per-role kernel set the plan may name (npu plan: the plan can name the per-role kernel set, and the engine refuses to disagree with it (A2a of #803) #1330, npu plan: the generator writes the design set, and a parity test holds it to the engine's derivation (A2a2 of #803) #1333), the both-directions agreement check (designs_agree), the artifact as a bare file name, the generator writing the section only where it can be complete, andPlan::check_artifacts()refusing a missing artifact at load — withnpu_plan.py --checkasking the same question, by the same rule, at import time.layers[]row said the forward "derives designs from the container" — that is now only the no-section case; the row says so.XdnaTarget::open_prefill()(npu(p4): the target opens the M=128 ELF set, and the forward stops creating contexts (A2b, part 1) #1380) and the lm-head context inopen_head()(npu forward: cut the lm-head context out of init_impl, unchanged, so moving it is a copy (A2b, prep) #1458, npu(p4): the lm-head context is the target's, so the forward no longer creates one (A2b, last setup block) #1478), soq4nx_forward.cppcreates noI8Ctxat all. What is genuinely still open is named precisely:load_blockis a named no-op and the cache fills lazily insidegemv, and turning that into an explicit block step is an optimisation whose evidence is a tokens/s row, not a structural move (that is A2c on the split page).Why bother
This page is the plan layer's schema reference, and its table is what a reader uses to know which fields the engine honours. A field the loader parses and refuses runs on being undocumented here:
designs[]can reject a plan at load, and the page did not mention it existed.Rows
Docs only — no compiled line changed, so no build or device row is claimed.
tools/copyright.py --checkrc 0.Refs #799, #803.