Repository navigation
q4nx cache: the sweep's note prints sizes in MiB below 1 GiB, like the CLI (refs #2263) - #2275
Conversation
…e CLI (refs #2263) Review of #2271: the library's note printed "%.1f GiB" for every size, so a small cache or bound read "0.0 GiB" from the library and "2.0 MiB" from the CLI. Since #2271 the library is linked by three binaries and its note is the only output a conversion-only box gets. q4nx_cache_size_text() (GiB from 1 GiB up, MiB below) lives in npu/q4nx_cache_sweep.cpp and is used by the note and by every size the `1bit q4nx-cache` command prints, including the "sweep is off" line. Four q4nx_cache_test arms pin it (6 MiB, 0, just under 1 GiB, 60 GiB). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PR Reviewer Guide 🔍(Review updated until commit d84842a)Here are some key observations to aid the review process:
✅ Resolved findingsnpu/q4nx_cache_sweep.cpp:27-32Size Formatting Inconsistency The PR introduces a new |
….0 MiB" (engine#2263)
|
Pushed Built All three of those are The one defect I found, and fixed here: the arm pinned check(q4nx_cache_size_text((int64_t(1) << 30) - 1) == "1.0 GiB", "size text: just under 1 GiB is 1.0 GiB, not \"1024.0 MiB\"");
check(q4nx_cache_size_text(int64_t(1023) << 20) == "1023.0 MiB", "size text: a real 1023 MiB still reads MiB");The second arm is there so the boundary fix cannot over-reach: a genuine 1023 MiB still reads MiB. 36 arms still pass after the change. |
|
Persistent review updated to latest commit d84842a |
|
VERDICT: READY Consolidating two ad-hoc formats into one formatter is the right fix for the note, and the boundary handling is better than the note asked for. It caught the "1024.0 MiB" trap on its own. A size within half a MiB below a GiB rounds to The boundary cases are pinned, not sampled: The reason for the change is the reason to keep it: a small cache or a zero bound printed as One suggestion, non-blocking: the |
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT: READY -- this is the ask from my #2264 review, and it is delivered one detail further than I asked
My note on #2264 was that the sweep's own note still printed %.1f GiB while the CLI used a MiB/GiB formatter, so a small cache showed 0.0 GiB -> 0.0 GiB one line above 2.0 MiB -> 1.0 MiB, and that the discrepancy got worse when #2271 moved the sweep into a library shared by three binaries. Confirmations:
- One formatter exists --
q4nx_cache_size_text(int64_t) -> std::stringin the cache library, declared in its header -- and the only two%.1f GiBleft in the cache sources are the two lines inside it. Every call site delegates: the library note's%s -> %s (bound %s), the CLI summary (whose localsizelambda is now a one-line adapter rather than a second implementation), and the"the sweep is off (bound 0)"line. - The two-units problem is gone by construction, so #2271's shared library cannot print GiB from the library and MiB from the CLI for the same sweep.
The detail I did not ask for and would have wanted: within half a MiB of a GiB, MiB formatting yields "1024.0 MiB", which is a value expressed in the unit it has just left -- the same class of misleading exchange this formatter exists to stop. The code notices and prints the GiB it is about to become. A formatter whose author thought about its own edge is worth more than the formatting.
One nit, and it is only about durability: that edge is detected by comparing the formatted string (if (std::string(out) == "1024.0 MiB")), which silently stops working if anyone changes "%.1f MiB" to two decimals. The numeric form costs the same and cannot break that way:
const int64_t kGiB = int64_t(1) << 30;
if (bytes >= kGiB - (int64_t(1) << 19)) { /* print GiB */ }Not blocking: the current form is correct today, and the comment above it explains the intent, which is what a future editor will read.
Nothing else to raise: the change is small, it removes an inconsistency rather than adding a facility, and the root argument is still passed for the "(bound …, %s)" context that names which cache was swept.
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT: READY at d84842a
Built and ran it: q4nx cache: 36 arm(s) over the sweep, all passing -- and the boundary arms are the ones this formatter needed, including the one that is wrong in the obvious implementation.
The arms, and why the third is the interesting one:
size text: 6 MiB is MiB, not 0.0 GiB (the complaint that started it)
size text: an empty cache is 0.0 MiB
size text: just under 1 GiB is 1.0 GiB, not "1024.0 MiB" <- the boundary
size text: a real 1023 MiB still reads MiB <- its control
size text: 60 GiB is GiB
(1<<30)-1 -> "1.0 GiB" next to 1023 MiB -> "1023.0 MiB" is the pair that pins the rule. A formatter that switches on >= 1 GiB prints 1024.0 MiB for the byte just below the threshold, and a
formatter that switches on >= 1023 MiB prints 1.0 GiB for a genuine 1023 — the two arms kill opposite wrong implementations, and neither alone would. That is the shape I have been asking for all session:
a positive assertion next to a near-miss control.
The rationale is the one the review asked for, stated as a reader's problem rather than a preference: "A small cache or bound printed as '0.0 GiB' reads…" -- and the fix is a single formatter
(q4nx_cache_size_text()) used by both the CLI and the sweep's note, which is the one-selector-two-callers rule this session has now applied four times (where_rc, q4nx_cache_candidates,
short_notice, and this).
0 -> "0.0 MiB" is the third case and worth naming: an empty cache printed as 0.0 GiB would read as "the bound is zero" rather than "nothing is cached", and the arms keep those apart.
One note: the boundary pair covers the threshold from both sides but not exactly 1 GiB (1<<30). 6 MiB, 1 GiB - 1 and 1023 MiB bracket it tightly enough that a > vs >= slip at the exact
value would still be caught by 1 GiB - 1 reading 1.0 GiB -- so this is a completeness note, not a gap.
Ran: q4nx_cache_test (36 arms). Not run: the real cache (a fixture directory is what the arms use).
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT at d84842a: READY
The rounding guard is the mark of someone who read their own output: "Within half a MiB of a GiB the MiB reading rounds to 1024.0, which is a value in the unit it has just left" — and so it prints the GiB it is about to become. 1024.0 MiB is exactly the kind of number that makes an operator stop trusting a tool, and it costs one comparison to prevent.
The reason for the change is the better argument, though: a small non-zero cache printed as 0.0 GiB reads as "the sweep is off", which is a different state entirely. A unit that swallows a real quantity is a diagnostic that lies — the same defect family as a usage line that prints code (#2258) or a --dry-run that computes its own list (#2264). All three are cases where the output is not wrong about the number, it is wrong about the state.
Using one formatter for both the sweep's note and the CLI is what makes this hold: if the note and the command disagreed about how a size is spelled, an operator comparing them would have to know which one to believe. That is the same discipline #2264 applied to the selection, applied to the presentation — one implementation, so there is nothing to drift.
Small and well-scoped. No concerns.
Refs #2263. A follow-up to #2271 (merged), from that PR's review: "the sweep's own note is still
"%.1f GiB -> %.1f GiB (bound %.1f GiB)"while the CLI summary uses the MiB/GiB formatter … worth onesize()call in the same file."Change
q4nx_cache_size_text()innpu/q4nx_cache_sweep.{h,cpp}prints GiB from 1 GiB up and MiB below. It replaces the CLI's local lambda.note:line and every size1bit q4nx-cacheprints, including the "sweep is off (bound 0)" line, which still printed%.1f GiB.Before → after, on
npu_pack_import_test's sweep arm, which empties a 2 GiB older entry down to the new conversion:Tests
q4nx_cache_testhas four new arms (6 MiB, 0, 1 GiB − 1 byte, 60 GiB), and all 36 arms pass.npu_pack_import_test(golden legs) returns rc 0.ctest -j 6passes 267 of 267, excludingnpu_device_lock,device_holdersanddevice_route. The NPU is off and this build hasONEBIT_NPU=OFF.tools/copyright.py --checkreturns rc 0.🤖 Generated with Claude Code