char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
7f3f843c3
char/aliceai-t5-moe
1#include "allreduce.cuh" 2 3#if !defined(GGML_USE_HIP) && !defined(GGML_USE_MUSA) 4 5#include "convert.cuh" 6#include "ggml-impl.h" 7 8#include <algorithm> 9#include <cstdlib> 10#include <cstring> 11#include <limits> 12 13// --------------------------------------------------------------------------- 14// CUDA AllReduce for tensor-parallel inference across two GPUs. 15// 16// Provides an in-place sum reduction over matching tensors on two CUDA 17// devices in the same process. Used by the tensor-split path alongside 18// NCCL; targets setups without NVLink, where data is exchanged between the 19// GPUs by staging it through pinned host memory over PCIe. 20// 21// Two reduction strategies are selected per call by tensor size: 22// 23// * Chunked kernel path (small reductions): a single CUDA kernel both 24// stages data through pinned host memory and performs the local sum. 25// Cross-GPU synchronization happens *inside the kernel* (busy-wait on 26// a host-memory flag), which keeps launch overhead low for the 27// latency-sensitive token-generation case. 28// 29// * Copy-engine path (large reductions): the transfer is split into 30// D2H + H2D cudaMemcpyAsync chunks driven by the GPU's copy engine, 31// followed by a small device-side add kernel. Cross-GPU 32// synchronization happens *outside the kernel*, via CUDA events 33// between streams. This keeps the compute engine free while large 34// transfers are in flight, which matters for prefill-sized tensors. 35// Reductions larger than the per-call inner cap are processed by an 36// outer chunker that issues sequential inner calls. 37// --------------------------------------------------------------------------- 38 39// --------------------------------------------------------------------------- 40// Cross-GPU signal mechanism 41// 42// One int per (slot, rank) pair in pinned host memory. Each AR call writes a 43// strictly increasing token (= the AR call number) into its own arrival int. 44// The peer spins until its read of the other's arrival int equals the token 45// it expects for this call -- a mismatch means the peer hasn't arrived yet. 46// Tokens never repeat over realistic call rates (32-bit int wraps in tens of 47// days at thousands of ARs/sec), so arrival ints don't need to be reset 48// between calls; we initialize once at pipeline init and let the values 49// accumulate. 50// 51// There is exactly one writer (the owning GPU) and one reader (the peer), so 52// we don't need atomics. A volatile store paired with __threadfence_system() 53// provides the release ordering that makes the D2H writes visible system-wide 54// before the arrival token is observed. 55// 56// atomicAdd_system() requires hostNativeAtomicSupported, which is unavailable 57// on PCIe-attached consumer GPUs without NVLink, so the volatile path is the 58// portable choice. 59// --------------------------------------------------------------------------- 60 61static __device__ __forceinline__ void ggml_cuda_ar_signal_set(int * p, int token) { 62 *(volatile int *)p = token; 63} 64static __device__ __forceinline__ int ggml_cuda_ar_signal_get(const int * p) { 65 return *(const volatile int *)p; 66} 67 68// Byte spacing between adjacent arrival ints. 64 bytes (one cache line) 69// ensures each GPU/block's arrival slot lives on its own line, preventing 70// false-sharing stalls on the polling GPU. 71static constexpr size_t GGML_CUDA_AR_ARRIVAL_STRIDE = 64; 72 73// Number of blocks the chunked kernel launches with. Each block stripes a 74// disjoint slice of the data and synchronizes through its own arrival-token 75// slot so multiple SMs can pump PCIe stores in parallel. 76static constexpr int GGML_CUDA_AR_KERNEL_BLOCKS = 8; 77 78// --------------------------------------------------------------------------- 79// Chunked kernel AllReduce -- 2 GPUs, supports float, half, and bfloat16. 80// 81// Both GPUs run this kernel simultaneously on independent streams. sendbuf 82// and recvbuf live in T_dst (the caller's tensor type); host_mine / host_other 83// carry data in T_wire (the on-wire type, possibly narrower than T_dst -- e.g. 84// T_dst=F32 with T_wire=BF16 halves the bytes pushed across PCIe). When 85// T_dst == T_wire the casts below are no-ops. 86// 87// Each GPU runs three phases: 88// 89// Phase 1 (all threads): cast sendbuf (T_dst) -> T_wire and store as 90// single-instruction-width vectors into host_mine. 91// __threadfence_system() commits these writes to host 92// memory. 93// Phase 2 (thread 0): write token to arrival_mine; spin until 94// arrival_other == token. 95// Phase 3 (all threads): read T_wire vectors from host_other, cast 96// each element to T_dst, and sum with the local 97// sendbuf value (also rounded through T_wire so that 98// both GPUs truncate identically -- this guarantees 99// bit-equivalent results across the two devices). 100// 101// Multi-block: blocks stripe vectors across (gridDim.x * blockDim.x) global 102// threads to keep multiple SMs issuing PCIe stores in parallel. Each block 103// has its own arrival-token slot (offset by blockIdx.x * ARRIVAL_STRIDE); 104// thread 0 of each block signals/spins on that slot independently of other 105// blocks. Tail elements (the leftover < ELEMS_PER_VEC at the end) are 106// handled only by block 0 to avoid cross-block writes to the same slots. 107// --------------------------------------------------------------------------- 108template <typename T_dst, typename T_wire> 109static __global__ void ggml_cuda_ar_kernel( 110 const T_dst * sendbuf, 111 T_dst * recvbuf, 112 T_wire * __restrict__ host_mine, 113 const T_wire * __restrict__ host_other, 114 int count, 115 int * arrival_mine, 116 int * arrival_other, 117 int token) { 118 119 // Vector unit for the wire type, sized to the arch's widest single-instruction 120 // copy (16 B on Volta+). Each phase-1 iter writes one vector to host memory; 121 // each phase-3 iter reads one and produces ELEMS_PER_VEC sums. 122 constexpr int ELEMS_PER_VEC = ggml_cuda_get_max_cpy_bytes() / sizeof(T_wire); 123 constexpr int ARRIVAL_INTS = (int)(GGML_CUDA_AR_ARRIVAL_STRIDE / sizeof(int)); 124 125 const int tid = threadIdx.x; 126 const int nt = blockDim.x; 127 const int bid = blockIdx.x; 128 const int gtid = bid * nt + tid; 129 const int gnt = gridDim.x * nt; 130 const int count_vec = count / ELEMS_PER_VEC; 131 const int tail = count_vec * ELEMS_PER_VEC; 132 133 // Phase 1: cast sendbuf (T_dst) -> host_mine (T_wire) and store as vectors. 134 { 135 for (int i = gtid; i < count_vec; i += gnt) { 136 const int off = i * ELEMS_PER_VEC; 137 T_wire wire[ELEMS_PER_VEC]; 138 #pragma unroll 139 for (int k = 0; k < ELEMS_PER_VEC; ++k) { 140 wire[k] = ggml_cuda_cast<T_wire>(sendbuf[off + k]); 141 } 142 ggml_cuda_memcpy_1<sizeof(wire)>(&host_mine[off], wire); 143 } 144 if (bid == 0 && tid < count - tail) { 145 host_mine[tail + tid] = ggml_cuda_cast<T_wire>(sendbuf[tail + tid]); 146 } 147 } 148 149 // Commit this block's host writes before signalling. 150 __threadfence_system(); 151 __syncthreads(); 152 153 // Phase 2: thread 0 of each block signals on its own arrival slot, then 154 // spins for the matching slot from peer. Per-block tokens mean blocks 155 // proceed independently -- no inter-block barrier needed. 156 if (tid == 0) { 157 int * my_slot = arrival_mine + bid * ARRIVAL_INTS; 158 const int * other_slot = arrival_other + bid * ARRIVAL_INTS; 159 160 ggml_cuda_ar_signal_set(my_slot, token); 161 __threadfence_system(); // make our signal visible system-wide 162 163 while (ggml_cuda_ar_signal_get(other_slot) != token) { 164#if __CUDA_ARCH__ >= GGML_CUDA_CC_VOLTA 165 __nanosleep(100); 166#else 167 NO_DEVICE_CODE; 168#endif // __CUDA_ARCH__ >= GGML_CUDA_CC_VOLTA 169 } 170 } 171 172 __syncthreads(); 173 174 // Acquire peer's host_other writes (this block's stripe of them). 175 __threadfence_system(); 176 177 // Phase 3: read peer's T_wire vector, cast both sides through T_wire for 178 // bit-equivalence, sum in T_dst precision, and write back to recvbuf. 179 { 180 for (int i = gtid; i < count_vec; i += gnt) { 181 const int off = i * ELEMS_PER_VEC; 182 T_wire wire[ELEMS_PER_VEC]; 183 ggml_cuda_memcpy_1<sizeof(wire)>(wire, &host_other[off]); 184 #pragma unroll 185 for (int k = 0; k < ELEMS_PER_VEC; ++k) { 186 const T_wire d_low = ggml_cuda_cast<T_wire>(sendbuf[off + k]); 187 recvbuf[off + k] = ggml_cuda_cast<T_dst>( 188 ggml_cuda_cast<float>(d_low) + ggml_cuda_cast<float>(wire[k])); 189 } 190 } 191 if (bid == 0 && tid < count - tail) { 192 const T_wire d_low = ggml_cuda_cast<T_wire>(sendbuf[tail + tid]); 193 recvbuf[tail + tid] = ggml_cuda_cast<T_dst>( 194 ggml_cuda_cast<float>(d_low) + 195 ggml_cuda_cast<float>(host_other[tail + tid])); 196 } 197 } 198} 199 200// Combined load-convert-add kernel. The peer's contribution arrives as T_src 201// (which may be a lower-precision type than T_dst when the BF16 round-trip is 202// active). For bit-equivalence between the two GPUs, dst is first rounded 203// through T_src's precision via ggml_cuda_cast -- peer already truncated its 204// own value the same way before sending -- so both sides perform identical 205// arithmetic. When T_dst == T_src the round-trip cast is a no-op. 206template <typename T_dst, typename T_src> 207static __global__ void ggml_cuda_ar_add_kernel( 208 T_dst * __restrict__ dst, 209 const T_src * __restrict__ src, 210 int count) { 211 const int tid = blockIdx.x * blockDim.x + threadIdx.x; 212 const int nt = gridDim.x * blockDim.x; 213 for (int i = tid; i < count; i += nt) { 214 const T_src d_low = ggml_cuda_cast<T_src>(dst[i]); 215 dst[i] = ggml_cuda_cast<T_dst>( 216 ggml_cuda_cast<float>(d_low) + ggml_cuda_cast<float>(src[i])); 217 } 218} 219 220// --------------------------------------------------------------------------- 221// Pipeline structure 222// --------------------------------------------------------------------------- 223 224// Number of slots in the event / arrival ring. Two slots is sufficient: 225// lockstep guarantees the two GPUs are at most one AR (or chunk) apart, so 226// slot[N%2] is always safe to reuse -- peer has already consumed slot[N%2] 227// from AR N-2 by the time we get to AR N. acquire_slot's 228// cudaEventSynchronize on ev.ker for both devices makes that consumption 229// explicit before we overwrite host_buf[slot] for the new AR. 230static constexpr int GGML_CUDA_AR_POOL_SIZE = 2; 231 232// Maximum chunk size (bytes per GPU) handled by one chunked kernel launch. 233// Larger tensors are reduced by issuing multiple chunked launches. 234static constexpr size_t GGML_CUDA_AR_MAX_BYTES = 1024 * 1024; // 1 MB 235 236// Copy-engine path: largest tensor accepted on this path; sets host_large / 237// dev_tmp allocation size. 238static constexpr size_t GGML_CUDA_AR_COPY_MAX_BYTES = 32 * 1024 * 1024; // 32 MB 239 240// AR wire size at which the copy-engine path takes over from the chunked- 241// kernel path. Override via GGML_CUDA_AR_COPY_THRESHOLD. 242static constexpr size_t GGML_CUDA_AR_COPY_THRESHOLD_DEFAULT = 1024 * 1024; // 1 MB 243// Per-call CE chunk-size heuristic: chunk_bytes = clamp(nbytes / 4, MIN, MAX). 244// The /4 keeps ~4 chunks in flight at any moment (good D2H/H2D overlap with 245// the peer); the clamps cover the cases where nbytes/4 is too small (per- 246// memcpy fixed cost dominates) or too large (chunk-level pipelining stalls). 247// Env var GGML_CUDA_AR_COPY_CHUNK_BYTES can override with a fixed value. 248static constexpr size_t GGML_CUDA_AR_COPY_CHUNK_BYTES_HEURISTIC_MIN = 512 * 1024; // 512 KB 249static constexpr size_t GGML_CUDA_AR_COPY_CHUNK_BYTES_HEURISTIC_MAX = 2 * 1024 * 1024; // 2 MB 250// Absolute floor that an env-var override is allowed to set; this caps the 251// per-slot copy-event array. 256 KB -> up to 128 chunks per 32 MB tensor. 252static constexpr size_t GGML_CUDA_AR_COPY_CHUNK_BYTES_MIN = 256 * 1024; 253static constexpr int GGML_CUDA_AR_COPY_MAX_CHUNKS = 254 static_cast<int>((GGML_CUDA_AR_COPY_MAX_BYTES + GGML_CUDA_AR_COPY_CHUNK_BYTES_MIN - 1) / 255 GGML_CUDA_AR_COPY_CHUNK_BYTES_MIN); 256 257struct ggml_cuda_ar_event_slot { 258 cudaEvent_t app = nullptr; // upstream computation complete 259 cudaEvent_t cpy[GGML_CUDA_AR_COPY_MAX_CHUNKS] = {}; // copy-engine D2H chunks complete 260 cudaEvent_t h2d = nullptr; // copy-engine H2Ds complete (handoff AR stream -> compute stream) 261 cudaEvent_t ker = nullptr; // AllReduce kernel complete 262}; 263 264// Mapped pinned host allocation: cudaHostAlloc + cudaHostGetDevicePointer 265// in one place, with the host handle preserved for cudaFreeHost. Used where 266// the CPU never touches the buffer -- only the device reads/writes via the 267// mapped device pointer. Required on systems where cudaDevAttrCanUseHost- 268// PointerForRegisteredMem is 0 and the host pointer can't be used as a 269// device pointer. 270struct ggml_cuda_ar_host_mapping { 271 uint8_t * host = nullptr; // cudaFreeHost handle; also the H-side ptr for cudaMemcpyAsync 272 uint8_t * dev = nullptr; // device-side pointer for kernels / cudaMemset 273 274 cudaError_t alloc(size_t bytes) { 275 cudaError_t rc = cudaHostAlloc(reinterpret_cast<void **>(&host), bytes, 276 cudaHostAllocPortable | cudaHostAllocMapped); 277 if (rc != cudaSuccess) { 278 host = nullptr; 279 return rc; 280 } 281 rc = cudaHostGetDevicePointer(reinterpret_cast<void **>(&dev), host, 0); 282 if (rc != cudaSuccess) { 283 cudaFreeHost(host); 284 host = nullptr; 285 dev = nullptr; 286 } 287 return rc; 288 } 289 290 void free() { 291 if (host) { 292 cudaFreeHost(host); 293 host = nullptr; 294 dev = nullptr; 295 } 296 } 297}; 298 299struct ggml_cuda_ar_pipeline { 300 int n_devices; 301 int devices[GGML_CUDA_MAX_DEVICES]; 302 size_t buf_bytes; // bytes per device in host_buf[] 303 size_t copy_bytes; // bytes per device in host_large[] / dev_tmp[] 304 size_t copy_threshold; 305 size_t copy_chunk_bytes; 306 size_t bf16_threshold; // tensors >= this size (bytes) are reduced via FP32->BF16 round-trip; 0 disables 307 uint64_t call_count; 308 309 // Per-device resources. 310 ggml_cuda_ar_host_mapping host_buf[GGML_CUDA_MAX_DEVICES]; // pinned staging (chunked kernel) 311 ggml_cuda_ar_host_mapping host_large[GGML_CUDA_MAX_DEVICES]; // pinned staging (copy-engine) 312 char * dev_tmp[GGML_CUDA_MAX_DEVICES]; // device scratch for copy-engine path 313 cudaStream_t streams[GGML_CUDA_MAX_DEVICES]; // non-blocking 314 ggml_cuda_ar_event_slot ev_pool[GGML_CUDA_MAX_DEVICES][GGML_CUDA_AR_POOL_SIZE]; 315 316 // Copy-engine: per-device "I finished reading my peer's host_large" 317 // event. Indexed by RECORDER device. Recorded same-device on streams[i] 318 // after stage 2's last H2D from host_large[peer]. Waited cross-device 319 // by peer's stage-1 stream before the next AR overwrites host_large[peer]. 320 cudaEvent_t host_large_read_done[GGML_CUDA_MAX_DEVICES]; 321 bool host_large_read_done_valid; 322 323 // Copy-engine: per-device "my add_kernel is done with dev_tmp" event. 324 // Recorded on the compute stream after each add_kernel; the AR stream 325 // waits on it before the next copy_impl's H2D overwrites dev_tmp. Lets us 326 // single-buffer dev_tmp despite add_kernel running on a separate stream. 327 cudaEvent_t dev_tmp_kernel_done[GGML_CUDA_MAX_DEVICES]; 328 bool dev_tmp_kernel_done_valid; 329 330 // Arrival ring: ARRIVAL_STRIDE bytes between adjacent ints. Mapped pinned 331 // memory; CPU never reads/writes -- only the kernel and cudaMemset. 332 // Use ggml_cuda_ar_arrival_ptr() to index. 333 ggml_cuda_ar_host_mapping arrival; 334}; 335 336// Base pointer for the (slot, rank) per-block token block. The kernel adds 337// blockIdx.x * (ARRIVAL_STRIDE/sizeof(int)) internally to land on its own slot. 338static int * ggml_cuda_ar_arrival_ptr(const ggml_cuda_ar_pipeline * p, int slot, int rank) { 339 const size_t offset = ((size_t)slot * p->n_devices + rank) * 340 GGML_CUDA_AR_KERNEL_BLOCKS * GGML_CUDA_AR_ARRIVAL_STRIDE; 341 return reinterpret_cast<int *>(p->arrival.dev + offset); 342} 343 344static uint64_t ggml_cuda_ar_env_u64(const char * name, uint64_t default_value) { 345 const char * value = getenv(name); 346 if (value == nullptr || value[0] == '\0') { 347 return default_value; 348 } 349 350 char * end = nullptr; 351 const unsigned long long parsed = strtoull(value, &end, 10); 352 return end != value ? (uint64_t) parsed : default_value; 353} 354 355struct ggml_cuda_ar_slot_info { 356 int slot; 357 int token; 358}; 359 360static ggml_cuda_ar_slot_info ggml_cuda_ar_acquire_slot(ggml_cuda_ar_pipeline * p) { 361 const int slot = static_cast<int>(p->call_count % GGML_CUDA_AR_POOL_SIZE); 362 const bool pool_lapped = p->call_count >= GGML_CUDA_AR_POOL_SIZE; 363 p->call_count++; 364 365 if (pool_lapped) { 366 for (int i = 0; i < p->n_devices; ++i) { 367 ggml_cuda_set_device(p->devices[i]); 368 CUDA_CHECK(cudaEventSynchronize(p->ev_pool[i][slot].ker)); 369 } 370 } 371 372 return { slot, (int) p->call_count }; 373} 374 375// Per-AR copy-engine chunk size: env-var override if set, else heuristic 376// (clamp(nbytes/4, HEURISTIC_MIN, HEURISTIC_MAX)). 377static size_t ggml_cuda_ar_chunk_bytes(const ggml_cuda_ar_pipeline * p, size_t nbytes) { 378 if (p->copy_chunk_bytes > 0) { 379 return p->copy_chunk_bytes; 380 } 381 return std::min(GGML_CUDA_AR_COPY_CHUNK_BYTES_HEURISTIC_MAX, 382 std::max(GGML_CUDA_AR_COPY_CHUNK_BYTES_HEURISTIC_MIN, nbytes / 4)); 383} 384 385static void ggml_cuda_ar_wait_for_compute( 386 ggml_cuda_ar_pipeline * p, ggml_backend_cuda_context * cuda_ctx, int rank, int slot) { 387 ggml_cuda_ar_event_slot & ev = p->ev_pool[rank][slot]; 388 CUDA_CHECK(cudaEventRecord(ev.app, cuda_ctx->stream())); 389 CUDA_CHECK(cudaStreamWaitEvent(p->streams[rank], ev.app)); 390} 391 392// --------------------------------------------------------------------------- 393// Init / free 394// --------------------------------------------------------------------------- 395 396ggml_cuda_ar_pipeline * ggml_cuda_ar_pipeline_init(const int * devices, size_t n_devices) { 397 398 if (n_devices != 2) { 399 GGML_LOG_DEBUG("%s: internal AllReduce only supports n_devices=2 (got %zu); " 400 "falling back\n", __func__, n_devices); 401 return nullptr; 402 } 403 404 // The chunked kernel uses __nanosleep, which is sm70+ (Volta+). 405 for (size_t i = 0; i < n_devices; ++i) { 406 const int cc = ggml_cuda_info().devices[devices[i]].cc; 407 if (cc < GGML_CUDA_CC_VOLTA) { 408 GGML_LOG_DEBUG("%s: internal AllReduce requires compute capability >= %d " 409 "(device %d has cc=%d); falling back\n", 410 __func__, GGML_CUDA_CC_VOLTA, devices[i], cc); 411 return nullptr; 412 } 413 } 414 415 auto * p = new ggml_cuda_ar_pipeline{}; 416 p->n_devices = n_devices; 417 p->copy_bytes = GGML_CUDA_AR_COPY_MAX_BYTES; 418 p->copy_threshold = ggml_cuda_ar_env_u64("GGML_CUDA_AR_COPY_THRESHOLD", GGML_CUDA_AR_COPY_THRESHOLD_DEFAULT); 419 // 0 = use the per-call heuristic (default). Non-zero env value forces a 420 // fixed chunk size for diagnostics, with a floor at COPY_CHUNK_BYTES_MIN. 421 p->copy_chunk_bytes = ggml_cuda_ar_env_u64("GGML_CUDA_AR_COPY_CHUNK_BYTES", 0); 422 if (p->copy_chunk_bytes > 0 && p->copy_chunk_bytes < GGML_CUDA_AR_COPY_CHUNK_BYTES_MIN) { 423 GGML_LOG_WARN("%s: GGML_CUDA_AR_COPY_CHUNK_BYTES=%zu below minimum %zu; clamping\n", 424 __func__, p->copy_chunk_bytes, GGML_CUDA_AR_COPY_CHUNK_BYTES_MIN); 425 p->copy_chunk_bytes = GGML_CUDA_AR_COPY_CHUNK_BYTES_MIN; 426 } 427 // Default 1: BF16 round-trip is always on for F32 inputs (any non-zero 428 // ne). Set GGML_CUDA_AR_BF16_THRESHOLD=0 to disable, or to a larger 429 // byte threshold to opt out for small tensors. 430 p->bf16_threshold = ggml_cuda_ar_env_u64("GGML_CUDA_AR_BF16_THRESHOLD", 1); 431 for (size_t i = 0; i < n_devices; ++i) { 432 p->devices[i] = devices[i]; 433 } 434 435 // Per-device streams and event pools. 436 for (size_t i = 0; i < n_devices; ++i) { 437 ggml_cuda_set_device(p->devices[i]); 438 439 cudaStream_t stream = nullptr; 440 if (cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking) != cudaSuccess) { 441 GGML_LOG_ERROR("%s: cudaStreamCreateWithFlags failed for device %d\n", 442 __func__, p->devices[i]); 443 ggml_cuda_ar_pipeline_free(p); 444 return nullptr; 445 } 446 p->streams[i] = stream; 447 448 for (int s = 0; s < GGML_CUDA_AR_POOL_SIZE; ++s) { 449 bool ok = 450 cudaEventCreateWithFlags(&p->ev_pool[i][s].app, cudaEventDisableTiming) == cudaSuccess && 451 cudaEventCreateWithFlags(&p->ev_pool[i][s].h2d, cudaEventDisableTiming) == cudaSuccess && 452 cudaEventCreateWithFlags(&p->ev_pool[i][s].ker, cudaEventDisableTiming) == cudaSuccess; 453 for (int c = 0; ok && c < GGML_CUDA_AR_COPY_MAX_CHUNKS; ++c) { 454 ok = cudaEventCreateWithFlags(&p->ev_pool[i][s].cpy[c], cudaEventDisableTiming) == cudaSuccess; 455 } 456 if (!ok) { 457 GGML_LOG_ERROR("%s: cudaEventCreate failed for device %d slot %d\n", 458 __func__, p->devices[i], s); 459 ggml_cuda_ar_pipeline_free(p); 460 return nullptr; 461 } 462 } 463 464 if (cudaEventCreateWithFlags(&p->host_large_read_done[i], cudaEventDisableTiming) != cudaSuccess) { 465 GGML_LOG_ERROR("%s: cudaEventCreate for host_large_read_done failed for device %d\n", 466 __func__, p->devices[i]); 467 ggml_cuda_ar_pipeline_free(p); 468 return nullptr; 469 } 470 if (cudaEventCreateWithFlags(&p->dev_tmp_kernel_done[i], cudaEventDisableTiming) != cudaSuccess) { 471 GGML_LOG_ERROR("%s: cudaEventCreate for dev_tmp_kernel_done failed for device %d\n", 472 __func__, p->devices[i]); 473 ggml_cuda_ar_pipeline_free(p); 474 return nullptr; 475 } 476 } 477 478 // Arrival ring: cache-line padded so each GPU's int is on its own line. 479 const size_t arrival_bytes = 480 (size_t)GGML_CUDA_AR_POOL_SIZE * n_devices * 481 GGML_CUDA_AR_KERNEL_BLOCKS * GGML_CUDA_AR_ARRIVAL_STRIDE; 482 if (p->arrival.alloc(arrival_bytes) != cudaSuccess) { 483 GGML_LOG_ERROR("%s: alloc for arrival ring failed (%zu bytes)\n", 484 __func__, arrival_bytes); 485 ggml_cuda_ar_pipeline_free(p); 486 return nullptr; 487 } 488 ggml_cuda_set_device(p->devices[0]); 489 if (cudaMemset(p->arrival.dev, 0, arrival_bytes) != cudaSuccess) { 490 GGML_LOG_ERROR("%s: cudaMemset for arrival ring failed (%zu bytes)\n", 491 __func__, arrival_bytes); 492 ggml_cuda_ar_pipeline_free(p); 493 return nullptr; 494 } 495 496 // Per-device pinned staging buffers -- POOL_SIZE-deep ring so the chunked- 497 // kernel can write the next slot's data while the peer is still reading 498 // the previous slot's. Indexed by (slot * buf_bytes) at the call site. 499 p->buf_bytes = GGML_CUDA_AR_MAX_BYTES; 500 const size_t host_buf_total = (size_t) GGML_CUDA_AR_POOL_SIZE * p->buf_bytes; 501 for (size_t i = 0; i < n_devices; ++i) { 502 if (p->host_buf[i].alloc(host_buf_total) != cudaSuccess) { 503 GGML_LOG_ERROR("%s: alloc for staging failed (%zu bytes)\n", 504 __func__, host_buf_total); 505 ggml_cuda_ar_pipeline_free(p); 506 return nullptr; 507 } 508 } 509 510 // Copy-engine path: pinned host staging + device scratch, sized for the 511 // largest tensor we accept on this path (GGML_CUDA_AR_COPY_MAX_BYTES). 512 // dev_tmp is single-buffered; cross-AR safety is enforced by an explicit 513 // cross-stream wait in copy_impl on the prior AR's add_kernel-done event. 514 for (size_t i = 0; i < n_devices; ++i) { 515 ggml_cuda_set_device(p->devices[i]); 516 if (p->host_large[i].alloc(p->copy_bytes) != cudaSuccess) { 517 GGML_LOG_ERROR("%s: alloc for large staging failed (%zu bytes)\n", 518 __func__, p->copy_bytes); 519 ggml_cuda_ar_pipeline_free(p); 520 return nullptr; 521 } 522 if (cudaMalloc(reinterpret_cast<void **>(&p->dev_tmp[i]), p->copy_bytes) != cudaSuccess) { 523 GGML_LOG_ERROR("%s: cudaMalloc for copy scratch failed (%zu bytes) on device %d\n", 524 __func__, p->copy_bytes, p->devices[i]); 525 ggml_cuda_ar_pipeline_free(p); 526 return nullptr; 527 } 528 } 529 530 GGML_LOG_INFO("%s: initialized AllReduce pipeline: %zu GPUs, " 531 "%zu KB chunked kernel staging + %zu MB copy-engine staging per GPU\n", 532 __func__, n_devices, p->buf_bytes >> 10, p->copy_bytes >> 20); 533 534 return p; 535} 536 537void ggml_cuda_ar_pipeline_free(ggml_cuda_ar_pipeline * p) { 538 if (!p) { 539 return; 540 } 541 542 // Drain all in-flight kernels before tearing down resources. 543 for (int i = 0; i < p->n_devices; ++i) { 544 if (p->streams[i]) { 545 ggml_cuda_set_device(p->devices[i]); 546 cudaStreamSynchronize(p->streams[i]); 547 } 548 } 549 550 for (int i = 0; i < p->n_devices; ++i) { 551 p->host_buf[i].free(); 552 p->host_large[i].free(); 553 if (p->dev_tmp[i]) { 554 ggml_cuda_set_device(p->devices[i]); 555 cudaFree(p->dev_tmp[i]); 556 } 557 ggml_cuda_set_device(p->devices[i]); 558 for (int s = 0; s < GGML_CUDA_AR_POOL_SIZE; ++s) { 559 if (p->ev_pool[i][s].app) { cudaEventDestroy(p->ev_pool[i][s].app); } 560 for (int c = 0; c < GGML_CUDA_AR_COPY_MAX_CHUNKS; ++c) { 561 if (p->ev_pool[i][s].cpy[c]) { cudaEventDestroy(p->ev_pool[i][s].cpy[c]); } 562 } 563 if (p->ev_pool[i][s].h2d) { cudaEventDestroy(p->ev_pool[i][s].h2d); } 564 if (p->ev_pool[i][s].ker) { cudaEventDestroy(p->ev_pool[i][s].ker); } 565 } 566 if (p->host_large_read_done[i]) { 567 ggml_cuda_set_device(p->devices[i]); 568 cudaEventDestroy(p->host_large_read_done[i]); 569 } 570 if (p->dev_tmp_kernel_done[i]) { 571 ggml_cuda_set_device(p->devices[i]); 572 cudaEventDestroy(p->dev_tmp_kernel_done[i]); 573 } 574 if (p->streams[i]) { 575 ggml_cuda_set_device(p->devices[i]); 576 cudaStreamDestroy(p->streams[i]); 577 } 578 } 579 p->arrival.free(); 580 delete p; 581} 582 583// --------------------------------------------------------------------------- 584// Dispatch 585// --------------------------------------------------------------------------- 586 587// Asymmetric copy_impl: data sent over PCIe in T_src precision (one element of 588// nbytes per ne element); accumulated locally into a T_dst buffer. When 589// T_src == T_dst this is the original homogeneous reduction. When they differ 590// (e.g. BF16 wire / F32 accumulator) the add kernel rounds dst through T_src 591// for bit-equivalence between GPUs and we skip the otherwise-needed 592// post-conversion entirely. 593template <typename T_src, typename T_dst> 594static bool ggml_cuda_ar_allreduce_copy_impl( 595 ggml_cuda_ar_pipeline * p, 596 ggml_backend_t * backends, 597 T_src * const src_buf[GGML_CUDA_MAX_DEVICES], 598 T_dst * const dst_buf[GGML_CUDA_MAX_DEVICES], 599 const bool compute[GGML_CUDA_MAX_DEVICES], 600 int64_t ne, 601 size_t nbytes) { 602 GGML_ASSERT(p->n_devices == 2); 603 GGML_ASSERT(nbytes <= p->copy_bytes); 604 GGML_ASSERT(ne <= std::numeric_limits<int>::max()); 605 606 const size_t chunk_bytes = ggml_cuda_ar_chunk_bytes(p, nbytes); 607 GGML_ASSERT(chunk_bytes > 0); 608 609 const int slot = ggml_cuda_ar_acquire_slot(p).slot; 610 const size_t copy_chunks = (nbytes + chunk_bytes - 1) / chunk_bytes; 611 GGML_ASSERT(copy_chunks <= GGML_CUDA_AR_COPY_MAX_CHUNKS); 612 613 ggml_backend_cuda_context * cuda_ctx[2] = {}; 614 615 // Stage 1: both GPUs copy their local contribution to pinned host memory. 616 for (int i = 0; i < 2; ++i) { 617 ggml_cuda_set_device(p->devices[i]); 618 cuda_ctx[i] = static_cast<ggml_backend_cuda_context *>(backends[i]->context); 619 GGML_ASSERT(cuda_ctx[i]->device == p->devices[i]); 620 621 ggml_cuda_ar_wait_for_compute(p, cuda_ctx[i], i, slot); 622 623 // Wait for peer's H2D from our host_large[i] (recorded in the 624 // previous AR's stage 2) to complete before we overwrite host_large[i]. 625 // host_large_read_done[peer] = peer finished reading host_large[i]. 626 // No-op on the first AR -- no prior record exists. 627 if (p->host_large_read_done_valid) { 628 const int peer = 1 - i; 629 CUDA_CHECK(cudaStreamWaitEvent(p->streams[i], p->host_large_read_done[peer])); 630 } 631 632 if (!compute[i]) { 633 CUDA_CHECK(cudaMemsetAsync(src_buf[i], 0, nbytes, p->streams[i])); 634 } 635 636 for (size_t c = 0; c < copy_chunks; ++c) { 637 const size_t offset = c * chunk_bytes; 638 const size_t this_bytes = (nbytes - offset) < chunk_bytes ? 639 (nbytes - offset) : chunk_bytes; 640 641 CUDA_CHECK(cudaMemcpyAsync( 642 p->host_large[i].host + offset, reinterpret_cast<char *>(src_buf[i]) + offset, this_bytes, 643 cudaMemcpyDeviceToHost, p->streams[i])); 644 CUDA_CHECK(cudaEventRecord(p->ev_pool[i][slot].cpy[c], p->streams[i])); 645 } 646 } 647 648 // Stage 2: each GPU waits for each peer D2H chunk, pulls that chunk back to 649 // local device scratch (dev_tmp), then performs one device-local add over 650 // the assembled peer tensor. The H2Ds run on the AR stream (copy engine) 651 // and the add_kernel runs on the caller's compute stream, so the AR stream 652 // stays pure-copy and avoids an in-stream copy->compute engine switch every 653 // AR. dev_tmp is single-buffered: the AR stream waits cross-stream on the 654 // prior AR's add_kernel-done event before overwriting it. 655 for (int i = 0; i < 2; ++i) { 656 const int peer = 1 - i; 657 ggml_cuda_set_device(p->devices[i]); 658 659 // Wait for the previous AR's add_kernel (on the compute stream) to 660 // finish reading dev_tmp before our H2D overwrites it. No-op on the 661 // first copy_impl call. 662 if (p->dev_tmp_kernel_done_valid) { 663 CUDA_CHECK(cudaStreamWaitEvent(p->streams[i], p->dev_tmp_kernel_done[i])); 664 } 665 666 for (size_t c = 0; c < copy_chunks; ++c) { 667 const size_t offset = c * chunk_bytes; 668 const size_t this_bytes = (nbytes - offset) < chunk_bytes ? 669 (nbytes - offset) : chunk_bytes; 670 671 CUDA_CHECK(cudaStreamWaitEvent(p->streams[i], p->ev_pool[peer][slot].cpy[c])); 672 CUDA_CHECK(cudaMemcpyAsync( 673 p->dev_tmp[i] + offset, p->host_large[peer].host + offset, this_bytes, 674 cudaMemcpyHostToDevice, p->streams[i])); 675 } 676 677 // Mark our reads of host_large[peer] complete so peer's next AR can 678 // safely overwrite it. 679 CUDA_CHECK(cudaEventRecord(p->host_large_read_done[i], p->streams[i])); 680 681 // Hand off from AR stream (copy engine) to compute stream: compute 682 // stream waits for all H2Ds to finish, then runs the add_kernel. 683 CUDA_CHECK(cudaEventRecord(p->ev_pool[i][slot].h2d, p->streams[i])); 684 CUDA_CHECK(cudaStreamWaitEvent(cuda_ctx[i]->stream(), p->ev_pool[i][slot].h2d)); 685 686 const int block_size = 256; 687 int n_blocks = (int) ((ne + block_size - 1) / block_size); 688 if (n_blocks > 1024) { 689 n_blocks = 1024; 690 } 691 ggml_cuda_ar_add_kernel<T_dst, T_src><<<n_blocks, block_size, 0, cuda_ctx[i]->stream()>>>( 692 dst_buf[i], 693 reinterpret_cast<const T_src *>(p->dev_tmp[i]), 694 (int) ne); 695 CUDA_CHECK(cudaGetLastError()); 696 697 // Record dev_tmp-released on the compute stream so the next copy_impl 698 // can wait for the kernel to finish before overwriting dev_tmp. Also 699 // record AR-done as ev.ker for acquire_slot's pool-wraparound sync. 700 CUDA_CHECK(cudaEventRecord(p->dev_tmp_kernel_done[i], cuda_ctx[i]->stream())); 701 CUDA_CHECK(cudaEventRecord(p->ev_pool[i][slot].ker, cuda_ctx[i]->stream())); 702 } 703 p->host_large_read_done_valid = true; 704 p->dev_tmp_kernel_done_valid = true; 705 706 return true; 707} 708 709// Outer-level chunker: copy_impl handles up to copy_bytes per call (limited by 710// the host_large / dev_tmp allocation size). When the full AR exceeds that, 711// slice the tensor into copy_bytes-sized pieces and call copy_impl repeatedly. 712// Each slice goes through its own stage 1 -> stage 2 cycle and acquires its own 713// slot, so cross-AR fences and pool wraparound work the same way as for any 714// other sequence of small ARs. 715template <typename T_src, typename T_dst> 716static bool ggml_cuda_ar_allreduce_copy_outer( 717 ggml_cuda_ar_pipeline * p, 718 ggml_backend_t * backends, 719 T_src * const src_buf[GGML_CUDA_MAX_DEVICES], 720 T_dst * const dst_buf[GGML_CUDA_MAX_DEVICES], 721 const bool compute[GGML_CUDA_MAX_DEVICES], 722 int64_t ne) { 723 const int64_t outer_max_elems = (int64_t) (p->copy_bytes / sizeof(T_src)); 724 GGML_ASSERT(outer_max_elems > 0); 725 726 bool ok = true; 727 for (int64_t outer_start = 0; outer_start < ne && ok; outer_start += outer_max_elems) { 728 const int64_t outer_ne = std::min(outer_max_elems, ne - outer_start); 729 const size_t outer_nbytes = (size_t) outer_ne * sizeof(T_src); 730 731 T_src * src[GGML_CUDA_MAX_DEVICES] = {}; 732 T_dst * dst[GGML_CUDA_MAX_DEVICES] = {}; 733 for (int i = 0; i < p->n_devices; ++i) { 734 src[i] = src_buf[i] + outer_start; 735 dst[i] = dst_buf[i] + outer_start; 736 } 737 ok = ggml_cuda_ar_allreduce_copy_impl<T_src, T_dst>( 738 p, backends, src, dst, compute, outer_ne, outer_nbytes); 739 } 740 return ok; 741} 742 743bool ggml_cuda_ar_allreduce( 744 ggml_cuda_ar_pipeline * p, 745 ggml_backend_t * backends, 746 ggml_tensor ** tensors) { 747 GGML_ASSERT(p != nullptr); 748 749 const int n = p->n_devices; 750 GGML_ASSERT(n == 2); 751 752 const ggml_type input_type = tensors[0]->type; 753 GGML_ASSERT(input_type == GGML_TYPE_F32 || input_type == GGML_TYPE_F16 || input_type == GGML_TYPE_BF16); 754 755 const int64_t ne = ggml_nelements(tensors[0]); 756 GGML_ASSERT(ne > 0); 757 758 const size_t input_nbytes = ggml_nbytes(tensors[0]); 759 760 // BF16 round-trip: F32 inputs >= bf16_threshold are converted to BF16 for 761 // the reduction (chunked or copy-engine), halving on-wire bytes. Matches 762 // NCCL's behaviour. The pre-conversion zeroes inactive shards so the 763 // inner paths see them as already-prepared compute tensors. 764 const bool use_bf16 = 765 input_type == GGML_TYPE_F32 && 766 p->bf16_threshold > 0 && 767 input_nbytes >= p->bf16_threshold; 768 769 const ggml_type kernel_type = use_bf16 ? GGML_TYPE_BF16 : input_type; 770 const size_t type_size = ggml_type_size(kernel_type); 771 GGML_ASSERT(p->buf_bytes >= type_size); 772 const size_t nbytes = (size_t) ne * type_size; 773 774 bool compute_flag[GGML_CUDA_MAX_DEVICES] = {}; 775 for (int i = 0; i < n; ++i) { 776 compute_flag[i] = (tensors[i]->flags & GGML_TENSOR_FLAG_COMPUTE) != 0; 777 } 778 779 // Decide between copy-engine and chunked kernel paths based on the working 780 // type's actual byte count. No upper bound: copy_outer slices reductions 781 // larger than copy_bytes into copy_bytes-sized pieces. 782 const bool use_copy_engine = 783 p->copy_threshold > 0 && 784 nbytes >= p->copy_threshold; 785 786 // BF16 inactive-shard zeroing: when use_bf16 is on, the combined kernel 787 // (chunked kernel path) and the combined add kernel (copy_engine path) 788 // both accumulate into the F32 tensor data directly, so an inactive 789 // shard's accumulator must start at zero. 790 if (use_bf16) { 791 for (int i = 0; i < n; ++i) { 792 if (!compute_flag[i]) { 793 auto * cuda_ctx = static_cast<ggml_backend_cuda_context *>(backends[i]->context); 794 GGML_ASSERT(cuda_ctx->device == p->devices[i]); 795 ggml_cuda_set_device(p->devices[i]); 796 CUDA_CHECK(cudaMemsetAsync(tensors[i]->data, 0, (size_t) ne * sizeof(float), cuda_ctx->stream())); 797 } 798 } 799 } 800 801 // Pre-convert F32 -> BF16 into bf16_tmp ONLY for the copy_engine + use_bf16 802 // path; the chunked kernel path's combined kernel does the conversion 803 // inline as it writes to host_buf. 804 ggml_cuda_pool_alloc<nv_bfloat16> bf16_tmp[GGML_CUDA_MAX_DEVICES]; 805 void * copy_src_ptr[GGML_CUDA_MAX_DEVICES] = {}; 806 807 if (use_copy_engine && use_bf16) { 808 to_bf16_cuda_t to_bf16 = ggml_get_to_bf16_cuda(GGML_TYPE_F32); 809 for (int i = 0; i < n; ++i) { 810 auto * cuda_ctx = static_cast<ggml_backend_cuda_context *>(backends[i]->context); 811 GGML_ASSERT(cuda_ctx->device == p->devices[i]); 812 bf16_tmp[i].pool = &cuda_ctx->pool(); 813 bf16_tmp[i].alloc(ne); 814 ggml_cuda_set_device(p->devices[i]); 815 if (compute_flag[i]) { 816 to_bf16(tensors[i]->data, bf16_tmp[i].get(), ne, cuda_ctx->stream()); 817 CUDA_CHECK(cudaGetLastError()); 818 } else { 819 CUDA_CHECK(cudaMemsetAsync(bf16_tmp[i].get(), 0, nbytes, cuda_ctx->stream())); 820 } 821 copy_src_ptr[i] = bf16_tmp[i].get(); 822 } 823 } 824 825 bool ok = true; 826 if (use_copy_engine) { 827 // After up-front BF16 conversion, the tmp buffers already hold the 828 // (possibly zeroed-for-inactive) data, so the inner path can treat 829 // every shard as compute. 830 bool inner_compute[GGML_CUDA_MAX_DEVICES]; 831 for (int i = 0; i < n; ++i) { 832 inner_compute[i] = use_bf16 ? true : compute_flag[i]; 833 } 834 835 // Dispatch into copy_impl with explicit src/dst types. When use_bf16 836 // is on, the wire type is BF16 (src = bf16_tmp) and the accumulator 837 // is F32 (dst = tensors[i]->data); the combined add kernel rounds dst 838 // through BF16 for bit-equivalence and writes F32 directly, so no 839 // post-conversion is needed. Otherwise src == dst (same native type). 840 if (use_bf16) { 841 GGML_ASSERT(kernel_type == GGML_TYPE_BF16); 842 nv_bfloat16 * src[GGML_CUDA_MAX_DEVICES] = {}; 843 float * dst[GGML_CUDA_MAX_DEVICES] = {}; 844 for (int i = 0; i < n; ++i) { 845 src[i] = static_cast<nv_bfloat16 *>(copy_src_ptr[i]); 846 dst[i] = static_cast<float *>(tensors[i]->data); 847 } 848 ok = ggml_cuda_ar_allreduce_copy_outer<nv_bfloat16, float>( 849 p, backends, src, dst, inner_compute, ne); 850 } else { 851 switch (kernel_type) { 852 case GGML_TYPE_F32: { 853 float * buf[GGML_CUDA_MAX_DEVICES] = {}; 854 for (int i = 0; i < n; ++i) { 855 buf[i] = static_cast<float *>(tensors[i]->data); 856 } 857 ok = ggml_cuda_ar_allreduce_copy_outer<float, float>( 858 p, backends, buf, buf, inner_compute, ne); 859 break; 860 } 861 case GGML_TYPE_BF16: { 862 nv_bfloat16 * buf[GGML_CUDA_MAX_DEVICES] = {}; 863 for (int i = 0; i < n; ++i) { 864 buf[i] = static_cast<nv_bfloat16 *>(tensors[i]->data); 865 } 866 ok = ggml_cuda_ar_allreduce_copy_outer<nv_bfloat16, nv_bfloat16>( 867 p, backends, buf, buf, inner_compute, ne); 868 break; 869 } 870 case GGML_TYPE_F16: { 871 half * buf[GGML_CUDA_MAX_DEVICES] = {}; 872 for (int i = 0; i < n; ++i) { 873 buf[i] = static_cast<half *>(tensors[i]->data); 874 } 875 ok = ggml_cuda_ar_allreduce_copy_outer<half, half>( 876 p, backends, buf, buf, inner_compute, ne); 877 break; 878 } 879 default: 880 GGML_ASSERT(false); 881 } 882 } 883 } else { 884 // host_buf carries T_wire-typed data; max_chunk_elems is the count that 885 // fits in one host_buf at the wire size. 886 const size_t max_chunk_elems = p->buf_bytes / type_size; 887 const size_t input_type_size = ggml_type_size(input_type); 888 889 // Chunked kernel path runs entirely on the caller's compute stream: 890 // since AR is a barrier here, same-stream ordering subsumes any 891 // cross-stream event handshake that the copy-engine path needs, and 892 // skips the cross-stream scheduling overhead that was hurting the 893 // small-tensor (tg) latency on the AR-stream variant. Only ev.ker is 894 // still recorded at end-of-AR for acquire_slot's pool-wraparound check. 895 for (int64_t chunk_start = 0; chunk_start < ne; chunk_start += (int64_t) max_chunk_elems) { 896 const size_t remaining_elems = (size_t) (ne - chunk_start); 897 const size_t chunk_elems = remaining_elems < max_chunk_elems ? remaining_elems : max_chunk_elems; 898 const size_t chunk_dst_bytes = chunk_elems * input_type_size; 899 900 const auto [slot, token] = ggml_cuda_ar_acquire_slot(p); 901 const bool last_chunk = chunk_start + (int64_t) chunk_elems == ne; 902 903 for (int i = 0; i < n; ++i) { 904 const int peer = 1 - i; // valid for n == 2 only 905 ggml_cuda_set_device(p->devices[i]); 906 auto * cuda_ctx = static_cast<ggml_backend_cuda_context *>(backends[i]->context); 907 GGML_ASSERT(cuda_ctx->device == p->devices[i]); 908 cudaStream_t stream = cuda_ctx->stream(); 909 910 char * data = static_cast<char *>(tensors[i]->data) + chunk_start * (int64_t) input_type_size; 911 912 // Match NCCL/meta-backend semantics: inactive shards contribute 913 // zeros. On the BF16 path the F32 tensor data was already 914 // zeroed up-front (above), so per-chunk zeroing isn't needed. 915 if (!compute_flag[i] && !use_bf16) { 916 CUDA_CHECK(cudaMemsetAsync(data, 0, chunk_dst_bytes, stream)); 917 } 918 919#define LAUNCH_AR_KERNEL(T_dst, T_wire) \ 920 ggml_cuda_ar_kernel<T_dst, T_wire><<<dim3(GGML_CUDA_AR_KERNEL_BLOCKS), dim3(256), 0, stream>>>( \ 921 reinterpret_cast<const T_dst *>(data), \ 922 reinterpret_cast<T_dst *>(data), \ 923 reinterpret_cast<T_wire *>(p->host_buf[i].dev + (size_t) slot * p->buf_bytes), \ 924 reinterpret_cast<const T_wire *>(p->host_buf[peer].dev + (size_t) slot * p->buf_bytes), \ 925 static_cast<int>(chunk_elems), \ 926 ggml_cuda_ar_arrival_ptr(p, slot, i), \ 927 ggml_cuda_ar_arrival_ptr(p, slot, peer), \ 928 token) 929 930 if (use_bf16) { 931 GGML_ASSERT(input_type == GGML_TYPE_F32); 932 LAUNCH_AR_KERNEL(float, nv_bfloat16); 933 } else { 934 switch (input_type) { 935 case GGML_TYPE_F32: LAUNCH_AR_KERNEL(float, float); break; 936 case GGML_TYPE_F16: LAUNCH_AR_KERNEL(half, half); break; 937 case GGML_TYPE_BF16: LAUNCH_AR_KERNEL(nv_bfloat16, nv_bfloat16); break; 938 default: GGML_ASSERT(false); 939 } 940 } 941 942#undef LAUNCH_AR_KERNEL 943 CUDA_CHECK(cudaGetLastError()); 944 945 if (last_chunk) { 946 CUDA_CHECK(cudaEventRecord(p->ev_pool[i][slot].ker, stream)); 947 } 948 } 949 } 950 } 951 952 return ok; 953} 954 955#else // defined(GGML_USE_HIP) || defined(GGML_USE_MUSA) 956 957// HIP and MUSA lack the host-mapped pinned-memory APIs (cudaHostAllocPortable 958// / cudaHostAllocMapped / cudaHostGetDevicePointer) and __nanosleep that this 959// implementation relies on, so the internal AllReduce is a CUDA-only feature. 960// The dispatcher in ggml-cuda.cu treats a nullptr pipeline as "init failed" 961// and silently falls back to the meta backend's generic AllReduce. 962ggml_cuda_ar_pipeline * ggml_cuda_ar_pipeline_init(const int *, size_t) { 963 return nullptr; 964} 965void ggml_cuda_ar_pipeline_free(ggml_cuda_ar_pipeline *) { 966} 967bool ggml_cuda_ar_allreduce(ggml_cuda_ar_pipeline *, ggml_backend_t *, ggml_tensor **) { 968 return false; 969} 970 971#endif // !defined(GGML_USE_HIP) && !defined(GGML_USE_MUSA)