Skip to content

ggml-cuda: avoid direct ROCm_Host compute on HIP integrated GPUs (port of ggml-org#25863) - #158

Open
danielhanchen wants to merge 1 commit into
masterfrom
port/25863-hip-integrated-host-buft
Open

ggml-cuda: avoid direct ROCm_Host compute on HIP integrated GPUs (port of ggml-org#25863)#158
danielhanchen wants to merge 1 commit into
masterfrom
port/25863-hip-integrated-host-buft

Conversation

@danielhanchen

Copy link
Copy Markdown
Member

Overview

Carries ggml-org#25863 at ce82541a into the fork, unmodified except for dropping that PR's file mode change (it flips ggml-cuda.cu to 100755; kept at 100644 here).

25 insertions, 2 deletions, one file. Companion to #147, which pins the same upstream commit into the nightlies. This one puts the code in the fork so it is reviewable here and survives as a branch of record.

The defect

c7d87229 (ggml-org#24233) restored prop.integrated on HIP builds. CUDA keeps it off, with the comment Temporarily disabled due to issues with corrupted output. With it on, the scheduler may place a compute input in pinned host memory, where an H2D input write can race a graph that is still running.

Two reporters bisected to that exact commit independently, from different symptoms:

report symptom
ggml-org#25992 -np 4 --kv-unified returns another slot's response verbatim, gfx1151
ggml-org#27506 perplexity 7.72 to 3024 on Llama-3.2-3B, reproduced on three gfx1151 machines, Vulkan clean on each
lemonade-sdk/lemonade#3160 our own Qwen3.8-27B UD-Q4_K_XL degrading under 8 concurrent requests until it emits ////////, recovering on reload; same harness scores 78-82% against cloud endpoints and ~17% here

Corruption tracks n_ubatch < n_batch, and output is clean whenever the whole prompt lands in a single ubatch. Making integrated runtime-switchable on an otherwise unmodified tree gives PPL 850,121 on and 72.80 off, from one binary.

This is the bug that reaches users who did nothing unusual: no environment variable, no special flags. It is separate from the unified-memory corruption in #157, and neither fix addresses the other.

Scope, checked rather than assumed

  • info.devices[id].integrated is set from prop.integrated only under GGML_USE_HIP; CUDA and MUSA get a hard false (ggml-cuda.cu:305-309). The supports_buft change is therefore dead code off HIP.
  • Discrete AMD is unaffected, since prop.integrated is false there.
  • One hunk is genuinely broader and should not be described as integrated-only: get_host_buffer_type now returns nullptr when GGML_CUDA_NO_PINNED is set, on every backend. That is a consistency fix, since props->host_buffer already reported false while the buffer type was still handed out, so a user asking for no pinned memory could still get it. The only caller in the tree, ggml-backend-meta.cpp:379, handles nullptr explicitly.

Why this and not ggml-org#27311

ggml-org#27311 is the better end state: a ring buffer for input tensors, making host buffers correct rather than unavailable. It is an 18-commit scheduler change and currently CONFLICTING, so it is not pinnable today. When it lands, both this and the #147 pin come out.

ggml-org#25863 is the narrow version: refuse to schedule compute out of the host buffer on HIP integrated devices, while pinned memory stays available for staging. Measured at PPL 63.67 against a Vulkan reference of 64.10, where the base gives 872,006.

Verification

Not yet measured on our runner. I would rather say so than imply otherwise: the gfx1151 machine available here has no compiler, so the check has to run against a nightly that carries the pin. Once one exists, llama-perplexity over wikitext-2 at -b 2048 -ub 512 before and after, expecting the previous nightly to be the broken number and the new one to match a Vulkan reference, plus one -np 4 --kv-unified run against ggml-org#25992's nonce-checked harness.

Throughput is unmeasured. This changes buffer placement on a chip where both pools are the same DRAM, so a benchmark is warranted before assuming it is free, and a benchmark has to check output validity or it will reward the broken build.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 31, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-08-31T08:17:27.806247Z abfc45b New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

Port of ggml-org#25863 at ce82541, unmodified except for dropping that PR's
file mode change.

c7d8722 (ggml-org#24233) restored prop.integrated on HIP builds. CUDA keeps it
off, with the comment 'Temporarily disabled due to issues with corrupted
output'. With it on, the scheduler may place a compute input in pinned host
memory, where an H2D input write can race a graph that is still running.

Two reporters bisected to that commit independently, from different symptoms:
ggml-org#25992 gets another slot's response verbatim under -np 4 --kv-unified,
and ggml-org#27506 sees perplexity go from 7.72 to 3024 on Llama-3.2-3B.
lemonade-sdk/lemonade#3160 is the same shape on our own Qwen3.8-27B GGUF.

Scope, checked rather than assumed: info.devices[id].integrated is set from
prop.integrated only under GGML_USE_HIP, and is hard false on CUDA and MUSA
(ggml-cuda.cu:305-309), so the supports_buft change is dead code off HIP.
Discrete AMD is unaffected because prop.integrated is false there. One hunk is
broader: get_host_buffer_type now returns nullptr when GGML_CUDA_NO_PINNED is
set, on every backend. That is a consistency fix, since props->host_buffer
already reported false while the buffer type was still handed out. The only
caller in the tree, ggml-backend-meta.cpp:379, handles nullptr.

Pinned in scripts/unsloth/pr-set.json so the nightlies carry it; the pin comes
out when a base tag contains the upstream merge.
@danielhanchen
danielhanchen force-pushed the port/25863-hip-integrated-host-buft branch from bfe2398 to abfc45b Compare August 31, 2026 08:14
danielhanchen added a commit that referenced this pull request Aug 31, 2026
…161)

* unsloth: repin ggml-org#25731 to the commit that merges onto b10705

edee0e1 stopped applying to the current base tag, which failed the pin
preflight. Because the preflight stops at the first conflict, it also meant the
two AMD fixes pinned by #160 were never dry-run merged at all.

44eb88e is the head after the upstream conflicts were resolved; the PR is
MERGEABLE upstream again as of 2026-08-31T08:06Z.

* unsloth: repoint the AMD pins at commits based on an upstream ancestor

#157 and #158 were branched from fork master, which is right for a small PR
diff and wrong for a pin. Fork master has diverged from upstream (it deletes a
dozen upstream workflow files), so merging those commits onto the base tag
dragged the whole divergence in and conflicted modify/delete on twelve
.github/workflows files. That is what exited the preflight with no message
after 'ok #137'.

The pins that work are branched from an upstream commit that is an ancestor of
both master and the base tag, so they carry only their own delta. Both branches
are now rebased onto 11cd988 on that pattern, and verified locally to merge
onto b10705 for a net +35/-3 across 4 files, which is the two fixes and nothing
else.

---------

Co-authored-by: danielhanchen <elliegouldingstuff@gmail.com>
@danielhanchen danielhanchen added the bug Something isn't working label Aug 31, 2026
danielhanchen added a commit that referenced this pull request Aug 31, 2026
…les it (#162)

ggml tests getenv(...) != nullptr, so =0 enables managed allocation. With #157
pinned this is no longer a correctness bug, but it still costs about 15%
throughput and can lower the ceiling: managed draws host RAM instead of the
device carve-out rather than adding to it, so a model that loads without the
variable can be OOM-killed with it.

Verified locally to co-merge onto b10705 with #157 and #158, net +79/-5 across
4 files.

Co-authored-by: danielhanchen <elliegouldingstuff@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant