Repository navigation
hrx: hrx-env names the HSA runtime, and a parity row compares CPU and device output (engine#674) - #883
Conversation
|
#883 ready for review at b1f4271 Two halves, both measured on strixhalo with one
Independent re-derivation, cheapest command: Not a close for #674 (its headline does not reproduce at the pin; ten arms of the 1-second reproducer are byte-identical). PR body has the table. |
PR Reviewer Guide 🔍(Review updated until commit 4fe4790)Here are some key observations to aid the review process:
|
b1f4271 to
104173d
Compare
|
Head moved to Plain Scope of the rest of the ticket note, stated so the review is not misled:
|
|
Persistent review updated to latest commit 104173d |
… device output (engine#674)
scripts/hrx-env.sh set the engine#715 save-area trio but not IREE_HAL_AMDGPU_LIBHSA_PATH, so a tool started
from a shell (llama-cli, llama-bench, test-backend-ops, a harness that starts llama-server itself) resolved the
*system* ROCr, which with HSA_USE_SVM=0 registers no HRX0 at all: --list-devices prints "(none)" and every arm of a
harness silently measures nothing. scripts/hw-gate.sh has always had to set that variable itself before sourcing this
file; now the recipe does it once (the same TheRock fallback serve uses), a caller's own value still wins, --check
refuses a path that does not exist, and --check names the runtime it resolved with its sha256, because engine#715's
table has three HSA runtimes on this box and a fault report is ambiguous without the name.
tests/hrx_cpu_weight_parity.sh is the row that compares placements instead of only requiring that each device answers
(engine#674): one greedy prompt through the CPU, through the device with every layer assigned to the CPU, and fully
offloaded, compared byte for byte. It skips (77) without a gfx1151 and fails, rather than skipping, when the machine has
a GPU and HRX0 did not register.
Measured on strixhalo (gfx1151), 2026-11-03:
before: source scripts/hrx-env.sh && llama-cli --list-devices -> Available devices: (none)
(ten -dev HRX0 arms of a #674 harness died in 1 s on this)
after: the same command -> HRX0: AMD Radeon 8060S Graphics (Node 1) (gfx1151) (122880 MiB, 73281 MiB free)
source scripts/hrx-env.sh --check ->
hrx-env: runtime .../_rocm_sdk_core/lib/libhsa-runtime64.so.1 sha256 21a6682452e159cf
(the sha256 prefix engine#715's table records for the runtime the engine picks by default)
row at the pin d8bd0ff6052a: cpu / weightscpu / offload all answer
"The capital of France is Paris. But wait, there's also a city called" -> PASS
row with the do-not-merge decline knob (MUL_MAT,MUL) as a test input:
weightscpu "irse ##Hmm ####maybemaybe", cpu and offload unchanged -> FAIL, as it must
104173d to
49b38bf
Compare
|
Head Also for whoever reads the checks: |
|
Persistent review updated to latest commit 49b38bf |
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT: READY
At 49b38bf5f. Both halves of the PR are gates that bite, and I ran both.
scripts/hrx-env.sh --check names the runtime on this box, which is the whole point of engine#715's ambiguity:
hrx-env: runtime /opt/rocm-therock/lib/python3.14/site-packages/_rocm_sdk_core/lib/libhsa-runtime64.so.1 sha256 21a6682452e159cf
That is the same library and the same sha256 prefix my own notes have for the TheRock nightly, so the resolution order
(baked ONEBIT_HRX_LIBHSA → /opt/rocm-therock pip layout → /opt/rocm) lands on the right one here. bash -n clean on
both scripts.
The parity row passes on the device, and it is the row #674 needed. From a full checkout of this head, under
gpu-hold -l "pi prs 883", three arms, one greedy prompt, 0.6B Q4_K_M:
cpu said: The capital of France is Paris. But wait, there's also a city called …
weightscpu said: The capital of France is Paris. But wait, there's also a city called …
offload said: The capital of France is Paris. But wait, there's also a city called …
ok the CPU answers at all
ok device with every layer on the CPU answers the CPU's text
ok device with every layer offloaded answers the CPU's text
PASS placement does not change the answer
And the trap it is built for is real — I hit it by accident and the test did the right thing. My first attempt passed a
llama-server path that did not exist; the GPU was present, so instead of skipping it failed with
FAIL the GPU is present but …/llama-server lists no HRX0
HRX needs a runtime it can open the device through: set IREE_HAL_AMDGPU_LIBHSA_PATH
which is exactly the silent-no-device failure mode I documented in this lane (IREE_HAL_AMDGPU_LIBHSA_PATH missing → empty
device list → test-backend-ops tests the CPU and exits 0). A row that fails with the variable named is worth more than
three that skip.
Live check before my re-verdicts: all six other PRs I just verdicted either merged (#878) or are being re-headed by the
waiter, so this one is the only fresh gate in the batch.
|
Persistent review updated to latest commit 978e01f |
|
Persistent review updated to latest commit 92c9317 |
|
Preparing review... |
|
Persistent review updated to latest commit 4fe4790 |
Root cause
scripts/hrx-env.shis the one place that sets up the environment for HRX processes the engine does not launchitself ("test-backend-ops, llama-bench, llama-cli, a harness that starts llama-server on its own"). It set the
engine#715 save-area trio and nothing else. On this box the trio is necessary and not sufficient: the HAL names the
HSA runtime with
IREE_HAL_AMDGPU_LIBHSA_PATHand otherwise resolves the system ROCr, which withHSA_USE_SVM=0registers no
HRX0. The failure is silent at the device-list level, so a harness measures nothing and looks like anull result:
scripts/hw-gate.shhas always had to work around this itself (LIBHSA=$(find "$TOOLCHAIN" -path '*/libhsa-runtime64.so.1' ...)then export it) before sourcing the script. This moves that into the recipe, with serve's own fallback (the TheRock
library), keeping a caller's value if it set one.
Found while running engine#674's reproducer: ten
-dev HRX0arms died in 1 s onuse --list-devices to see a list of available devices, i.e. the issue's own 1-second reproducer does not run from a shell on this box without the export.Measured before/after (strixhalo, gfx1151, 2026-11-03)
source scripts/hrx-env.sh && llama-cli --list-devicesAvailable devices: (none)— every-dev HRX0arm dies in 1 sHRX0: AMD Radeon 8060S Graphics (Node 1) (gfx1151) (122880 MiB, 73281 MiB free)IREE_HAL_AMDGPU_LIBHSA_PATH=/nonexistent.so source scripts/hrx-env.sh --check--checkrefuses (rc 3) with the reasonSecond half: the row that compares placements (engine#674)
serve_e2e.shrequires each device to answer; no row compares one placement's text with another's. #674 is exactlythat failure — a placement change produced plausible-looking garbage — so this adds
tests/hrx_cpu_weight_parity.sh, wired as ctesthrx_cpu_weight_paritynext toserve_e2e_hrx/serve_e2e_cpu:cpu—-dev none: every node on the CPU, no deviceweightscpu—-dev HRX0 -ngl 0: device present, every layer assigned to the CPU (the partial-offload path hrx: any graph whose weight ops all run on the CPU computes garbage (1 s reproducer) #674 names)offload—-dev HRX0 -ngl 99: the shipped serve placementGreedy decoding at temperature 0 must be byte-identical across all three, because placement changes where a number is
computed, never what it is. Skips (77) without a gfx1151; fails, not skips, when the machine has a GPU and
HRX0didnot register, with the
IREE_HAL_AMDGPU_LIBHSA_PATHdiagnosis (which is how the trap above gets caught rather thansilently passing).
gpu-hold, Qwen3-0.6B-Q4_K_M)d8bd0ff6052a, row as addedThe capital of France is Paris. But wait, there's also a city calledGGML_HRX_169_DECLINE=MUL_MAT,MULas a test inputweightscpusaysirse ##Hmm ####maybemaybe,cpuandoffloadunchanged, so the row reportsFAIL placement changed the answerllama-clirerun, for provenanceMUL_MAT,MUL→inaryиона this this is.(the issue's own garbage, byte-identical);noneandMUL_MAT→ correctSo the row would have caught the corruption #674 is about, and the knob is used only as a test input — it stays
unmerged and out of this PR.
What this does not do
It does not close #674. On merged code the issue's headline does not reproduce: ten arms of the 1-second reproducer at
origin/main23d6972/ pind8bd0ff6052a—-dev none,-dev HRX0 -ngl 0,-ngl 0with-otCPU pins onattn_norm/attn_q/token_embdweights,--no-op-offload,-ngl 4,-ngl 99,-fa 0, and the decline knob itself(absent from the binary) — all produce
Okay, the user is asking, byte-identical. The corrupting placement was only everproduced by the do-not-merge patch, and #173's refusal band makes the equivalent budget-driven split unreachable. This PR
adds the instrument that keeps the invariant checkable and fixes the environment trap that made its reproducer unrunnable
from a shell; the close/re-scope word on #674 is the owner's.
Refs engine#674, engine#715, engine#315.