fix(MODEL-MM-QWEN4-EXP): release a staged borrow's source pages, and stop prefaulting for a staging device - #3173
Merged
Merged
Conversation
mudler
force-pushed
the
row/MODEL-MM-QWEN4-EXP-ROCM-RESIDENCY
branch
4 times, most recently
from
September 13, 2026 04:16
969dd6f to
26427bc
Compare
The 67.56 GiB Qwen3.8-Flash-Next UD-IQ1_S loads on gfx1151 with zero op refusals and then wedges forever in svm_range_set_attr on a 31 GiB host. We call no SVM API ourselves, so the stall is host residency, and two sites compound to make it: PrefaultBorrowedSpan faults in every borrowed span synchronously, and nothing releases those pages once the device copy exists. llama.cpp does the opposite on both counts at the recorded pin. The spec also records a reversal. RocmPlatform::residency_policy() still carries the reason "on a unified part freeing the host copy after upload would free the ONLY copy". #2511 falsified that: gfx1151 reports pageableMemoryAccess = 0, the host-alias arm is not taken, and the staging branch always makes a real second copy. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5-1m [claude-code]
…stop prefaulting for a staging device ResidentWeight's staging arm copies a weight to the device and then leaves every source page of it mapped for the process lifetime. On a GGUF keep-quant load those pages are the whole model, and the load-time prefault has already faulted all of them in. On gfx1151 that is 65.488 GiB resident against a 31 GiB host, and the load wedges in svm_range_set_attr. MaybeReleaseStagedBorrowSource drops the spent source pages once the device copy exists, on a device whose kernels cannot read host memory, for a borrow whose mmap_fd says the pages are file-backed and therefore re-faultable. It is called from behind the d_dev memo, which is #1299's lesson: this function runs about 1,361 times per forward step, and an unmemoized release would madvise away the pages the GPU is about to read on every one of them. It synchronizes the queue first, because the staging copy is an async stream copy. GgufPrefaultForDevice gives the prefault the same device term QuantRepackForDevice got in #2406, and GgufLoadPolicy::FromEnv carries it into the field the loader reads. VT_GGUF_PREFAULT and vllm_cpp.mmap.prefault are answered before the device is consulted, so the same-binary A/B stays reachable on the device the default narrows. The stale reason in RocmPlatform::residency_policy() is corrected in the same change. #2511 falsified it, and leaving it in place is how the defect survived. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5-1m [claude-code]
Adding the release helper to qwen3_5_weights.h and one call line to qwen3_5.cpp shifted three cited symbols. SPEC-MTP-GGUF and SPEC-DFLASH-GGUF each moved by one line in qwen3_5_gguf_weights.cpp, and QUANT-QWEN38-27B-NVFP4-ARM's Fp8Weight moved from 652 to 719. The last one is cited as a markdown link whose label and target must both say the same number, which is why repairing only the visible half left the checker red. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5-1m [claude-code]
…as total RSS The residency case watched VmRSS, which cannot see this release. The staging branch allocates a device buffer the same size as the weight, and on the fake backend that is a malloc, so total RSS ends roughly where it started: the source pages went and an equal-sized anonymous copy arrived. The case would have been subtracting two large numbers and reading the remainder as zero. RssFile is the half a GGUF mapping contributes and the half the KFD accounting walks. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5-1m [claude-code]
…on found Deleting the host-addressable guard from MaybeReleaseStagedBorrowSource left all six new cases green. The obvious host-addressable case never reaches the helper: ResidentWeight aliases an aligned borrow and returns before the staging arm, so the guard looked tested and was not. A MISALIGNED borrow declines the alias and falls through to staging on a platform whose kernels can read host storage, which is the path the guard is load-bearing on. The new case takes it. The anonymous-borrow case grows from 96 bytes to 1 MiB in the same change. DropResidentInteriorPages madvises whole pages only, so a 96-byte buffer had no interior page and its byte assertion held whether the mmap_fd discriminator existed or not. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5-1m [claude-code]
…CTS the arm The release asked `vt::Backend::DeviceMemoryIsHostAddressable()` while `ResidentWeight` selects its alias arm on the platform's `host_memory_is_device_addressable()`. Those answer different questions and can disagree: the platform one is "may a kernel follow a host pointer" (#125, #1299), the backend one is "is a DEVICE allocation host-dereferenceable", which GB10 answers false while being physically unified. A weight reaches the staging arm on a platform that answers YES whenever a misaligned borrow declines the alias, and the release then dropped pages the kernels may still read directly. The new guard case found it: one release counted where none was allowed. The caller now passes its already-computed platform answer in and the helper refuses on either, so there is one predicate computed once. That is the move #2406 made for QuantRepackForDevice, and for the same reason: a second spelling is how a refusal and its route predicate come to disagree about one weight. gfx1151 answers false to both since #2511, so the ROCm behaviour this row measures is unchanged. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5-1m [claude-code]
…eader grew again The predicate fix added lines to qwen3_5_weights.h, so the symbol QUANT-QWEN38-27B-NVFP4-ARM cites moved from 719 to 732. Both halves of the markdown link carry the number and both are updated; repairing only the visible label is what left the checker red the first time. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5-1m [claude-code]
…exp actually takes There are two ResidentWeights and this row's model uses the other one. The release landed in the one in the unnamed namespace of qwen3_5.cpp, which shadows the header one inside that translation unit. That covers the Qwen3.5 dense weights and, through KqResidentSlice and KqGrouped, the shared MoE seam's keep-quant expert towers, which qwen4_exp does reach. It covers nothing else in qwen4_exp: every attention, norm, hyper-connection, PLE and lm_head weight stages through dense_attn::ResidentWeight instead, at 12 call sites in qwen4_exp_forward.cpp, 10 in qwen4_exp_qsa_block.cpp, 7 in qwen4_exp_ple_block.cpp and 1 in qwen4_exp_registry.cpp, with no reference to the qwen3_5.cpp function anywhere in that model. Measured with one arm wired: the load peak fell from 27.67 GB to 8.18 GB and host RssFile still climbed to 21.08 GB during the forward and stayed there. The same call now sits in the same place in dense_attn_block.h: inside the d_dev memo, immediately before AdoptDeviceBytesAsHost, with the platform's own host_memory_is_device_addressable() answer passed in rather than re-derived. Three cases repeat the release, the memo and the platform term against that seam. Deleting the new call site turns the first of them red on both its counter and its RssFile assertion: 0 releases counted, and resident file pages 138100 kB before against 138228 kB after. The replacement justification in RocmPlatform::residency_policy() was itself false and is corrected. It claimed the flag has two readers, ShouldReleaseHostWeights and ShouldInterleaveLoadStream, that both also require marlin_committed, so flipping it would change no behaviour. DirectDeviceLoadEligible (qwen3_5_dense_weights.cpp:146-180) is a third reader, requires no marlin_committed, and gates StageAndReleaseLoadedDense for every Qwen3.5-dense safetensors load. Flipping the flag WOULD change behaviour on ROCm. The comment now carries the full enumeration. Spec section 6 declared a focused selector, -tc=*resident*, that selects zero cases: no case name in the file contains that substring, so doctest reported 0 assertions and SUCCESS. It is -tc=*release* now, which selects 9 cases and 52 assertions, and the counts are printed with the evidence. Two preconditions of the helper are NOT convicted by this harness and are recorded rather than chased: deleting the backend's DeviceMemoryIsHostAddressable() term leaves the suite green because the fake backend answers false unconditionally, and deleting the synchronize leaves it green because HostBackend::Copy is a memcpy that cannot express a live DMA. ISSUE-LOCAL-01M2CCNA0S74WT5WBV50B3VD0W owns both. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5-1m [claude-code]
… survives both fixes The 67.56 GiB UD-IQ1_S produces no token on strix:gpu0 with the release wired into both ResidentWeight staging arms. Two runs, each killed at a 1200 s deadline, peak VmHWM 25.99 and 26.05 GiB, peak RssFile 20.22 and 20.35 GiB, peak RssAnon 5.55 GiB both, peak device memory 29.69 GiB both. Host RssFile during the forward is essentially unchanged from the 21.08 GB the one-arm build showed, so releasing the spent source pages is not by itself sufficient on this board. Device memory is no longer UNVERIFIED. rocm-smi is not on PATH in the leased container, so the figure is read from /sys/class/drm/card0/device/mem_info_vram_used: 154 MB at rest, 31.88 GB of the board's 33.27 GB total at the deadline. The earlier "717 MB of 103 GB" figure does not describe this board and is withdrawn. Sampling every thread's stat and wchan every 6 s, the uninterruptible thread is in svm_range_set_attr for 153 of 196 samples in run 1 and 148 of 198 in run 2, in folio_wait_bit_common for 41 and 47, and in lock_mm_and_find_vma for 3. The KFD SVM path is still the dominant blocker; the page-cache share is a CIFS confound that is named rather than separated, because the checkpoint lives on the share. That is section 7's fourth risk realised, and it makes ISSUE-LOCAL-01M2BZ5QK4XRETK48CXKSHKRDW required rather than optional. No throughput, latency or prefill number is recorded, because no token was produced. The artifact was asserted HIP-linked before it was timed. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5-1m [claude-code]
mudler
force-pushed
the
row/MODEL-MM-QWEN4-EXP-ROCM-RESIDENCY
branch
from
September 13, 2026 05:21
94aaf1e to
a6e243e
Compare
… this seam, and make every gate line reproducible A fresh review returned FAIL on two counts, and both are about what the record CLAIMS rather than about what the code does. The release logic is unchanged. THE SHARED SEAM'S REACH WAS STATED AS THE REACH WITHIN ONE MODEL. `dense_attn::ResidentWeight` is a production header, and of the 42 files that call it only four are qwen4_exp's. Every sentence in the spec, the pull request body and the eight commit bodies framed the reach as "12 call sites in `qwen4_exp_forward.cpp`, 10 in `qwen4_exp_qsa_block.cpp`, 7 in `qwen4_exp_ple_block.cpp`, 1 in `qwen4_exp_registry.cpp`" -- 30 sites, which §5 also miscounted as 31. Spec §4a now carries the enumeration, built by listing every caller of the two GGUF keep-quant borrow producers `OwnGgufQuantBlocks` and `OwnGgufF16` (the only writers of `mmap_fd`, which is the field the release requires) and tracing each one forward to its consumption site. Four other families execute the release on discrete CUDA and non-unified ROCm, so on `dgx`, `thor`, `orin` and `strix`: GLM-MoE-DSA at 24 sites in `glm_moe_dsa_forward.cpp` (its expert towers are refused one frame earlier by `GlmResidentExpertSlice`, its MLA, router, norms, rope cache, embedding and head are not), GLM5-Next at `glm5_next_moe.cpp:243-245`, Muse-Glimmer at twelve sites in `muse_glimmer.cpp` plus `muse_glimmer_mm.cpp:264`, and the Qwen3.5 DFlash draft head, whose `EmbedTable()` is the target's kept-F16 GGUF table after the rebind in `model_loader.cpp`. DeepSeek-V4 and Laguna have GGUF loaders and were checked and EXCLUDED, with the reason recorded so nobody re-derives them. NO TEST COVERS ANY FAMILY BUT QWEN4-EXP, and that is recorded as owed on ISSUE-LOCAL-01M2CKN5516AKE7W2JVDV86Z8X rather than answered with a case that would prove nothing. The focused harness's fake backend implements memory operations only, so another family's entry point refuses on a missing op before residency is asked about, and calling the seam directly with another family's tensor shape would add a family's name and not a family's call site. The same reasoning is why the one cheap-looking repair was NOT made: giving the header `ResidentWeight` the `VT_CHECK(!w.expert_streamed)` its translation-unit-local twin has would make the shared seam refuse a weight it accepts today, on paths no test covers. That hazard, `ResidentWeightF32`'s independent `d_dev_f32` memo and GLM5-Next's host-fallback re-read are owed on ISSUE-LOCAL-01M2CKNHVKJXT29XN8WM11KF6W. §6 SHIPPED A SECOND SELECTOR THAT MATCHES NOTHING, on the line edited to repair the first. `-tc=*DSA*` against `test_resident_weight_host_addressable` reports 0 cases, 0 assertions and `Status: SUCCESS!`; `DSA` selects two cases in `test_backend_cross_device`, which is a separate line, and the section named no binary at all. Every selector now names its binary and carries the case and assertion counts it actually produced, measured on `strix:gpu0` under rc job f8a6ceec-d8bf-4e99-b6e4-3b2cbf71f8b9: focused binary 28/139 whole, `*release*` 9/52, `*prefault*` 2/11, `*dense_attn*` 3/19, and cross-device 60/84833 whole with `*DSA*` 2/273. The rest of the section was then swept for a third dud and the sweep is reported rather than asserted: the four checkers are the only other gate lines, none accepts or defaults to a selector, and each was run with no arguments to confirm it refuses rather than passing empty. Each now carries the invocation that measures the branch instead of the one commit at HEAD. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5-1m [claude-code]
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
fix(MODEL-MM-QWEN4-EXP): release a staged borrow's source pages, and stop prefaulting for a staging device
On gfx1151 the 67.56 GiB Qwen3.8-Flash-Next UD-IQ1_S loads with zero op
refusals and then wedges forever in
svm_range_set_attron a ~31 GiB host.We call no SVM API ourselves, so the stall is host residency, and two sites
compound to make it.
Every borrowed GGUF span is prefaulted synchronously at load, and nothing
releases those pages once the device copy exists. For a keep-quant load the
source pages are the whole model: 65.488 GiB faulted in and read exactly once.
ReleaseHost()'s borrowed branch declines to touch them, arguing that cleanfile-backed pages are reclaimable without our help. That holds against the page
reclaimer and fails against the KFD, which walks what this process has resident.
At the pinned
llama-cpporacle10bf611e5(b10451) the same model isO(one tensor) resident during load and zero after:
llama-model-loader.cppgrows
mmap_usedonly for tensors landing in a HOST buffer, thenunmap_fragment(0, mmap_used.first)— a realmunmap— drops the whole mappingfor a fully offloaded model. Its prefetch is
MAP_POPULATEplus an advisoryposix_madvise, with no synchronous touch loop.Two changes.
MaybeReleaseStagedBorrowSourcedrops the spent source pages after the stagingupload, on a device whose kernels cannot read host memory, for a borrow whose
mmap_fdsays the pages are file-backed and therefore re-faultable. The borrowitself is untouched and stays a valid
PROT_READ MAP_PRIVATEview, so a laterread re-faults identical bytes. It is called from behind the
d_devmemo, whichis #1299's lesson:
ResidentWeightruns about 1,361 times per forward step onthis checkpoint, and an unmemoized release would madvise away the pages the GPU
is about to read on every one of them. It synchronizes the queue first, because
the staging copy is an async stream copy.
THERE ARE TWO
ResidentWeights AND THE CALL IS IN BOTH. The first revision ofthis branch wired only the one in the unnamed namespace of
qwen3_5.cpp, whichshadows the header one inside that translation unit. That covers the Qwen3.5
dense weights and, through
KqResidentSliceandKqGrouped, the shared MoEseam's keep-quant expert towers, which
qwen4_expdoes reach. It covers nothingelse in
qwen4_exp: every attention, norm, hyper-connection, PLE and lm_headweight stages through
dense_attn::ResidentWeightinstead, at 12 call sites inqwen4_exp_forward.cpp, 10 inqwen4_exp_qsa_block.cpp, 7 inqwen4_exp_ple_block.cppand 1 inqwen4_exp_registry.cpp-- 30 in all, whichan earlier revision of the spec miscounted as 31 -- with no reference to
the
qwen3_5.cppfunction anywhere in that model. A fresh review measured theconsequence: the load peak fell from 27.67 GB to 8.18 GB and host
RssFilestillclimbed to 21.08 GB during the forward and stayed there. The same call now sits
in the same place in
dense_attn_block.h— inside thed_devmemo, immediatelybefore
AdoptDeviceBytesAsHost, with the platform's ownhost_memory_is_device_addressable()answer passed in rather than re-derived.THE SEAM IS SHARED, AND FOUR OTHER MODEL FAMILIES EXECUTE THE RELEASE. Counting
qwen4_exp's 30 call sites states the reach WITHIN this row's model, not the reach
of the change:
dense_attn::ResidentWeightlives in a production header, and ofthe 42 files that call it only four are qwen4_exp's. Nothing in an earlier
revision of this body, the spec or the commits said so. Spec §4a now carries the enumeration,
built by listing every caller of the two GGUF keep-quant borrow producers —
OwnGgufQuantBlocksandOwnGgufF16, the only writers ofmmap_fd— andtracing each forward to its consumption site. The release fires wherever a
BORROWED span with
mmap_fd >= 0meets a platform and a backend that cannotdereference host memory, which is discrete CUDA and non-unified ROCm:
dgx,thor,orinandstrix, the whole measurement fleet. Besides qwen4_exp thatis GLM-MoE-DSA (
glm_moe_dsa_forward.cpp:162-186,:269,:334— its MLA,down_proj, router, norms, rope cache, embedding and head weights across 24sites; its 228 routed-expert towers are refused one frame earlier by
GlmResidentExpertSlice), GLM5-Next (glm5_next_moe.cpp:243-245),Muse-Glimmer (twelve sites in
muse_glimmer.cppplusmuse_glimmer_mm.cpp:264)and the Qwen3.5 DFlash draft head, whose
EmbedTable()is the TARGET's kept-F16GGUF table once
model_loader.cpprebinds it, atqwen3_dflash.cpp:597,:906,:1851and:1956. DeepSeek-V4 and Laguna have GGUF loaders and were checkedand EXCLUDED with the reason recorded, so nobody re-derives them.
NO TEST COVERS ANY FAMILY BUT QWEN4-EXP, AND THAT IS RECORDED AS OWED RATHER
THAN CLOSED.
ISSUE-LOCAL-01M2CKN5516AKE7W2JVDV86Z8Xowns it. The focusedharness's fake backend implements memory operations only and registers no
Embedding,MatmulBTorRmsNormforkXPU, so a GLM5-Next or Muse-Glimmerentry point refuses on a missing op long before residency is asked about, and
its process-global registrar cannot hold a second fake backend beside the
existing one. A case that called the seam directly with another family's tensor
shape would add a family's NAME and not a family's CALL SITE. Spec §4a also says
why the one repair that looked cheap was NOT taken: giving the header
ResidentWeighttheVT_CHECK(!w.expert_streamed)its translation-unit-localtwin at
qwen3_5.cpp:1181already has would make the shared seam REFUSE aweight it accepts today, and GLM5-Next stages three expert banks through it with
no test on any staging device — so the one-liner could remove a working path
instead of closing a hazard, and no gate here would say which. That hazard and
two more second-reader hazards are owed on
ISSUE-LOCAL-01M2CKNHVKJXT29XN8WM11KF6W.SPEC §6 NOW NAMES A BINARY FOR EVERY SELECTOR AND PRINTS ITS COUNTS. A second
fresh review found that the focused gate line still carried a selector that
matches nothing —
-tc=*DSA*againsttest_resident_weight_host_addressablereports 0 cases, 0 assertions and
Status: SUCCESS!, becauseDSAmatches twocases in
test_backend_cross_deviceand the section named no binary. That isthe same failure
-tc=*resident*already caused on this branch, twice in tworounds, so every remaining gate line in the spec was swept for a third and the
sweep is reported rather than asserted: the four checkers are the only other
gate lines, none of them accepts or defaults to a selector, and each was run
with no arguments to confirm it refuses rather than passing empty. Measured on
strix:gpu0under rc job f8a6ceec-d8bf-4e99-b6e4-3b2cbf71f8b9: the focusedbinary is 28 cases / 139 assertions whole,
-tc=*release*9/52,-tc=*prefault*2/11,
-tc=*dense_attn*3/19, andtest_backend_cross_deviceis 60/84833 wholewith
-tc=*DSA*2/273.GgufPrefaultForDevicegives the prefault the same device termQuantRepackForDevicegot in #2406, andGgufLoadPolicy::FromEnvcarries itinto the field the loader reads; before this, every call site passed a literal
true.VT_GGUF_PREFAULTandvllm_cpp.mmap.prefaultare answered before thedevice is consulted, so the same-binary A/B stays reachable on the device the
default narrows.
The stale reason in
RocmPlatform::residency_policy()is corrected in the samechange. It still said that freeing the host copy on a unified part would free the
only copy; #2511 falsified that, and leaving it in place is how this defect
survived. The flag itself stays false — but NOT because flipping it would be
inert, which is what an earlier revision of this body and that comment both
claimed.
release_host_weights_after_uploadhas three readers, not two.ShouldReleaseHostWeightsandShouldInterleaveLoadStream(
platforms/interface.h:88-96) do also requiremarlin_committed, which no ROCmpath sets.
DirectDeviceLoadEligible(qwen3_5_dense_weights.cpp:146-180) doesnot: it returns the flag as its last term and gates
StageAndReleaseLoadedDenseat
:1205-1212for every Qwen3.5-dense safetensors load, behindneeds_weight_staging(), which is true on ROCm. Flipping the bit WOULD changebehaviour on this board, on a path nobody has measured there. That is the reason
it stays false, and the enumeration is the whole of
grep -rn release_host_weights_after_upload src include tests.Two preconditions of the helper are not convicted by this tree's harness and are
recorded rather than chased. Deleting the backend's
DeviceMemoryIsHostAddressable()term leaves the focused suite green, becausethe fake backend answers false unconditionally and no case presents the
disagreeing pair. Deleting
backend.Synchronize(queue)leaves it green, becauseHostBackend::Copyis a synchronousmemcpythat cannot express a live DMA.Both are owed on
ISSUE-LOCAL-01M2CCNA0S74WT5WBV50B3VD0W; neither test wasweakened or contorted to manufacture a conviction.
THE MODEL GATE IS MEASURED AND THE STALL SURVIVES BOTH FIXES. On
strix:gpu0(gfx1151, ROCm 7.2.4, 30 GiB host) the 67.56 GiB UD-IQ1_S produces no token with
the release wired into both arms. Two runs, each killed at a 1200 s deadline:
peak
VmHWM25.99 and 26.05 GiB, peakRssFile20.22 and 20.35 GiB, peakRssAnon5.55 GiB both, peak device memory 29.69 GiB of the board's 30.99 GiBboth. Host
RssFileduring the forward is essentially unchanged from the 21.08GB the one-arm build showed. Sampling every thread every 6 s, the uninterruptible
thread is in
svm_range_set_attrfor 153 of 196 samples in run 1 and 148 of 198in run 2, in
folio_wait_bit_commonfor 41 and 47. Device memory was read from/sys/class/drm/card0/device/mem_info_vram_usedbecauserocm-smiis not onPATHin the leased container; the earlier "717 MB of 103 GB" figure does notdescribe this board and is withdrawn. The artifact was asserted HIP-linked before
it was timed. No throughput or latency number is recorded, because no token was
produced.
Chunked H2D through a pinned bounce buffer (llama.cpp's 4 x 64 MiB shape) is
deliberately NOT built here. That result makes it REQUIRED rather than optional,
and it has its own issue: it touches every staged weight on every backend and
needs its own measurement.
FOLLOWING_AGENTS_PROTOCOL
Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]