Sched: Reintroduce less synchronizations between token, with fixed pipeline parallelism.#20793
Conversation
This comment was marked as outdated.
This comment was marked as outdated.
|
Will test soon. |
|
@mxxm-t don't bother right now. Once I think I have the correct solution, I'll force-push and ping you again. |
a48fd3b to
06e8b36
Compare
|
With #20927 being merged, I now see identical PPL in master and the reapplied original PR on my RTX PRO 6000 Max-Q / RTX PRO 4500 setup of @mxxm-t feel free to try it if time allows. Linux Performance |
Benchmark: 2x NVIDIA L40S (sm_89 / Lovelace)Tested with model: Qwen3.5-122B-A10B Q5_K_S Hardware: 2x NVIDIA L40S 48GB, AMD EPYC 9354P, Linux Master (dc8d14c, b8537)
PR branch (06e8b36, b8508)
SummaryAll results within noise on this setup. No regression, no measurable improvement. tg128 ~+0.1 t/s (within margin of error). This is consistent with your observation that the benefit is primarily on Windows I think, Linux multi-GPU pipeline parallelism scheduling appears unaffected by this change for my setup. |
|
@mxxm-t @slavap @Superbobo75 @thejacer if you have the time, please test PPL (as outlined in #20463 (comment)) and performance on your setups. |
|
PR #20793 Master |
|
@ggerganov |
JohannesGaessler
left a comment
There was a problem hiding this comment.
Sorry, I'm confused. The way I read the linked PR in which the original one got reverted @ggerganov is saying that the original PR had to be reverted to restore correct results. But isn't this PR making the exact same changes as before?
correct in both aspects. Since then @am17an merged his fix. Reapplying my changes now still yields correct results, since the scheduling bug was not part of my PR. My PR just removed a lot of "speed bumps" in the form of synchronizations, which exposed the bug. |
|
Can you also link the fix in question? |
|
I think we should wait for the TP PR (#13776) to be merged and stable before merging this again. Debugging synchronization issues is a pain. |
|
@JohannesGaessler it's #20927 |
|
@am17an @JohannesGaessler do we have a rough timeline when we consider #19378 to be stable? Since this closes a major perf gap between windows and linux, most of the user base is on windows, and there is no real benefit leaving it sitting, revisiting/merging this sooner than later makes sense to me. |
|
I need to find the time to test this on HIP first for sure, since the previous attempt at this broke HIP entirely. |
|
Sorry, I forgot about this PR. Please rebase on top of master and I'll check whether there are issues (though the synchronization logic should be backend-agnostic). |
5002405 to
38a6f1e
Compare
|
I rebased and spot-checked single and multi-GPU again. In both cases, PPL is identical to master. |
|
Using 4x RTX 4090 I am unable to provoke issues, the PPL values I get are bit-for-bit identical both for |
My testing above was done on 2xMi50. Does that satisfy HIP testing? |
|
@thejacer that helps, yeah |
|
Unfortionatly this reintroduces #20433 on hip, repdoucer from that issue: This pr: Master @b8785
|
|
@IMbackK I guess there is a subtle difference in the inherent ordering of memcpy and compute between CUDA and hip. That means it makes sense to make the Just out of curiosity, can you check if unguarding the |
|
Master:
PR (rebased):
Patch1:
Patch2:
Both: PPL: pass
All of the performance values are within run to run variance. From the perspective of purely the hip backend, this pr is not worthwhile in the first place. |
…kend pipeline parallel bugs.
GPU split optimization
non-async backends
…hat it is out of precaution, but that no perf-impact is visible, and that it can be revisited separately anytime.
29baa60 to
0234bb9
Compare
|
@ggerganov @aendk I can confirm on my 4x MI50 Rig, running ROCm 6.3.3 Official, this is causing a major regression in speed, around a 50% loss in prefill speed: Before: After: While these cards are EOL the version of ROCm that I am using does officially support them. Edit: To be thorough, pulled down the commit prior to this merge, then built: |
…org#20793) * CUDA: Improve performance via less synchronizations between token (ggml-org#17795) * Adds CPU-to-CUDA copy capability to ggml_backend_cuda_cpy_tensor_async() * Adds function to relax sync requirements between input copies on supported backends (CUDA for now) * Exchanges synchronous copy with async copy function. * Adds macro guards to allow compilation in non-CUDA builds * Reworked backend detection in ggml-backend.cpp to avoid linking conflicts * Relax requirement of checks in async CUDA copies from backend and buffer type to just buffer type, to avoid linking issues * Minor cleanup * Makes opt-in to relax use of explicit syncs more general. Backends like vulkan which require a synchronization between HtoD copies and graph execution could also adopt this change now. * Reintroduces stricter check for CPU->CUDA backend async copy via GGML_DEVICE_TYPE_CPU. * Corrects initialization of ggml_backend_sync_mode in ggml_backend_sched_split initialization * Simplifies synchronizations to adhere to `saaasg` pattern. * Apply suggestion from @ggerganov (src->buffer to buf_src) Co-authored-by: Georgi Gerganov <ggerganov@gmail.com> * Apply suggestion from @ggerganov (src->buffer to buf_src) v2 Co-authored-by: Georgi Gerganov <ggerganov@gmail.com> --------- Co-authored-by: Georgi Gerganov <ggerganov@gmail.com> * Apply suggestions from @JohannesGaessler code review Co-authored-by: Johannes Gäßler <johannesg@5d6.de> * Adds single-GPU synchronizations to multi-GPU settings to fix hip backend pipeline parallel bugs. * Scheduler Hardening: Exclude hip/MUSA from copy_from_host CPU split -> GPU split optimization * Scheduler Hardening: Re-adding original additional synchronizations for non-async backends * Adds disclaimer to hip/musa exclusion of copy_from_host. Highlights that it is out of precaution, but that no perf-impact is visible, and that it can be revisited separately anytime. --------- Co-authored-by: Georgi Gerganov <ggerganov@gmail.com> Co-authored-by: Johannes Gäßler <johannesg@5d6.de>
|
@aendk @ggerganov This type of argumentation is really disappointing. The thing is that mi50 is old, but still very popular, and support has been restored for it in modernized Rocm (TheRock) https://github.com/ROCm/TheRock/blob/main/SUPPORTED_GPUS.md |
|
It definitely isn't just EOL hardware, this is a pretty big regression on my 3 R9700s. On the Qwen models and Gemma 31B, I get around a 35% decrease in prefill. Decode seems unaffected for dense models but it takes a hit for MOE models. Software: Ubuntu 24.04, ROCm version 7.2.1. Tested on commit 5d8ccdf (immediately before this PR) and commit 3fc4e10 (this PR). Before/5d8ccdf: nathan@Theseus:~/bin/llama.cpp-prechange$ ./build/bin/llama-bench -ngl 999 -fa 1 -mmp 0 -dio 1 -r 5 -sm layer -p 4096 -n 1024 -m ~/models/Qwen3.6-35B-A3B-Q8_0.gguf --device ROCm0/ROCm1/ROCm2
| model | size | params | backend | ngl | fa | dev | mmap | dio | test | t/s |
| ------------------------------ | ---------: | ---------: | ---------- | --: | --: | ------------ | ---: | --: | --------------: | -------------------: |
| qwen35moe 35B.A3B Q8_0 | 35.19 GiB | 35.51 B | ROCm,Vulkan | 999 | 1 | ROCm0/ROCm1/ROCm2 | 0 | 1 | pp4096 | 5471.62 ± 14.17 |
| qwen35moe 35B.A3B Q8_0 | 35.19 GiB | 35.51 B | ROCm,Vulkan | 999 | 1 | ROCm0/ROCm1/ROCm2 | 0 | 1 | tg1024 | 72.46 ± 0.10 |nathan@Theseus:~/bin/llama.cpp-prechange$ ./build/bin/llama-bench -ngl 999 -fa 1 -mmp 0 -dio 1 -r 5 -sm layer -p 4096 -n 1024 -m ~/models/Qwen3.6-27B-Q6_K.gguf --device ROCm0/ROCm1/ROCm2
| model | size | params | backend | ngl | fa | dev | mmap | dio | test | t/s |
| ------------------------------ | ---------: | ---------: | ---------- | --: | --: | ------------ | ---: | --: | --------------: | -------------------: |
| qwen35 27B Q6_K | 21.30 GiB | 27.32 B | ROCm,Vulkan | 999 | 1 | ROCm0/ROCm1/ROCm2 | 0 | 1 | pp4096 | 1312.72 ± 2.98 |
| qwen35 27B Q6_K | 21.30 GiB | 27.32 B | ROCm,Vulkan | 999 | 1 | ROCm0/ROCm1/ROCm2 | 0 | 1 | tg1024 | 22.32 ± 0.04 |nathan@Theseus:~/bin/llama.cpp-prechange$ ./build/bin/llama-bench -ngl 999 -fa 1 -mmp 0 -dio 1 -r 5 -sm layer -p 4096 -n 1024 -m ~/models/gemma-4-26B-A4B-it-UD-Q6_K_XL.gguf --device ROCm0/ROCm1/ROCm2
| model | size | params | backend | ngl | fa | dev | mmap | dio | test | t/s |
| ------------------------------ | ---------: | ---------: | ---------- | --: | --: | ------------ | ---: | --: | --------------: | -------------------: |
| gemma4 26B.A4B Q6_K | 22.17 GiB | 25.23 B | ROCm,Vulkan | 999 | 1 | ROCm0/ROCm1/ROCm2 | 0 | 1 | pp4096 | 2308.53 ± 12.22 |
| gemma4 26B.A4B Q6_K | 22.17 GiB | 25.23 B | ROCm,Vulkan | 999 | 1 | ROCm0/ROCm1/ROCm2 | 0 | 1 | tg1024 | 71.94 ± 0.20 |nathan@Theseus:~/bin/llama.cpp-prechange$ ./build/bin/llama-bench -ngl 999 -fa 1 -mmp 0 -dio 1 -r 5 -sm layer -p 4096 -n 1024 -m ~/models/gemma-4-31B-it-Q8_0.gguf --device ROCm0/ROCm1/ROCm2
| model | size | params | backend | ngl | fa | dev | mmap | dio | test | t/s |
| ------------------------------ | ---------: | ---------: | ---------- | --: | --: | ------------ | ---: | --: | --------------: | -------------------: |
| gemma4 31B Q8_0 | 30.38 GiB | 30.70 B | ROCm,Vulkan | 999 | 1 | ROCm0/ROCm1/ROCm2 | 0 | 1 | pp4096 | 1781.16 ± 4.75 |
| gemma4 31B Q8_0 | 30.38 GiB | 30.70 B | ROCm,Vulkan | 999 | 1 | ROCm0/ROCm1/ROCm2 | 0 | 1 | tg1024 | 16.18 ± 0.02 |After/3fc4e10: nathan@Theseus:~/bin/llama.cpp-postchange$ ./build/bin/llama-bench -ngl 999 -fa 1 -mmp 0 -dio 1 -r 5 -sm layer -p 4096 -n 1024 -m ~/models/Qwen3.6-35B-A3B-Q8_0.gguf --device ROCm0/ROCm1/ROCm2
| model | size | params | backend | ngl | fa | dev | mmap | dio | test | t/s |
| ------------------------------ | ---------: | ---------: | ---------- | --: | --: | ------------ | ---: | --: | --------------: | -------------------: |
| qwen35moe 35B.A3B Q8_0 | 35.19 GiB | 35.51 B | ROCm,Vulkan | 999 | 1 | ROCm0/ROCm1/ROCm2 | 0 | 1 | pp4096 | 3547.74 ± 10.50 |
| qwen35moe 35B.A3B Q8_0 | 35.19 GiB | 35.51 B | ROCm,Vulkan | 999 | 1 | ROCm0/ROCm1/ROCm2 | 0 | 1 | tg1024 | 63.47 ± 0.08 |nathan@Theseus:~/bin/llama.cpp-postchange$ ./build/bin/llama-bench -ngl 999 -fa 1 -mmp 0 -dio 1 -r 5 -sm layer -p 4096 -n 1024 -m ~/models/Qwen3.6-27B-Q6_K.gguf --device ROCm0/ROCm1/ROCm2
| model | size | params | backend | ngl | fa | dev | mmap | dio | test | t/s |
| ------------------------------ | ---------: | ---------: | ---------- | --: | --: | ------------ | ---: | --: | --------------: | -------------------: |
| qwen35 27B Q6_K | 21.30 GiB | 27.32 B | ROCm,Vulkan | 999 | 1 | ROCm0/ROCm1/ROCm2 | 0 | 1 | pp4096 | 851.84 ± 1.45 |
| qwen35 27B Q6_K | 21.30 GiB | 27.32 B | ROCm,Vulkan | 999 | 1 | ROCm0/ROCm1/ROCm2 | 0 | 1 | tg1024 | 21.77 ± 0.03 |nathan@Theseus:~/bin/llama.cpp-postchange$ ./build/bin/llama-bench -ngl 999 -fa 1 -mmp 0 -dio 1 -r 5 -sm layer -p 4096 -n 1024 -m ~/models/gemma-4-26B-A4B-it-UD-Q6_K_XL.gguf --device ROCm0/ROCm1/ROCm2
| model | size | params | backend | ngl | fa | dev | mmap | dio | test | t/s |
| ------------------------------ | ---------: | ---------: | ---------- | --: | --: | ------------ | ---: | --: | --------------: | -------------------: |
| gemma4 26B.A4B Q6_K | 22.17 GiB | 25.23 B | ROCm,Vulkan | 999 | 1 | ROCm0/ROCm1/ROCm2 | 0 | 1 | pp4096 | 2290.15 ± 13.22 |
| gemma4 26B.A4B Q6_K | 22.17 GiB | 25.23 B | ROCm,Vulkan | 999 | 1 | ROCm0/ROCm1/ROCm2 | 0 | 1 | tg1024 | 59.96 ± 0.34 |nathan@Theseus:~/bin/llama.cpp-postchange$ ./build/bin/llama-bench -ngl 999 -fa 1 -mmp 0 -dio 1 -r 5 -sm layer -p 4096 -n 1024 -m ~/models/gemma-4-31B-it-Q8_0.gguf --device ROCm0/ROCm1/ROCm2
| model | size | params | backend | ngl | fa | dev | mmap | dio | test | t/s |
| ------------------------------ | ---------: | ---------: | ---------- | --: | --: | ------------ | ---: | --: | --------------: | -------------------: |
| gemma4 31B Q8_0 | 30.38 GiB | 30.70 B | ROCm,Vulkan | 999 | 1 | ROCm0/ROCm1/ROCm2 | 0 | 1 | pp4096 | 1165.06 ± 1.54 |
| gemma4 31B Q8_0 | 30.38 GiB | 30.70 B | ROCm,Vulkan | 999 | 1 | ROCm0/ROCm1/ROCm2 | 0 | 1 | tg1024 | 16.01 ± 0.01 | |
ggml-org#20793)" This reverts commit 3fc4e10.
|
We definitely should not be breaking HIP performance like this for a few % on other hardware. Revert this PR. |
Agreed, though I must say I'm surprised as we did validate HIP performance to be fine at some point of the journey #20793 (comment) (though only for a single dense model it seem).
Tensors flagged as |
I don't think this is generally true for this flag although it is true for |
I stand corrected :) Though for our intents, the non-overlapping aspect in itself should be sufficient as we effectively "batch-copy" all inputs at the beginning 🤔 I think a bisect of this PR's commits on PPL/Perf on AMD GPUs would help greatly root-cause this as it should be performance positive across backends - unfortunately I don't have access to AMD GPUs. |
…org#20793) * CUDA: Improve performance via less synchronizations between token (ggml-org#17795) * Adds CPU-to-CUDA copy capability to ggml_backend_cuda_cpy_tensor_async() * Adds function to relax sync requirements between input copies on supported backends (CUDA for now) * Exchanges synchronous copy with async copy function. * Adds macro guards to allow compilation in non-CUDA builds * Reworked backend detection in ggml-backend.cpp to avoid linking conflicts * Relax requirement of checks in async CUDA copies from backend and buffer type to just buffer type, to avoid linking issues * Minor cleanup * Makes opt-in to relax use of explicit syncs more general. Backends like vulkan which require a synchronization between HtoD copies and graph execution could also adopt this change now. * Reintroduces stricter check for CPU->CUDA backend async copy via GGML_DEVICE_TYPE_CPU. * Corrects initialization of ggml_backend_sync_mode in ggml_backend_sched_split initialization * Simplifies synchronizations to adhere to `saaasg` pattern. * Apply suggestion from @ggerganov (src->buffer to buf_src) Co-authored-by: Georgi Gerganov <ggerganov@gmail.com> * Apply suggestion from @ggerganov (src->buffer to buf_src) v2 Co-authored-by: Georgi Gerganov <ggerganov@gmail.com> --------- Co-authored-by: Georgi Gerganov <ggerganov@gmail.com> * Apply suggestions from @JohannesGaessler code review Co-authored-by: Johannes Gäßler <johannesg@5d6.de> * Adds single-GPU synchronizations to multi-GPU settings to fix hip backend pipeline parallel bugs. * Scheduler Hardening: Exclude hip/MUSA from copy_from_host CPU split -> GPU split optimization * Scheduler Hardening: Re-adding original additional synchronizations for non-async backends * Adds disclaimer to hip/musa exclusion of copy_from_host. Highlights that it is out of precaution, but that no perf-impact is visible, and that it can be revisited separately anytime. --------- Co-authored-by: Georgi Gerganov <ggerganov@gmail.com> Co-authored-by: Johannes Gäßler <johannesg@5d6.de>
sched : reintroduce less synchronizations during split compute (ggml-org#20793)
sched : reintroduce less synchronizations during split compute (ggml-org#20793)
sched : reintroduce less synchronizations during split compute (ggml-org#20793)
sched : reintroduce less synchronizations during split compute (ggml-org#20793)
Follow up to #20463 (comment).
#17795 improved performance in the single GPU setting on CUDA, but it was rolled back due to a bug surfacing in multi-GPU pipeline parallel settings.
For the single GPU setting, it moved the scheduling from
sassassasgto the more efficientsaaasgpattern, wheres= sync,a= async copy,g= graph execution.Each asynchronous copy was enclosed in two synchronizations. Removing some superfluous synchronizations improved performance, especially on windows. The change was to only do a single synchronization between memory copies and graph execution.
However in multi-GPU settings, we saw
llama-perplexityregressions indicating incorrect scheduling (#20463).I found that the event-based pipeline parallelism scheduling mechanism very likely implicitly relies on synchronous copies, as (i) in my testing
copy_from_hostworked as intended, and (ii) disabling it and therefore introducing synchronous copies fixed the bug,llama-perplexityperplexity was then identical to master.The proposed fix here is therefore to enroll pipeline parallelism into the same synchronization between async copies and graph execution as the single GPU case already has.
I think this can be a good solution as it keeps scheduling similar between single GPU and multi GPU, and because it is simpler and safer than reworking the event-driven pipeline parallelism logic.
In my testing, this proposal has same performance benefits as the initial PR, and it yields correct perplexity scores both in single and multi-GPU.
As this bug surfaced in the community with their more diverse hardware setups and usage scenarios, it would be awesome if you could test-drive this change both with
llama-benchandllama-perplexitywith your usual model and launch-options usage!@mxxm-t @slavap @Superbobo75 @thejacer
If you can, check out this branch and compare this against its master (
git checkout HEAD~2). Let me know if you run into performance or accuracy issues!