Skip to content

llama, cuda : opt-in AllReduce paths with --allreduce host|p2p - #18

Merged
Piggidragon merged 4 commits into
mainfrom
cuda/allreduce-p2p
Oct 16, 2026
Merged

Piggidragon merged 4 commits into
mainfrom
cuda/allreduce-p2p

Conversation

@Piggidragon

@Piggidragon Piggidragon commented Oct 16, 2026 •

Copy link
Copy Markdown
Owner

Adds --allreduce host|p2p for -sm tensor on two CUDA GPUs:

Without the flag, NCCL does every reduction, as upstream. Before, #14 and #16 were always on and switched by environment variables. After merging, a setup that used them needs --allreduce host (or p2p), or it falls back to NCCL.

The README's fork section is rewritten as one list (what, benefit, how to enable), with an empty table for upstream vs this fork. The server's RAM prompt cache changes are the exception that needs no flag, and the README says so.

Why

On an RTX 4070 + RTX 3060 (PCIe 3.0 x4) with BAR1 P2P, before this PR:

  • Small reductions (decode): with peer access, every reduction went to NCCL. NCCL over P2P is slower than the internal pipeline through host memory: tg32 dropped from 36.9 to 34.2 t/s as soon as peer access was there.
  • Large reductions with --split-overlap: NCCL's kernels hold an SM on each GPU, which slows down the other ubatch's compute. Prompt processing dropped from 1180 to 865 t/s.
  • Large reductions in general: the 3060's x4 link limits them whether or not they go through host memory.

Change

--allreduce p2p

  • Small reductions (below 512 KiB): allreduce-p2p.cu. One kernel per GPU, with 8 blocks.
    • Each block writes its slice of the own contribution (BF16, as the internal pipeline does) into the other GPU's receive buffer. It then writes its token into that GPU's flag, after __threadfence_system.
    • It polls only its own flag, in its own memory, and adds f32(bf16(own)) + f32(bf16(peer)). Both GPUs get the same bits.
    • Two slots alternate, without host synchronization. A GPU writes slot k%2 of call k only after its kernel for call k-1 saw the other GPU's arrival for call k-1. The other GPU signals that arrival after its kernel for call k-2 has read the slot (stream order).
    • Tokens are compared in unsigned arithmetic and wrap at 2^32.
  • Large reductions: the copy-engine AllReduce of cuda : reduce large tensors through the copy engines when NCCL has no peer access #16 gets a peer mode. Each GPU copies its BF16 contribution straight into the other GPU's buffer with cudaMemcpyPeerAsync, with no pinned memory.
  • Peer access: both paths enable it themselves. Without peer access (or on HIP/MUSA) p2p falls back to host with a warning.

The flag

  • Plumbing: llama_context_params.allreduce_type (enum llama_allreduce_type) is passed to the backends through a new reg proc, ggml_backend_comm_set_mode. It is called before a context creates its meta backends.
  • Tools: --allreduce in common, and -ar in llama-bench.
  • Environment variables:
    • Removed: GGML_CUDA_AR_CE, GGML_CUDA_AR_CE_P2P and GGML_CUDA_AR_P2P_SMALL.
    • Kept for tuning: the two size thresholds, GGML_CUDA_AR_NCCL_MIN_BYTES and GGML_CUDA_AR_CE_MIN_BYTES.
    • GGML_CUDA_ALLREDUCE still overrides the flag.
  • Docs: recipe 7 in docs/multi-gpu.md describes the flag.

Results

Qwen3.8-27B UD-Q4_K_S, -sm tensor -ts 0.625/0.375 -as 1/3, q5_1, native FA, -ub 256 --split-overlap, patched libcuda with BAR1 P2P, t/s:

test default (NCCL over P2P) host p2p
pp2048 866 1178 1181
pp3 88.8 97.4 99.5
tg32 34.2 36.9 37.6

Earlier run at 16k depth (same configuration), host vs p2p: pp2048 1046 vs 1067, pp3 77.6 vs 79.2, tg32 35.77 vs 36.48.

In an nsys trace of decode, a small reduction takes 10 µs on the 3060 (median), against 17 µs for the internal pipeline and 41 µs for NCCL without P2P.

Tests

  • test-llama-archs: the new configs Meta -ar host and Meta -ar p2p -so pass on every architecture, with peer access. No failures in the run.

    • The configs lower the copy-engine threshold to 4 KiB, so that decode takes the small path and the ubatches go through the copy engines; the test models' reductions are otherwise all below 512 KiB.
  • Peer access against none: llama-perplexity --kl-divergence (6 × 2048) gives mean KLD 0 and same top token 100%. The paths use the same arithmetic, so the output is bit-identical.

  • Review, round 2 (reviewer-high): two low findings, both fixed:

    • the README claimed no fork change was active without a flag, which is wrong for the server's RAM prompt cache;
    • the copy-engine path was not covered by the new test configs.
  • Review, round 1 (reviewer-xhigh) found three issues, all fixed:

    • the token comparison overflowed at 2^31;
    • P2P was used without an opt-in;
    • a threshold gap left some reductions between the two paths.

    Also checked in that review:

    • memory ordering, slot reuse with lanes and an MTP context, and event hazards;
    • compute-sanitizer memcheck clean;
    • server output byte-identical with and without peer access.

🤖 Generated with Claude Code

Piggidragon and others added 3 commits October 16, 2026 16:23
With peer access llama.cpp left every AllReduce to NCCL. Over a PCIe link
NCCL's small reductions have more latency than needed and its kernels hold an
SM on each GPU, which slows down the compute of the other ubatch with
--split-overlap; a large reduction is bound by the slower link either way.

- small reductions (below 512 KiB): a peer-to-peer kernel (allreduce-p2p.cu).
  Each block writes its slice of the own BF16 contribution into the memory of
  the other GPU and then its token into that GPU's flag, and polls only its
  own flag; the data crosses the link once, as posted writes. Two slots
  alternate; the stream order of the kernels and the growing tokens make that
  safe without host synchronization.
- large reductions: the copy engines copy GPU to GPU (cudaMemcpyPeerAsync)
  instead of through pinned host memory.

Both are on by default with peer access without native peer atomics (PCIe);
NVLink keeps NCCL, and so does HIP. GGML_CUDA_AR_P2P_SMALL and
GGML_CUDA_AR_CE_P2P override the choice.

RTX 4070 + RTX 3060 (PCIe 3.0 x4) with BAR1 P2P, Qwen3.8-27B, -ts 0.625/0.375
-as 1/3, q5_1, -ub 256 --split-overlap, against the same GPUs without peer
access: tg32 36.95 -> 37.73 t/s, pp3 97.4 -> 99.6, pp2048 1179 -> 1188, at 16k
35.77 -> 36.48, 77.6 -> 79.2, 1046 -> 1067. Against NCCL with peer access:
tg32 34.2 -> 37.7, pp2048 with --split-overlap 865 -> 1188. A small reduction
takes 10 us on the 3060 (internal pipeline 17 us, NCCL without P2P 41 us).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- compare the flag with the token in unsigned arithmetic: the signed difference overflowed at token 2^31, and
  the first GPU to arrive then read a stale slot
- peer access over PCIe only with GGML_CUDA_P2P (as in the rest of the backend), not with NCCL_P2P_DISABLE
- the copy engines start where the peer-to-peer kernel ends (512 KiB) when GGML_CUDA_AR_NCCL_MIN_BYTES is larger

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The paths of #14, #16 and this PR were chosen by the hardware and switched by environment variables. They are now
opt-in with one flag, like the other features of this fork, and the default is upstream's NCCL:

- --allreduce host: small reductions through the internal pipeline, large ones through the copy engines, through
  pinned host memory (#14, #16)
- --allreduce p2p: the peer-to-peer kernel and the copy engines from GPU to GPU; host without peer access. The paths
  enable peer access themselves, GGML_CUDA_P2P is not needed
- llama_context_params.allreduce_type, passed to the backends through the new reg proc ggml_backend_comm_set_mode
  before the meta backends of a context are created
- llama-bench -ar, test-llama-archs configs "Meta -ar host" and "Meta -ar p2p -so"
- GGML_CUDA_AR_CE, GGML_CUDA_AR_CE_P2P and GGML_CUDA_AR_P2P_SMALL are gone; the two size thresholds stay as tuning
  variables, GGML_CUDA_ALLREDUCE still overrides
- README: the features of this fork as one list (what, benefit, how to enable), and a table upstream vs this fork

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Piggidragon Piggidragon changed the title cuda : AllReduce paths for two GPUs with peer access over PCIe llama, cuda : opt-in AllReduce paths with --allreduce host|p2p Oct 16, 2026
…ays-on server changes

- test-llama-archs: the reductions of the test models stay below 512 KiB, so the -ar configs never used the copy
  engines; they now create their contexts with GGML_CUDA_AR_CE_MIN_BYTES=4096
- README: the RAM prompt cache changes of the server are on without a flag

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Piggidragon
Piggidragon merged commit 7ec035b into main Oct 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant