From bd3121a4e83afc5cecb2115769d86b6dad038dce Mon Sep 17 00:00:00 2001 From: t Date: Tue, 10 Nov 2026 00:22:09 -0400 Subject: [PATCH] npu-pack: put the 1.9 canonical .fconst copy on disk, not in the temp directory The canonical copy is a cache -- reused when size, mtime and the rules version all match -- but its size is the pack's own .fconst, and it was written to fs::temp_directory_path() first. On strixhalo /tmp is tmpfs, so that is RAM, per pack, held until reboot: three h4096 imports from one afternoon left 11.4 GB of tmpfs behind (/tmp/1bit-fconst19-20133236-4096-32-32.fconst and two siblings), which is a large part of why the box was down to a few GiB available twice that evening. Order is now: 1. ONEBIT_FCONST_SCRATCH, when set (a lane's own scratch dir, or a test's temporary one); 2. $XDG_CACHE_HOME/1bit/fconst19, else $HOME/.cache/1bit/fconst19 -- created if missing, which is the part the first version got wrong: without create_directories() the new first choice never wins on a fresh box, because it does not exist and the loop walked straight past it back to /tmp. Caught by testing the default path as well as the override; 3. the temp directory, then /tmp, then the current directory -- the existing fallbacks, kept for a box with no HOME. Nothing about the cache key changes, so an existing copy in the old location is simply not found and one is written in the new place; the note that already says where the copy went still prints. Measured, both arms: default -> ~/.cache/1bit/fconst19/1bit-fconst19v1-...-1536-12-28-995712640-....fconst ONEBIT_FCONST_SCRATCH=/tmp/scratchtest -> /tmp/scratchtest/1bit-fconst19v1-... (same name) npu_pack_import_test: all npu-pack checks passed, 0 FAIL --- npu/pack_import.cpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/npu/pack_import.cpp b/npu/pack_import.cpp index 331eae0ba..c1fc3aad8 100644 --- a/npu/pack_import.cpp +++ b/npu/pack_import.cpp @@ -717,8 +717,19 @@ std::string materialise_token_fconst(const std::string& pack_dir, const Dims& di // 1.9 pack failed with `filesystem error: temp_directory_path: No such file or directory [/tmp/x]` and no hint that // the pack was fine. Fall back rather than fail: /tmp, then the current directory. The copy is scratch, and a note // says where it went so nobody wonders what that 291 MB file is. + // Where the canonical copy lives. This is a CACHE (reused on size+mtime+rules version), not a leak, but its size is the + // pack's fconst -- 3.6 GB for an h4096 pack -- and on a box whose /tmp is a RAM disk that is RAM, per pack, held until + // reboot: three of them from one afternoon's imports were 11.4 GB of tmpfs (measured 2026-11-10). So the order is: + // an explicit override, then the on-disk cache directory, and only then the temp directory this used to prefer. std::vector dirs; + if (const char* scratch = std::getenv("ONEBIT_FCONST_SCRATCH"); scratch && *scratch) + dirs.emplace_back(scratch); + if (const char* xdg = std::getenv("XDG_CACHE_HOME"); xdg && *xdg) + dirs.emplace_back(fs::path(xdg) / "1bit/fconst19"); + else if (const char* home = std::getenv("HOME"); home && *home) + dirs.emplace_back(fs::path(home) / ".cache/1bit/fconst19"); try { + // Last resort only: on strixhalo /tmp is tmpfs, so this costs RAM equal to the pack's fconst. dirs.push_back(fs::temp_directory_path()); } catch (const std::exception& e) { std::fprintf(stderr, @@ -730,7 +741,13 @@ std::string materialise_token_fconst(const std::string& pack_dir, const Dims& di std::string work; for (const auto& d : dirs) { std::error_code ec; - if (!fs::is_directory(d, ec) || ec) continue; + // The cache directory is ours to create: without this the new first choice never wins, because it does not exist on a + // fresh box and the loop walked straight past it to /tmp (caught by testing the default, not the override). + if (!fs::is_directory(d, ec) || ec) { + std::error_code made; + fs::create_directories(d, made); + if (made || !fs::is_directory(d)) continue; + } const fs::path cand = d / name; // Writable in fact, not just in theory: the copy is written immediately after this. std::ofstream probe(cand, std::ios::binary | std::ios::app);