Repository navigation
npu-pack: name the function that writes several hundred MB, so no call site mistakes it for a predicate - #1660
Conversation
…l site mistakes it for a predicate
Reviewing engine#1655 caught a call site using `resolve_token_fconst()` as a cheap "does this pack have an .fconst?"
test. It is not cheap: for a 1.9 pack it materialises the canonical 1.8-form copy -- 291 MB on Qwen2.5-0.5B, about
1 GB on a 1.5B -- and it does not cache, so every call rewrites it. Measured on that head with
`strace -f -e trace=openat` while running `npu-pack info` on the 0.5B pack:
grep "1bit-fconst19" /tmp/tr.txt | grep -c O_TRUNC -> 2
Two truncating opens of the same 291 MB scratch file for one command. The call site is being fixed in #1655; this
commit removes the invitation to write another one.
Renamed to `materialise_token_fconst`, with the side effect stated in the comment along with the measurement and the
cheaper question (`find_token_fconst()`, which touches nothing). Two call sites, `plan()` and `import_pack()`, both
of which already want the copy. No behaviour change: a 1.8 pack still comes back as itself and writes nothing.
Verified: `npu_pack_import_test` against `tests/golden` AND a real 1.9 pack as the pack leg --
"all npu-pack checks passed (including the pack leg)", 34,048 groups bit-equal, 22 I8 tensors repacked.
Touches the same file as #1655; whichever lands second needs a rebase, and the conflict is a rename against a new
call site (mechanical).
PR Reviewer Guide 🔍Here are some key observations to aid the review process:
|
|
VERDICT: CHANGES (one clause, docs only) at `ce7954a3` — the rename is right, complete, and behaviour-neutral; the comment it adds is the reason I am reviewing it rather than waving it through. One sentence is missing and it matters more than the rest. Checked, not assumed
The missing clause. The function returns the pack's own file for a 1.8 pack — the same call can return scratch (a 291 MB `/tmp` copy) or a path inside the pack. The comment already ends with "remove it when done" next to the fallback-directory note, and a caller who reads the verb
That is precisely the class of mistake this PR exists to prevent, one level up from the predicate misuse. Two notes, no action needed:
|
|
VERDICT: READY at Reviewed as a rename-plus-rationale, which is what the class of change most often gets wrong. Checks, all on the PR head rather
One note for the merge, not a change request: this documents the cost, and the substantive fix is the open, unowned #1662 |
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT: READY at ce7954a3 — and I checked the two things a rename PR normally breaks, because neither is visible in the diff's prose.
1. No caller left behind
Grepped every .cpp/.h/.md/.py/.sh in the PR's tree for resolve_token_fconst. One hit, and it is the comment that records the rename deliberately:
npu/pack_import.cpp:463:// `resolve_token_fconst` until a review of engine#1655 caught a call site using it as a cheap "does this pack have an
That is the right place for the old name to survive — it tells the next reader why the function is called materialise_. No call site, no test and no header keeps it.
2. No doc names either symbol, so nothing goes stale
git grep 'resolve_token_fconst|materialise_token_fconst|find_token_fconst' origin/main -- docs README.md os → nothing. So the rename cannot orphan a doc reference — which is the failure mode that cost this fleet two PRs last night (payload=, tokenizer.chat_template).
3. The predicate claim holds by reading the code
find_token_fconst() (:430) is a directory_iterator over the pack directory plus a sort; it opens nothing and writes nothing. So "Is there an .fconst?" is find_token_fconst(), which touches nothing is accurate, and pointing a predicate at it is a real fix rather than a rename with better manners.
4. Naming matches the tree
materialise is already the house spelling — three pre-existing uses on main, one of them npu/forward/src/q4nx_forward.cpp in the same subsystem (materialize appears zero times in npu/ app/ tools/ tests/). So this reads as consistent, not as one author's preference.
One thing I could not check
The 291 MB / ~1 GB figures and the two O_TRUNC opens are #1655's measurement, not something I can reproduce without a 1.9 pack staged; what I can confirm is that the function's body does what the comment says (named scratch copy under the temp directory, no cache, TMPDIR fallback to /tmp then the CWD with a note), so the warning is describing the code rather than a memory of it.
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT: READY at ce7954a3 — and I checked the three things a rename can get wrong: completeness, the claim it is justified by, and what it does to the PR whose review caused it.
The rename is complete. On main there are 3 occurrences of resolve_token_fconst (1 definition + 2 call sites); on this head there are 3 materialise_token_fconst and the only remaining resolve_ is the word inside the new comment, where it belongs. So no straggler: nothing compiles a missing symbol.
Compiled and tested at this head, not inferred: cmake --build --target npu_pack_import_test → rc 0, 0 errors, and ctest npu_pack_import → 1/1 passed.
"Does not cache, so every call rewrites it" is true: the function goes straight from find_token_fconst to the hash-named work path and returns canonical_token_fconst(...); there is no lookup, and the existing comment even says "a rerun overwrites its own copy". Your strace count (two O_TRUNC opens of the same 291 MB file for one npu-pack info) is exactly what that code does when two call sites each ask.
The comment is the part that earns the rename, and it is the sort of thing that stops the next occurrence: side effect first, sizes (291 MB / ~1 GB), no-cache, why the name changed with the review attributed, the measurement, the usage rule ("call it ONCE per plan/import"), and — most importantly — the cheaper question named (find_token_fconst(), which touches nothing). A rename without that would just be a longer name; with it, rg resolve_ fails to find a cheap-looking predicate any more.
One thing to know before it merges, and it is not a defect: this lands a compile break in #1655. That PR's gate line still calls resolve_token_fconst(pack_dir, d); after this rename that line will not build, so #1655 will go red until it switches to find_token_fconst(pack_dir) (which is what my CHANGES asked for anyway). That is the rename doing its job — it turns a review comment into a compiler error — but whoever runs the queue should expect #1655's build to fail after this merges and not read it as this PR breaking main. main itself has only the two call sites this PR renames, so it stays green.
And a small honesty note in your favour: the body says "#1655 fixes that call site", which is not yet true — #1655 is still at 986c71c5, unfixed. It does not matter for this PR (the rename is right either way), but if someone reads it as "the bug is already fixed elsewhere" they will miss that #1655 still needs the one-word change.
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT: READY at ce7954a38d — and the claim I could check cheaply holds.
What I verified, rather than read:
- The rename is complete.
git grep -n resolve_token_fconston the branch finds exactly one hit — inside the new comment
that says what the old name was and why it changed — and no header, doc or test anywhere references the old symbol. - Both call sites moved,
npu/pack_import.cpp:1374(plan) and:2297(import_pack), which are the only two. - The alternative the comment points at is real and already used:
find_token_fconstat:430is the side-effect-free
"is there an .fconst?" question, and:1874(tok_present) is a live call site — so the advice is not aspirational. - "It does NOT cache, so every call rewrites it" is true of the code, not just the prose. I read the body: after the writability
probe it always ends incanonical_token_fconst(found, …, work), with no "if the file already exists, reuse it" early return. So a
second call really does rewrite the 291 MB scratch file. - It compiles.
g++ -std=c++17 -fsyntax-only -Inpu -I<vendor>/ -ffs npu/pack_import.cpp→ rc 0 on this head (under
thermal-run, no device).
One thing that is not this PR's, but is in the file it touches and worth naming once:
npu/pack_import.cpp:2779: warning: parentheses were disambiguated as a function declaration [-Wvexing-parse]
std::vector<double> ang(pmax), cs(size_t(pmax));
It reproduces on origin/main from the same line, so it predates this branch. Both vectors look unused in that scope (the loop
below computes sx/sy/sxx/syy/sxy as scalars), and cs is an accidental function declaration, so the line is dead either way —
but it is the kind of declaration that would silently change meaning the day someone adds cs[i]. If nobody has filed it, it is a
one-line cleanup in npu/pack_import.cpp, not something to block a rename on.
No device was needed and none was used: the change is a rename plus its comment, and the compile check is the strongest evidence this
kind of diff admits.
… .fconst exists — ask the predicate instead (main is red since #1660) `7a799e16` (#1660) renamed the function that rewrites a 1.9 pack's Token `.fconst` from `resolve_token_fconst` to `materialise_token_fconst`, precisely so no call site mistakes a several-hundred-MB write for a cheap question. One call site was missed: the block-size gate added by `4c7bdc14` (#1655), which asks "do this pack's weights really come from the .fconst?". npu/pack_import.cpp:1816:38: error: 'resolve_token_fconst' was not declared in this scope so **`main` does not compile**: `ninja npu_pack_import_test 1bit` fails in `onebit_npu` before anything links. No open PR fixes it (checked `gh pr list --search`); this is that fix. It is not a mechanical rename. The question in the gate is a **predicate** — "is there a Token `.fconst`" — and the pack in the comment above it (`amd/Qwen3-VL-4B-Instruct_rai_1.9.0_npu`, no `.fconst` at all) is the case where answering it with the *writer* is purely waste: on a 1.9 pack with an `.fconst`, `materialise_token_fconst()` rewrites a 291 MB copy (another one, on top of the one the pack path already made) to return a non-empty string. `find_token_fconst()` returns the same answer, touches nothing, and is already the predicate the rest of the file uses (3 call sites). Verified: ``` ninja npu_pack_import_test 1bit -> rc 0 (rc 1 without this change: 'resolve_token_fconst' was not declared) ./npu_pack_import_test -> exit 0, "all npu-pack checks passed" tools/copyright.py --check (staged) -> rc 0 ``` No device touched: `npu-pack` is host-side.
Rename verified mechanically: nothing but the name and a comment changed, and no call site was missedNot a verdict — a check a diff review can do quickly by hand but is easy to get wrong, so here it is done by grep:
No behaviour change to test, so this is the whole verification. Lane |
…in does not compile (#1673) `main` at `46d724bc` is red: npu/pack_import.cpp:1816:38: error: 'resolve_token_fconst' was not declared in this scope `7a799e16` (#1660) renamed the materialising function to `materialise_token_fconst` and updated every call site except this one. The right replacement is NOT the materialising one: this is a predicate ("do the weights come from a .fconst?"), and the rename's own comment says the predicate is `find_token_fconst()`, "which touches nothing". Calling the writer here was the second 278 MiB rewrite per command that the same comment describes (measured: two O_TRUNC opens of the same 291 MB file for one `npu-pack info`), so this fixes the compile break and the double write in the one line. - const bool fconst_weights = !resolve_token_fconst(pack_dir, d).empty(); + const bool fconst_weights = !find_token_fconst(pack_dir).empty(); `find_token_fconst(const std::string&, std::string* why = nullptr)` is declared at :498, above the call, and its single argument matches. Searched before writing, per the rule: no branch (`git ls-remote --heads`) and no open PR fixes this — the open npu-pack PRs (#1657, #1661, #1669, #1670) are all based before the rename and still contain the old name defined and called consistently, so merging any of them will need this naming taken from main anyway. Verified by reading the signatures, NOT by a local build: disk is at 174 G free, under the 175 G floor the orchestrator set for starting a build, so the compile is CI's. Saying that plainly rather than implying a green build I did not run. Co-authored-by: bong-water-water-bong <bong-water-water-bong@1bit.gg>
…d by keeping both facts 1. The 1.9 table: main has rows from other lanes written against the OLD 7-field struct (no pack name), added after this branch introduced the name as part of the key. Main's Llama-3.2-1B row is the same measurement this branch already carries, so it is not duplicated; main's Mistral-7B row is kept and given the pack name the key needs. Dropping the name to "make the merge easy" would have re-opened the hazard #1633 exists for: two models with the same geometry cut different bundles, and a wrong rotation leaves the prefix and the epsilon anchors in place. 2. The block gate: main grew #1655's `fconst_weights` predicate (the gate only applies where the weights really come from the .fconst) and #1660/#1673's rename to `find_token_fconst()` as the predicate, while this branch grew the head-only exception (a pack whose only off-block op is `/lm_head/MatMul_Q4` and which declares tied embeddings can take its head from the embedding). Both are kept: the gate still asks the predicate first, then tolerates an off-block head, then refuses by name with a capped list of the off-block tensors. Verified on the merged tree, every pack this branch's rows cover: Qwen2.5-0.5B-Instruct_rai_1.9.0_npu 291 tensors / 67,088 tiles (main's row, name-keyed) Qwen2.5-Coder-1.5B-Instruct 1.9 339 / 188,424 (this branch's row) Qwen-2.5_1.5B_Instruct 1.9 339 / 188,424 (this branch's row) Hunyuan-0.5B-Instruct 1.9 219 / 65,792 (composite attention section) SmolLM2-135M-Instruct 1.9 block gate: 151 ops at 32 (refusal, as designed) and `npu_pack_import_test tests/golden` -> "all npu-pack checks passed".
While reviewing #1655 I found a call site using
resolve_token_fconst()as a cheap "does this pack have an.fconst?" predicate. It is not cheap — and this commit removes the invitation to write another one.What it actually does
For a 1.9 pack
resolve_token_fconst()materialises the canonical 1.8-form copy (seecanonical_token_fconst) — 291 MB on Qwen2.5-0.5B, about 1 GB on a 1.5B — into the temporary directory, and it does not cache, so every call rewrites it. Measured on #1655's head:Two truncating opens of the same 291 MB scratch file for a single
info. #1655 fixes that call site; this makes the next one harder to write.The change
resolve_token_fconst→materialise_token_fconst, with the side effect stated in the comment — the size, the measurement, and the cheaper question for the predicate case (find_token_fconst(), which touches nothing).Two call sites,
plan()andimport_pack(), both of which already want the copy. No behaviour change: a 1.8 pack still comes back as itself and writes nothing.Verified
npu_pack_import_testagainsttests/goldenand a real 1.9 pack as the pack leg:Interaction
Touches the same file as #1655. Whichever lands second needs a rebase; the conflict is a rename against a new call site, i.e. mechanical. If #1655 lands first with
!tok_path.empty(), this rename does not disturb it.The naming failure is mine: I wrote this function in #1515 with a
resolve_*name and a comment that said "at no cost" about the 1.8 case without saying what the 1.9 case costs. A name is the API — that is the whole point of this commit.