Skip to content

Proper fix for host buffer sync - #26225

Closed
pwilkin wants to merge 2 commits into
ggml-org:masterfrom
pwilkin:sched-host-input-sync
Closed

Proper fix for host buffer sync#26225
pwilkin wants to merge 2 commits into
ggml-org:masterfrom
pwilkin:sched-host-input-sync

Conversation

@pwilkin

@pwilkin pwilkin commented Jul 28, 2026

Copy link
Copy Markdown
Member

Overview

On top of #26167 , proper fix to synchronization issues with host buffers.

Additional information

Supersedes #25863

Requirements

  • I have read and agree with the contributing guidelines
  • AI usage disclosure: Yes, told Opus to run the sanitizer and prepare the sync fix.

am17an and others added 2 commits July 28, 2026 11:26
…uffers

On backends that advertise support for their own host buffer type (HIP
integrated GPUs expose ROCm_Host, and the same applies to CUDA_Host and
Vulkan_Host), the scheduler may place graph inputs such as inp_tokens in
that buffer and let the device read them directly.

Such a buffer is created through the CPU buffer interface, so
ggml_backend_tensor_set on it is a bare memcpy on the calling thread: it
never reaches the owning backend and cannot be ordered against an
in-flight graph_compute_async whose kernels are still reading the tensor.
The scheduler inserts no copy or sync either, precisely because the
backend claims it can consume the buffer directly. set_inputs then
overwrites the previous ubatch's inputs while the GPU is still reading
them, which corrupts the prompt.

The existing guard above only synchronizes for pipeline parallelism,
which requires n_devices() > 1 and is therefore never active on a single
integrated GPU.

Synchronize before set_inputs when the graph actually has such inputs.
The condition is computed once per graph build and cached, so configs
without device-owned host inputs are unaffected.

Reproducer, on gfx1151 with any prompt longer than one ubatch:

  GGML_SCHED_SANITIZE=1 llama-completion -ngl 99 -f long.txt

aborts with a write-after-read on ROCm_Host inp_tokens (detected by the
scheduler sanitizer, ggml-org#26167) and without the sanitizer silently produces
different output from a race-free build.

Measured on Ornith-1.0-35B IQ4_XS, 33k-token prompt, ABBA-interleaved,
gfx1151: prefill 720.8 -> 720.3 t/s, decode unchanged at ~51 t/s.

Assisted-By: Claude <noreply@anthropic.com>
@pwilkin
pwilkin requested a review from ggerganov as a code owner July 28, 2026 10:36
@github-actions github-actions Bot added the ggml changes relating to the ggml tensor library for machine learning label Jul 28, 2026
@DeMaulwurfn

Copy link
Copy Markdown

I built this PR myself and ran an A/B against the commit right below it, on Strix Halo. Short version,
it fixes the problem completely and it does not cost any speed. Numbers below, i hope this is usefull
for getting it approved.

My setup is a Framework Desktop with Strix Halo, gfx1151, 96 GB unified memory as a BIOS carve out,
Fedora 44 with kernel 7.1.8. Model is Qwen3.5-122B-A10B as unsloth UD-Q4_K_XL with -c 262144, flags are
-ngl 99 -dio --no-mmap --jinja -fa on --cache-type-k q8_0 --cache-type-v q8_0. All throughput numbers
come from the servers own timings object and not from wall clock.

The nice thing about this PR is that it consists of exactly two commits, so no patching was needed at
all. I just built the same branch twice, once at dd1e191 (the sanitizer, without the fix) and once at
d1259e0 (with the fix). Those two differ by 53 added lines in src/llama-context.cpp and
src/llama-context.h and nothing else, so the A/B has exactly one variable. I built from your branchs own
.devops/rocm.Dockerfile with two changes, AMDGPU_TARGETS=gfx1151 only because i have one card, and i left
GGML_HIP_ROCWMMA_FATTN unset because master removed it on 2026-08-09 and i wanted to be closer to the
current official image. Base was rocm/dev-ubuntu-24.04:7.2.1-complete, same as the official recipe.

For measuring i use a needle test. A marker KANARIE-<8 hex> is put at a defined position inside a
deterministic filler prompt. The marker has no relation to prompt length or position so it can not be
guessed, and the model is asked to either echo it or to answer NICHTGEFUNDEN, which is german for "not
found". Six prompt lengths times five positions, so 30 cells.

Here is the result. Same machine, same model, same flags, only the commit differs.

build needle test
dd1e191, without the fix 17 of 30
d1259e0, with the fix 30 of 30

All 13 failures in the control are honest "not found", the model never invented a marker. This is the
control run, numbers in brackets are prompt_tokens.

prompt length 5% 25% 50% 75% 95%
1000 ok (1070) ok (1071) ok (1069) ok (1071) ok (1068)
1500 not found (1588) not found (1588) ok (1589) ok (1588) ok (1588)
2000 not found (2093) not found (2093) not found (2092) ok (2092) ok (2092)
2500 not found (2545) not found (2543) not found (2544) ok (2544) ok (2545)
3000 not found (3055) not found (3057) not found (3057) ok (3056) ok (3056)
4000 ok (3983) not found (3980) not found (3981) ok (3980) ok (3982)

With the fix applied every single one of these cells comes back ok.

If you sort the cells not by prompt length but by how far the marker sits away from the end of the
prompt, the border is very sharp, it sits at about 1024 tokens.

distance from prompt end cell result
803 1071 at 25% ok
995 3980 at 75% ok
1017 1070 at 5% ok
1046 2092 at 50% not found
1191 1588 at 25% not found
1509 1588 at 5% not found
2418 2545 at 5% not found
2985 3980 at 25% not found

One thing worth mentioning because i can not explain it. In the 4000 token row the marker at 5 percent
sits 3784 tokens from the end, so far outside that border, and it comes back correctly. I first thought
this was randomness, but it is not, the same cell survives in two independent runs on two different
builds. So it is reproducible and i have no explanation for it. Maybe it has to do with where the marker
lands relative to batch boundaries. I am just reporting it, not claiming anything from it.

I also ran the control against the official image ghcr.io/ggml-org/llama.cpp:server-rocm, digest
sha256:079b121dc8ae65619a36165372f218252367ad7f0f3e152baf4e67465628a4cb, which is build 10454 and commit
4df29be. That one fails the exact same 13 cells, not just the same amount but cell for cell. So my own
build reproduces the official behaviour faithfully and my build flags are not creating artifacts.

Now the speed side, because that is what makes this PR important for this hardware and not just nice to
have. Prefill in t/s, same machine and model.

prompt tokens 9776 (before #24233) 10454 (master, no fix) my build with your fix
3373 274,5 380,0 368,0
26357 109,0 318,7 315,4
52592 64,2 265,3 263,6
104777 35,4 196,9 196,0

At 104777 tokens thats 196,0 with the fix against 196,9 without, so 0,5 percent apart while my
measurement noise on prefill is 1,9 percent. Your fix keeps the full scaling. Decode is unaffected as
well. Note my build sits on your branch base from end of july while 10454 is master from mid august, so
the small differences at the short end are more likely two and a half weeks of other commits than your
patch.

The reason this matters so much here, the build from before #24233 reads a 100k token prompt in 49
minutes, and the current one in 9 minutes. So on this hardware people currently have to choose between a
build that computes correctly and a build that is five times faster at long context. This PR removes that
choice, thats why i went to the trouble of building it.

Which leads to a question i can not answer myself. Does #25863, so disabling direct ROCm_Host compute on
HIP integrated GPUs, also switch off whatever gives this prefill scaling? If it does, then that workaround
would buy correctness for a factor 5,5 in prefill on APUs, wich would be a rough trade. Your approach
keeps the path and only synchronizes and costs nothing measurable, at least i could not measure it. From
where i sit that is a real difference between the two and i did not see it discussed.

Happy to run anything else you want tested on gfx1151, and i can share the test scripts, they are plain
perl and curl with no dependencies. Unrelated to this PR, i also noticed something about decode speed at
long context between end of july and mid august that looks like a regression. If that is interesting i
can open a separate issue with numbers, i did not want to mix it into this one.

@pwilkin

pwilkin commented Aug 17, 2026

Copy link
Copy Markdown
Member Author

@DeMaulwurfn yeah, we want to keep the direct host compute. Disabling it is masking the real problem.

I'll have the ring buffer PR as per that discussion soon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ggml changes relating to the ggml tensor library for machine learning

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants