Repository navigation
Stage HIP device weight uploads through a bounded host buffer - #1070
Merged
0xShug0 merged 1 commit intoOct 25, 2026
Merged
Conversation
GGUF tensor uploads into HIP device buffers now copy through a 64 MiB host staging buffer instead of handing the memory-mapped model file straight to ggml_backend_tensor_set. On ROCm iGPUs without XNACK (Strix Halo, gfx1151) the direct copies left the file pages registered with the GPU: they were never released and the load stalled at about 30% of the weights once they approached the container memory limit. Host buffers and the other backends keep the direct copy. The new core::is_hip_buffer() identifies the owning registry the same way backend_type() does for backend handles.
Owner
|
@Ramon-Balaguer Thanks! PR merged. |
cunba-ai
pushed a commit
to cunba-ai/audio.cpp
that referenced
this pull request
Oct 28, 2026
…d staging) + Earshot VAD + BreezeTTS CUDA opts + Echo-TTS CFG + /health version Upstream commits 899d84a..71f42b8 (22): - fix HIP VMM access updates for growing pools (0xShug0#1059) - gguf: numeric array metadata in one bulk fread (0xShug0#1072) - Echo-TTS optional Joint-CFG (0xShug0#773); Sway time scheduling later - validated HIP VMM defaults in Arch AMD installer (0xShug0#1080) - HIP argsort extended to large rows (0xShug0#1076) - Irodori-TTS MeanFlow support (0xShug0#1083) - assets: direct uploads preserved in weight-norm tensor views (0xShug0#1081) - HIP device weight uploads staged through bounded host buffer (0xShug0#1070) - empty/null speech voice values treated as omitted (0xShug0#1086) - redundant VoxCPM2 KV cache staging/reset avoided (0xShug0#1085) - Drama3 device sampling default off (0xShug0#1087) - Studio LoRA controls + shared adapter groups (0xShug0#1088) - Arena styling alignment (0xShug0#1089) - Earshot VAD native C++ port (0xShug0#1095) - ASR VAD chunking loads any built-in VAD family (0xShug0#1096) - /health reports build version + git commit (0xShug0#1097) - BreezeTTS opt-in fused depth graph CUDA (0xShug0#1090); bf16 rounding fusion in rms_norm/add/SwiGLU (0xShug0#1091); batch-2 CFG decode (0xShug0#1092); option names normalized - ci: best-effort HIP test release binaries (0xShug0#1100) - Echo-TTS Sway time scheduling (0xShug0#1105)
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.
Summary
On a ROCm iGPU without XNACK (Strix Halo, gfx1151), Fish Audio Drama 3 never finishes loading: the backbone weight upload stops at about 30% and the server never becomes ready. This PR makes
set_tensor_bytesfill HIP device buffers through a bounded 64 MiB host staging buffer instead of passing the memory-mapped GGUF straight toggml_backend_tensor_set. Host buffers and the other backends (CUDA, Vulkan, Metal) keep the direct copy, so their code path does not change.With the change, the same model loads in 15-18 s, and file-backed RSS stays below ~350 MB instead of growing to ~14 GB.
Root cause
GgufTensorSource::set_backend_tensorpassesset_tensor_bytesa pointer into theMAP_PRIVATEmapping of the GGUF. Each device tensor is therefore uploaded with a singlehipMemcpyAsyncwhose source is pageable, file-backed memory, often hundreds of MB long. On this device the HIP runtime reportsHMM support: 1, XNACK: 0, Direct host access: 0. Evidence from the stalled load:AMD_LOG_LEVEL=4shows one call per tensor, sourced from the GGUF mapping:hipMemcpyAsync(dst, 0x7197ebfc7e20, 290635776, hipMemcpyHostToDevice, ...)./proc/<pid>/status,RssFilegrows with the upload and is never released (up to ~13.7 GB), even thoughdiscard_range()callsmadvise(MADV_DONTNEED)after each tensor.RssAnonstays below 300 MB./proc/<pid>/task/*/wchan, the loader thread blocks insvm_range_set_attr(KFD SVM,ioctl). OnceRssFilenears the container's 12 GB memory limit, disk reads drop to 0 MB/s and the thread spins in user space.upload_progressstays at 0.30-0.31 for minutes.Staging the data through a reusable heap buffer turns this into 64 MiB copies from that buffer.
RssFilestays flat and the load completes.The stall does not come from the recent Drama 3 changes. It reproduces with a local build of
c8365f39and with2f6c70bf,a7f18608anddd92e32b, withGGML_HIP_HIPBLASLTboth ON and OFF, and with both the@builtinand@ggufmodel contracts.Change
core::is_hip_buffer(ggml_backend_buffer_t)(new,backend.h/backend.cpp): true when the buffer's device belongs to the HIP registry. It uses the samereg_name_matcheslookup thatbackend_type()uses for backend handles.set_tensor_bytes()(tensor_source.cpp): for a HIP device buffer (the tensor's own buffer, or its view source's), the bytes are copied in chunks of at most 64 MiB into a local staging vector, and each chunk is uploaded withggml_backend_tensor_set(tensor, staging, offset, count). Any other buffer uses the previous singleggml_backend_tensor_setcall. The staging vector is freed when the call returns.llama.cpp's loader follows the same pattern: it fills device buffers from staging buffers rather than straight from the mapped file.
Validation
Hardware and software: AMD Radeon 8060S (Strix Halo, gfx1151) with a 96 GiB VRAM carve-out (UMA), ROCm 10.0.0 (HIP 7.15.26333, AMD clang 23.0.0git), running in a Debian 13 LXC with a 12 GB memory limit.
Builds:
Model:
drama-3-backbone-4bit-experts-Q4_K_S.gguf(Jmica/Drama-3-GGUF). Server config:backend: hip,cpu_expert_layers: 0,context_length: 4096, in offline and streaming modes. Load check:dd92e32b)a7f18608: not ready after 5 min;2f6c70bf: not ready after 6 min 40 s (upload_progressstuck at ~0.31)RssFile≤ 342 MBdd92e32b: stuck at 0.31 after 90 sRssFile≤ 310 MBRequests after the fix used Spanish sentences with a cloned voice, sent sequentially to one session, with
fish_drama3.device_sampling=false(see below):response_format: wav)stream_format: sse,response_format: pcm)The results are the same with both builds. Whisper transcripts of the generated audio match the input text. Peak VRAM with the model loaded is 21.8 GiB, the same as an earlier working build.
Regression check on the staged path: an offline request with
seed: 42, run twice with 64 MiB staging chunks and twice with a 1 MiB build of the same code, gave byte-identical WAVs in all four runs (MD5188e6fccf8881a6127ff7e92f6098906). The uploaded weights therefore do not depend on how the copy is split.Not tested: CUDA, Vulkan and Metal. Their uploads take the same code path as before.
Related, not addressed here
On the same HIP setup,
fish_drama3.device_sampling=true(the default since #1063) aborts on the first request:fish_drama3.device_sampling=falseworks, and all the numbers above use it. I can open a separate issue with logs.