Skip to content

docs(npu): the head-image cache is swept with the Q4NX cache, as one entry (engine#1716, #1956) - #2025

Merged
bong-water-water-bong merged 1 commit into
mainfrom
1bit/head-image-docs-sweep
Nov 10, 2026
Merged

bong-water-water-bong merged 1 commit into
mainfrom
1bit/head-image-docs-sweep

Conversation

@bong-water-water-bong

Copy link
Copy Markdown
Collaborator

Docs-only correction to the head-image cache paragraph I wrote in #1950. Refs #1716, #1956.

docs/npu.md says of the head images: "nothing prunes them, the same as the device-form copies next to them". That stopped being true when #1973 merged: q4nx_cache_evict() (app/q4nx_cache.cpp) walks every directory directly under the cache root, and head-images/ is one of them, so it is counted against ONEBIT_Q4NX_CACHE_MAX_GIB and removed whole when it is the least recently written entry over the bound.

The paragraph now says that, and says what the directory's age is: the last tile written into it. A serve that only reads its tiles does not refresh it, so a head-image directory in daily read-only use can still be the one evicted; the cost is one repack of the head on the next serve.

How I know: read from the code on main (fs::directory_iterator(root), is_directory, last_write_time of the entry). I did not run a sweep against a live cache for this PR. On this box the directory is 7.5 G in 143 files a few hours after #1950 merged, which is why the sentence matters.

No behaviour change. Whether a head-image hit should refresh the directory's age is a separate question I have left alone here; cl-review has an open note on the sweep's coverage under #1956.

🤖 Generated with Claude Code

…entry (engine#1716, #1956)

docs/npu.md still said nothing prunes the head images. Since #1973 the Q4NX cache sweep walks every directory under the
cache root, and head-images/ is one of them: it is counted and removed whole when it is the least recently written entry
over the bound. Say so, and say what its age is (a tile write, not a read).

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

Copy link
Copy Markdown
Collaborator Author

ready for review at 5f61774

@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 5f61774: READY

Checked at 5f61774 (docs only) against main's code. The corrected paragraph is right:

  • q4nx_cache_evict() iterates the directories directly under the cache root, so head-images/ is one entry, counted against ONEBIT_Q4NX_CACHE_MAX_GIB and removed whole.
  • Its age is its own mtime, which a new tile written into it moves and a read does not. q4nx_cache_touch() is called only on the GGUF-repack hits (app/serve.cpp:2966, :3045, :3049), never on a head-image read.

One clause would make it complete. Not blocking: say when a sweep runs. q4nx_cache_evict() is called only from repack_gguf() / repack_gguf_lane() (app/serve.cpp:3068, :3138), i.e. when 1bit serve repacks a GGUF. A box that only imports OGA packs and serves their containers never sweeps, so head-images/ (7.5 G here, as you note) and the device-form entries grow unbounded there. That's the gap I recorded under #1956. As written, "the Q4NX cache sweep … removes it whole when the cache is over its bound" reads as if it happened on every serve.

@bong-water-water-bong
bong-water-water-bong merged commit 8e9e871 into main Nov 10, 2026
8 of 9 checks passed
@bong-water-water-bong
bong-water-water-bong deleted the 1bit/head-image-docs-sweep branch November 10, 2026 21:37
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.

1 participant