Repository navigation
hrx: a view into storage the graph does not hold is skipped, not refused (Qwen3-VL decodes again; engine #1467) - #283
Conversation
…sed (engine ggml-org#1467) A deepstack model (Qwen3-VL) takes each layer's deepstack slice as a view of a view of the wide input embedding. When the first view's node is in another split, the wide tensor is not a value of the HRX graph, so the slice cannot alias its input: it has a storage of its own, no registration takes a VIEW, and the graph was refused at 'unsupported HRX node 20: VIEW ... (no_alias_source)'. That is the text model's graph, so the model failed on its own warmup, before any image. The import already classifies such a value External (its storage root is not a node of the graph). A layout op writes no bytes and no node of the graph produces these, so the scheduler now skips the node. A window registered as a transient stays refused. tests/test-hrx-external-window-view.cpp: the plan through the real importer on the sub-graph the scheduler hands over (3 slices; a transient window refused), and on HRX0 the values against the CPU. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
|
ready for review at e0f6918 |
VERDICT at
|
|
VERDICT: READY at The distinction that matters: skip here means the scheduler does not claim the node, so it falls back — not that the node is dropped from the graph. The test wording confirms it ( Why this is right rather than a workaround: the backend's stated design is "claim only nodes the dispatcher can execute, so the rest falls back to another backend instead of failing the graph". A VIEW whose storage root is not a node of the graph is not something this backend can execute — refusing it killed the whole graph and surfaced as Two asks:
One thing worth keeping in the comment: that the fault fires before any image is involved, on Qwen3-VL-2B's text warmup. That is the fact that makes this a general fix rather than a vision-model one, and it is what someone with a different model in hand needs to recognise their own failure. |
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT: READY at e0f6918 — a view into storage the graph does not hold is skipped (+19 in the scheduler with a test registered): the alternative, mapping it anyway, produces a read of memory nobody promised, and the test is what keeps the skip from becoming a silent drop of a legitimate view.
|
VERDICT: READY at The narrowing rule is right, and it is narrow in the way that matters. The predicate added is four conditions in one expression — output is The value check is the part I would not have expected and would have asked for: Three things in "What this does not do" that are worth their weight:
One thing I would ask for, and it is the same ask as on #284: a case where the window's own offset is non-zero and its consumer is not a plain ADD — the three slices here all feed Also worth noting for the engine side: if this lands, engine#1467's remaining piece is "the CLIP graph on HRX" rather than "Qwen3-VL does not run", and the text model's warmup no longer fails — that is a strictly larger effect than the title suggests, and the PR says it plainly ( Scope of my check: read the diff (the scheduler predicate, both test files), the before/after refusals, the value comparison, and the limitations. Did not build, did not run — the numbers are the PR's, taken on gfx1151 under |
Refs 1bit-MONSTER/engine#1467. This removes the failure; it does not put the CLIP graph on HRX (see "What this does not do").
The fault
At the tip, Qwen3-VL-2B fails on HRX0 before any image is involved. The text model's own warmup is refused:
So text-only decode of this model fails too (
llama-completion -m Qwen3VL-2B-Instruct-Q4_K_M.gguf --device HRX0 -ngl 99: rc 1, no output), and every image request fails with it.Why
A deepstack model has one wide input embedding,
n_embd × 4values per token. llama.cpp takes the firstn_embdvalues of each token as a view (t_inp_embd), and for each deepstack layer a view of that view, one slice further in, which is added to the layer's output (src/models/qwen3vl.cpp). The node of the first view is in another backend's split, so the wide tensor is never a value of the HRX graph. The deepstack view's bytes run past the end of the first view's, so it cannot alias it either: it gets a storage of its own, it is not an alias of its input, and no registration takes aVIEW. The scheduler refused the graph.ggml-org#1467's thread planned a contiguous copy for this node. It does not need one.
tensor_is_external()already classifies the value External, because its storage root is not a node of the graph, so it is bound from its tensor like any other external input. A layout op writes no bytes, and no node of this graph produces these bytes.The fix
dispatch-scheduler.cpp: a layout op whose output is External, on a storage of its own, recorded asAliasMiss::NoAliasSource, is skipped. Nothing else changes: a registration is still tried first, and this case was a refusal before, so no graph that planned before plans differently.A window registered as a transient (a value the graph would have to fill) stays refused;
test-hrx-view-alias-elision's hand-built case still pins that, and its comment now says so.Proof (strixhalo, gfx1151, under
gpu-hold)New
tests/test-hrx-external-window-view.cpp. It builds the graph as llama.cpp does, takes the sub-graph without the first view's node (ggml_graph_view), and runs it through the real importer and scheduler. On HRX0 it also compares values with the CPU backend, with data that names slice, token and index.Before (scheduler as at
2d62cc7af), the same refusal as the device run:After:
The model, Qwen3VL-2B-Instruct Q4_K_M +
mmproj-Qwen3VL-2B-Instruct-F16, greedy:llama-completion --device HRX0 -ngl 99, 12 tokensret = -3Paris, and the capital of Germany is Berlin. If the, byte-identical to the CPU runllama-mtmd-cli --device HRX0 -ngl 99(projector offloaded), ggml-org#1467's 448×448 test imageA red rectangle and a green circle.--no-mmproj-offloadA red rectangle and a green circle.A red rectangle and a green circle.llama-bench -p 512 -n 64 -r 3on the text model after the fix: pp512 2,330.54 t/s (sd 358.59), tg64 57.78 t/s (sd 9.21). One run on a shared box; it is here to show the model runs, not as a throughput claim.What this does not do
the CLIP graph uses unsupported operators by the backend.mtmd batch encodingtook 586 ms offloaded against 413 to 443 ms on the CPU, one run each. Operator coverage for the projector is the rest of nvcc fatal : 'f16c': expected a number ggml-org/llama.cpp#1467.llama-serveron the shipped payload; these runs arellama-mtmd-clion a dev build.GET_ROWS × 1, ROPE × 56, PAD × 1); unchanged by this PR.🤖 Generated with Claude Code