Repository navigation
hrx: a measured zero host figure refuses again, not 'unreadable' (review of #259) - #271
Conversation
… 'unreadable' Review point on #259 that merged without it: both call sites wrote const size_t host_raw = host_memory_available_bytes(); const size_t host_available = host_raw == 0 ? kHostUnknown : host_raw; and host_memory_available_bytes() returns 0 BOTH when /proc/meminfo cannot be read AND when MemAvailable is genuinely 0. So a host with nothing left was treated as a host whose figure could not be read: the term was dropped, in the one case the comment above those lines calls 'the worst moment to admit a launch'. The header and tests/test-hrx-pool-refusal-budget.cpp both say a measured zero must refuse. The read now says whether it read anything: bool host_memory_available_bytes(size_t & out); // false = unknown, *out untouched bool mem_available_from_meminfo(const char *, size_t & out); 'MemAvailable: 0 kB' returns true with 0; an absent field, a truncated body, a lookalike key and an unparsable number return false. The size_t forms stay for the callers that only want a number (host-memory.cpp's first-weight warning uses 0 == unknown and is unchanged). The mapping then lives in one tested place rather than in three ternaries: size_t pool_host_term(size_t raw, bool readable); // readable ? raw : kHostUnknown used by pool_reserve's live check and by the open-time budget in ggml-hrx.cpp. The pool_budget_observe() call site still passes kHostUnknown deliberately: its job is to refute or confirm a refusal cap, and adding a term there would change what a cap is allowed to mean. Tests: five arms in test-hrx-pool-refusal-budget.cpp (pass-through, MEASURED ZERO stays zero and refuses through pool_live_fits, unreadable becomes unknown and still admits on the GTT term) and six in test-hrx-host-memory-available.cpp (zero is a reading, absent/unparsable/empty/null are not, *out untouched on failure). Both suites built and run here: all requirements pass. ggml-hrx.cpp is the one file I could not compile locally (it needs hrx_runtime.h from the hrx-system submodule include path); its edit is the same two-line mapping and the engine's hrx_build is what will prove it.
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT: APPROVE. This is the right repair for a sentinel collision: not a second sentinel, not a comment, but making absence representable — bool host_memory_available_bytes(size_t & out) where false means "did not read" and *out is untouched. A value that means two things cannot be fixed by agreeing about which meaning applies; it has to stop being one value.
And the encoding now lives in one function. Both call sites read pool_host_term(host_raw, host_memory_available_bytes(host_raw)), with kHostUnknown produced in exactly one place (return readable ? raw : kHostUnknown). That is what I asked #259 for and did not get — there the answer was a comment saying the pathspec "mirrors" the other spelling; here it is structural, and the two sites cannot drift because neither of them spells the conversion.
The completeness argument is the strongest part, and it is free. Changing the signature makes every stale caller a compile error, so "did you find them all?" is answered by the build rather than by a grep. I checked anyway — hrx-1bit-safety.cpp:275 and ggml-hrx.cpp:1641 are the two, both converted — but the property I actually trust is that a third one could not survive.
And the honesty about the test is the best sentence in the PR: "the test drives the function, and the defect was in the encoding at the call site." tests/test-hrx-pool-refusal-budget.cpp promised PASS a measured zero host figure is NOT unknown: it refuses rather than falling back to GTT, and it passed while the call sites violated it — because it exercised the function rather than the path that consumes it. A test that names the property and checks a different layer is worse than no test, since it converts an open question into a belief. Fixing the arm alongside the code is the right response, and it is the second time today the same lesson has landed (ggml-org#1766's branch was likewise the one no test drove).
One line worth adding: kHostUnknown still exists as the internal sentinel, now produced only by pool_host_term. Saying so where it is defined — "only this function may produce it; a call site spelling x == 0 ? kHostUnknown : x is the bug this came from" — is the guard against the next person re-introducing exactly the idiom this PR removes.
Refs #259 (merged without this), ggml-org#1502, #255.
|
VERDICT: READY at What I got wrong in #259I checked the sentinel ( const size_t host_raw = host_memory_available_bytes();
const size_t host_available = host_raw == 0 ? kHostUnknown : host_raw; // ← collapses two casesThe header described the intent; the caller contradicted it, and The fix is the right shape
bool mem_available_from_meminfo(const char * meminfo, size_t & out); // false = unknown, out untouchedwith the discrimination spelled at the exact line that matters — a present-but-unparseable field returns The test pins the whole three-way split rather than the happy path: expect_eq("a measured host figure passes through", pool_host_term(64 * GiB, true), 64 * GiB);
expect_eq("a MEASURED ZERO stays zero, so the term refuses", pool_host_term(0, true), 0);
expect_eq("an unreadable /proc becomes unknown, so the term is dropped", pool_host_term(0, false), kHostUnknown);
expect_true("a measured zero refuses through the live check", !pool_live_fits(1*MiB, gtt, gtt - 45*GiB, margin_256, pool_host_term(0, true)));
expect_true("an unreadable /proc still admits on the GTT term alone", pool_live_fits(…, pool_host_term(0, false)));plus One line I would still addThe legacy overloads survive: |
Takes the review point from #259 that merged without it: a measured zero host figure must refuse, and both call sites turned it into "unknown".
The defect
Both sites wrote
and
host_memory_available_bytes()returns0both when/proc/meminfocannot be read and whenMemAvailableis genuinely0. So a host with nothing left read as a host whose figure could not be read — the term was dropped, in the case the comment immediately above those lines calls "the worst moment to admit a launch". The header andtests/test-hrx-pool-refusal-budget.cppboth promised the opposite (PASS a measured zero host figure is NOT unknown: it refuses rather than falling back to GTT), which is why a reader could reasonably believe it was covered: the test drives the function, and the defect was in the encoding at the call site.The fix
The read now says whether it read anything:
"MemAvailable: 0 kB"returns true with 0; an absent field, a truncated body, a lookalike key (MemAvailableX:) and an unparsable number return false. Thesize_tforms stay for callers that only want a number —host-memory.cpp's first-weight warning treats0as unknown and is unchanged.The mapping then lives in one tested place instead of three ternaries:
used by
pool_reserve's live check and by the open-time budget inggml-hrx.cpp. Thepool_budget_observe()call site still passeskHostUnknowndeliberately: its job is to refute or confirm a refusal cap, and adding a host term there would change what a cap is allowed to mean — that is a separate decision, not a bug fix.Tests
test-hrx-pool-refusal-budget.cpp, five arms: pass-through; a MEASURED ZERO stays zero and refuses throughpool_live_fits; an unreadable/procbecomes unknown and still admits on the GTT term alone.test-hrx-host-memory-available.cpp, six arms: zero is a reading; absent / unparsable / empty / null are not;*outis untouched on failure.Both suites built and run here —
all requirements passed.What I could not verify locally
ggml-hrx.cppneedshrx_runtime.hfrom the hrx-system submodule include path, so I could not compile that file here; its edit is the same two-line mapping and the engine'shrx_buildis what proves it.hrx-1bit-safety.cppandhost-memory-available.cppare compiled by the two test targets above.