Merge remote-tracking branch 'nccl/master' into develop
Tento commit je obsažen v:
@@ -0,0 +1,60 @@
|
||||
# Run the scripts once during configuration to get the file lists
|
||||
execute_process(
|
||||
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/generate.py ${CMAKE_CURRENT_BINARY_DIR}/gensrc "${ONLY_FUNCS}"
|
||||
OUTPUT_VARIABLE files
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
string(STRIP "${files}" files)
|
||||
list(TRANSFORM files PREPEND ${CMAKE_CURRENT_BINARY_DIR}/gensrc/)
|
||||
|
||||
execute_process(
|
||||
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/symmetric/generate.py ${CMAKE_CURRENT_BINARY_DIR}/gensrc/symmetric "${ONLY_FUNCS}"
|
||||
OUTPUT_VARIABLE symmetric_files
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
string(STRIP "${symmetric_files}" symmetric_files)
|
||||
list(TRANSFORM symmetric_files PREPEND ${CMAKE_CURRENT_BINARY_DIR}/gensrc/symmetric/)
|
||||
|
||||
# Create custom commands to generate source files with proper dependencies
|
||||
add_custom_command(
|
||||
OUTPUT ${files}
|
||||
BYPRODUCTS ${files}
|
||||
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/generate.py ${CMAKE_CURRENT_BINARY_DIR}/gensrc "${ONLY_FUNCS}"
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/generate.py
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
COMMENT "Generating device source files"
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${symmetric_files}
|
||||
BYPRODUCTS ${symmetric_files}
|
||||
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/symmetric/generate.py ${CMAKE_CURRENT_BINARY_DIR}/gensrc/symmetric "${ONLY_FUNCS}"
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/symmetric/generate.py
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
COMMENT "Generating symmetric device source files"
|
||||
)
|
||||
|
||||
# Add library target
|
||||
add_library(nccl_device OBJECT
|
||||
${files}
|
||||
${symmetric_files}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/common.cu
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/onerank.cu
|
||||
)
|
||||
|
||||
set_target_properties(nccl_device PROPERTIES
|
||||
CUDA_SEPARABLE_COMPILATION ON
|
||||
CUDA_RESOLVE_DEVICE_SYMBOLS ON
|
||||
)
|
||||
|
||||
# Set include directories for the target
|
||||
target_include_directories(nccl_device PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_SOURCE_DIR}/src/include
|
||||
${CMAKE_SOURCE_DIR}/src/include/plugin
|
||||
${CMAKE_BINARY_DIR}/include
|
||||
${CUDAToolkit_INCLUDE_DIRS}
|
||||
${CUDAToolkit_INCLUDE_DIRS}/cccl
|
||||
)
|
||||
|
||||
add_dependencies(nccl_device nccl_header)
|
||||
@@ -19,7 +19,7 @@ OBJDIR := $(BUILDDIR)/obj/device
|
||||
MANIFEST := $(OBJDIR)/manifest
|
||||
DEVGLUE_OBJ := $(OBJDIR)/device_glue.o
|
||||
|
||||
INCFLAGS = -I. -I.. -I$(BUILDDIR)/include -I../include
|
||||
INCFLAGS = -I. -I.. -I$(BUILDDIR)/include -I../include -I../include/plugin
|
||||
NVCUFLAGS += $(INCFLAGS) --compiler-options "-fPIC -fvisibility=hidden"
|
||||
CXXFLAGS += $(INCFLAGS)
|
||||
|
||||
@@ -47,7 +47,11 @@ endif
|
||||
define COMPILE_SYM
|
||||
@$(SAY) "Compiling" $2;\
|
||||
mkdir -p $(dir $1);\
|
||||
$(NVCC) $(NVCUFLAGS_SYM) $3 -dw $2 -o $1
|
||||
if [[ -n "$3" ]]; then\
|
||||
$(NVCC) $(NVCUFLAGS_SYM) $3 -dw $2 -o $1;\
|
||||
else\
|
||||
touch $2.empty.cu; $(NVCC) $(NVCUFLAGS_SYM) -dw $2.empty.cu -o $1; rm $2.empty.cu;\
|
||||
fi
|
||||
endef
|
||||
|
||||
DEPENDS.cu = $(NVCC) $(NVCUFLAGS) -M -dc $1
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace {
|
||||
}
|
||||
|
||||
template<typename T, typename RedOp>
|
||||
struct RunWorkColl<ncclFuncAllToAllPivot, T, RedOp, NCCL_ALGO_RING, NCCL_PROTO_SIMPLE> {
|
||||
struct RunWorkColl<ncclFuncAlltoAllPivot, T, RedOp, NCCL_ALGO_RING, NCCL_PROTO_SIMPLE> {
|
||||
__device__ __forceinline__ void run(int tid, int nThreads, struct ncclDevWorkColl* work) {
|
||||
using Proto = ProtoSimple<ALLTOALL_PIVOT_CHUNKSTEPS/ALLTOALL_PIVOT_SLICESTEPS, ALLTOALL_PIVOT_SLICESTEPS>;
|
||||
runRing<T, RedOp, Proto>(tid, nThreads, work);
|
||||
|
||||
@@ -150,7 +150,7 @@ struct ncclShmemData {
|
||||
struct ncclDevKernelArgs args;
|
||||
int channelId;
|
||||
int aborted;
|
||||
alignas(16) struct ncclDevComm comm;
|
||||
alignas(16) struct ncclKernelComm comm;
|
||||
alignas(16) struct ncclDevChannel channel;
|
||||
#ifdef ENABLE_WARP_SPEED
|
||||
int warpComm;
|
||||
@@ -502,7 +502,7 @@ __device__ __forceinline__ void profiler(int action) {
|
||||
ncclShmem.comm.workCompleted[ncclShmem.channelId].data[wc%MAX_PROFILER_EVENTS_PER_CHANNEL].counter = wc;
|
||||
}
|
||||
ncclShmem.channel.workCounter += ncclShmem.nWorks;
|
||||
if (action == FINI) ((ncclDevCommAndChannels*)ncclShmem.args.comm)->channels[ncclShmem.channelId].workCounter = ncclShmem.channel.workCounter;
|
||||
if (action == FINI) ((ncclKernelCommAndChannels*)ncclShmem.args.comm)->channels[ncclShmem.channelId].workCounter = ncclShmem.channel.workCounter;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -579,7 +579,7 @@ __device__ __forceinline__ void ncclKernelMain(struct ncclDevKernelArgs const* a
|
||||
/* set abort flag to 0 */
|
||||
if (tid == 0) {
|
||||
ncclShmem.aborted = 0;
|
||||
ncclShmem.channel.workCounter = ((ncclDevCommAndChannels*)ncclShmem.args.comm)->channels[ncclShmem.channelId].workCounter;
|
||||
ncclShmem.channel.workCounter = ((ncclKernelCommAndChannels*)ncclShmem.args.comm)->channels[ncclShmem.channelId].workCounter;
|
||||
}
|
||||
|
||||
// Use first 2 warps to load comm and channel, and remaining load work batch.
|
||||
@@ -587,14 +587,14 @@ __device__ __forceinline__ void ncclKernelMain(struct ncclDevKernelArgs const* a
|
||||
case 0:
|
||||
{ void* dst = &ncclShmem.comm;
|
||||
void* src = ncclShmem.args.comm;
|
||||
int bytes = sizeof(ncclDevComm);
|
||||
static_assert(sizeof(ncclDevComm) <= 16*WARP_SIZE, "ncclDevComm cannot be loaded by a single warp in one insn.");
|
||||
int bytes = sizeof(ncclKernelComm);
|
||||
static_assert(sizeof(ncclKernelComm) <= 16*WARP_SIZE, "ncclKernelComm cannot be loaded by a single warp in one insn.");
|
||||
copyToShmem16(tid, dst, src, bytes);
|
||||
} break;
|
||||
case 1:
|
||||
{ // Get address of channel without incurring indirect load from ncclDevComm::channels
|
||||
{ // Get address of channel without incurring indirect load from ncclKernelComm::channels
|
||||
void* dst = &ncclShmem.channel;
|
||||
void* src = &((ncclDevCommAndChannels*)ncclShmem.args.comm)->channels[ncclShmem.channelId];
|
||||
void* src = &((ncclKernelCommAndChannels*)ncclShmem.args.comm)->channels[ncclShmem.channelId];
|
||||
int bytes = sizeof(ncclDevChannel);
|
||||
static_assert(sizeof(ncclDevChannel) <= 16*WARP_SIZE, "ncclDevChannel cannot be loaded by a single warp in one insn.");
|
||||
copyToShmem16(tid-WARP_SIZE, dst, src, bytes);
|
||||
@@ -641,7 +641,7 @@ __device__ __forceinline__ void ncclKernelMain(struct ncclDevKernelArgs const* a
|
||||
__syncthreads();
|
||||
if(ncclShmem.warpChannelId[localWarpId] >= 0) {
|
||||
void* dst = &ncclShmem.warpChannel[localWarpId];
|
||||
void* src = &((ncclDevCommAndChannels*)ncclShmem.args.comm)->channels[ncclShmem.warpChannelId[localWarpId]];
|
||||
void* src = &((ncclKernelCommAndChannels*)ncclShmem.args.comm)->channels[ncclShmem.warpChannelId[localWarpId]];
|
||||
int bytes = sizeof(ncclDevChannel);
|
||||
static_assert(sizeof(ncclDevChannel) <= 16*WARP_SIZE, "ncclDevChannel cannot be loaded by a single warp in one insn.");
|
||||
// assert((tid-localWarpId*WARP_SIZE) >= 0 && (tid-localWarpId*WARP_SIZE) < WARP_SIZE);
|
||||
|
||||
@@ -3,9 +3,10 @@ import os
|
||||
import sys
|
||||
import subprocess
|
||||
from dataclasses import dataclass
|
||||
import shutil
|
||||
|
||||
# Order of colls, redops, tys, protos, algos must match src/include/device.h
|
||||
all_colls = ["Broadcast", "Reduce", "AllGather", "ReduceScatter", "AllReduce", "SendRecv", "", "", "AllToAllPivot", "AllToAllGda"]
|
||||
all_colls = ["Broadcast", "Reduce", "AllGather", "ReduceScatter", "AllReduce", "SendRecv", "", "", "AlltoAllPivot", "AllToAllGda"]
|
||||
all_redops = ["Sum","Prod","MinMax","PreMulSum","SumPostDiv"]
|
||||
all_tys = ["i8","u8","i32","u32","i64","u64","f16","f32","f64","bf16","f8e4m3","f8e5m2"]
|
||||
all_protos = ["LL","LL128","SIMPLE"]
|
||||
@@ -24,8 +25,11 @@ gensrc = sys.argv[1]
|
||||
|
||||
if os.path.exists(gensrc):
|
||||
for name in os.listdir(gensrc):
|
||||
os.remove(os.path.join(gensrc, name))
|
||||
#os.truncate(os.path.join(gensrc, name), 0)
|
||||
path = os.path.join(gensrc, name)
|
||||
if os.path.isfile(path):
|
||||
os.remove(path)
|
||||
elif os.path.isdir(path):
|
||||
shutil.rmtree(path)
|
||||
else:
|
||||
os.makedirs(gensrc)
|
||||
|
||||
@@ -64,7 +68,7 @@ else:
|
||||
# make ONLY_FUNCS="AllReduce RING SIMPLE * *|ReduceScatter RING LL * f32"
|
||||
# --- or ---
|
||||
# make ONLY_FUNCS="AllReduce RING SIMPLE|ReduceScatter RING LL * f32"
|
||||
# make ONLY_FUNCS="AllReduce RING/TREE LL/SIMPLE Sum/MinMax i8/u8/f16/f32/f64/bf16/f8e4m3/f8e5m2|AllGather RING LL/SIMPLE Sum i8|AllToAllPivot RING SIMPLE Sum i8|Broadcast RING LL/SIMPLE Sum i8|Reduce RING LL/SIMPLE Sum/MinMax i8/u8/f16/f32/f64/bf16/f8e4m3/f8e5m2|ReduceScatter RING LL/SIMPLE Sum/MinMax i8/u8/f16/f32/f64/bf16/f8e4m3/f8e5m2|SendRecv RING SIMPLE Sum i8"
|
||||
# make ONLY_FUNCS="AllReduce RING/TREE LL/SIMPLE Sum/MinMax i8/u8/f16/f32/f64/bf16/f8e4m3/f8e5m2|AllGather RING LL/SIMPLE Sum i8|AlltoAllPivot RING SIMPLE Sum i8|Broadcast RING LL/SIMPLE Sum i8|Reduce RING LL/SIMPLE Sum/MinMax i8/u8/f16/f32/f64/bf16/f8e4m3/f8e5m2|ReduceScatter RING LL/SIMPLE Sum/MinMax i8/u8/f16/f32/f64/bf16/f8e4m3/f8e5m2|SendRecv RING SIMPLE Sum i8"
|
||||
|
||||
# Paste all non-None arguments together with `sep`.
|
||||
def paste(sep, *args):
|
||||
@@ -79,14 +83,14 @@ func_pattern = sys.argv[6:7]
|
||||
if func_pattern and func_pattern[0]:
|
||||
func_pattern = func_pattern[0]
|
||||
else:
|
||||
func_pattern = "AllGather|AllReduce|AllToAllPivot|AllToAllGda|Broadcast|Reduce|ReduceScatter|SendRecv"
|
||||
func_pattern = "AllGather|AllReduce|AlltoAllPivot|AllToAllGda|Broadcast|Reduce|ReduceScatter|SendRecv"
|
||||
|
||||
################################################################################
|
||||
|
||||
algos_of_coll = {
|
||||
"AllGather": ["RING", "PAT"],
|
||||
"AllReduce": ["RING", "TREE"],
|
||||
"AllToAllPivot": ["RING"],
|
||||
"AlltoAllPivot": ["RING"],
|
||||
"AllToAllGda": ["RING"],
|
||||
"Broadcast": ["RING"],
|
||||
"Reduce": ["RING"],
|
||||
@@ -97,7 +101,7 @@ algos_of_coll = {
|
||||
protos_of_coll = {
|
||||
"AllGather": all_protos,
|
||||
"AllReduce": all_protos,
|
||||
"AllToAllPivot": ["SIMPLE"],
|
||||
"AlltoAllPivot": ["SIMPLE"],
|
||||
"AllToAllGda": ["SIMPLE"],
|
||||
"Broadcast": all_protos,
|
||||
"Reduce": all_protos,
|
||||
@@ -108,7 +112,7 @@ protos_of_coll = {
|
||||
redops_of_coll = {
|
||||
"AllGather": ["Sum"],
|
||||
"AllReduce": all_redops,
|
||||
"AllToAllPivot": ["Sum"],
|
||||
"AlltoAllPivot": ["Sum"],
|
||||
"AllToAllGda": ["Sum"],
|
||||
"Broadcast": ["Sum"],
|
||||
"Reduce": all_redops,
|
||||
@@ -119,7 +123,7 @@ redops_of_coll = {
|
||||
tys_of_coll = {
|
||||
"AllGather": ["i8"],
|
||||
"AllReduce": all_tys,
|
||||
"AllToAllPivot": ["i8"],
|
||||
"AlltoAllPivot": ["i8"],
|
||||
"AllToAllGda": ["i8"],
|
||||
"Broadcast": ["i8"],
|
||||
"Reduce": all_tys,
|
||||
@@ -130,7 +134,7 @@ tys_of_coll = {
|
||||
acc_of_coll = {
|
||||
"AllGather": ["0"],
|
||||
"AllReduce": all_accs,
|
||||
"AllToAllPivot": ["0"],
|
||||
"AlltoAllPivot": ["0"],
|
||||
"AllToAllGda": ["0"],
|
||||
"Broadcast": ["0"],
|
||||
"Reduce": ["0"],
|
||||
@@ -141,7 +145,7 @@ acc_of_coll = {
|
||||
pipelines_of_coll = {
|
||||
"AllGather": ["0"],
|
||||
"AllReduce": all_pipelines,
|
||||
"AllToAllPivot": ["0"],
|
||||
"AlltoAllPivot": ["0"],
|
||||
"AllToAllGda": ["0"],
|
||||
"Broadcast": ["0"],
|
||||
"Reduce": all_pipelines,
|
||||
@@ -153,7 +157,7 @@ pipelined_types = ["bf16"]
|
||||
coll_camel_to_lower = {
|
||||
"AllGather": "all_gather",
|
||||
"AllReduce": "all_reduce",
|
||||
"AllToAllPivot": "alltoall_pivot",
|
||||
"AlltoAllPivot": "alltoall_pivot",
|
||||
"AllToAllGda": "alltoall_gda",
|
||||
"Broadcast": "broadcast",
|
||||
"Reduce": "reduce",
|
||||
@@ -510,7 +514,7 @@ with open(os.path.join(gensrc, "host_table.cpp"), "w") as f:
|
||||
)
|
||||
if fn.coll == "Broadcast":
|
||||
key = ((coll_idx & 0x3F) | ((proto_idx & 0x3F) << 8))
|
||||
if fn.coll in ["SendRecv", "AllToAllPivot", "AllToAllGda"]:
|
||||
if fn.coll in ["SendRecv", "AlltoAllPivot", "AllToAllGda"]:
|
||||
key = ((coll_idx & 0x3F))
|
||||
|
||||
out(f' {{{key}, {fn_id}}}, {comment}\n')
|
||||
|
||||
@@ -93,7 +93,7 @@ __device__ __forceinline__ static void mscclReduce(int c, int numReductions, int
|
||||
|
||||
template<typename T, typename RedOp, typename Proto, bool fullOps>
|
||||
__device__ __forceinline__ void mscclRunInterpreter(
|
||||
struct ncclDevComm* comm, struct mscclAlgo* algo, struct mscclWork* work) {
|
||||
struct ncclKernelComm* comm, struct mscclAlgo* algo, struct mscclWork* work) {
|
||||
const int tid = threadIdx.x;
|
||||
const int bid = blockIdx.x;
|
||||
const int nthreads = MSCCL_MAX_NTHREADS;
|
||||
@@ -120,12 +120,12 @@ __device__ __forceinline__ void mscclRunInterpreter(
|
||||
case 0:
|
||||
dst = &ncclShmem.comm;
|
||||
src = comm;
|
||||
bytes = sizeof(ncclDevComm);
|
||||
bytes = sizeof(ncclKernelComm);
|
||||
break;
|
||||
case 1:
|
||||
// Get address of channel without incurring indirect load from ncclDevComm::channels
|
||||
// Get address of channel without incurring indirect load from ncclKernelComm::channels
|
||||
dst = &ncclShmem.channel;
|
||||
src = &((ncclDevCommAndChannels*)comm)->channels[channelId];
|
||||
src = &((ncclKernelCommAndChannels*)comm)->channels[channelId];
|
||||
bytes = sizeof(ncclDevChannel);
|
||||
break;
|
||||
case 2:
|
||||
@@ -372,13 +372,13 @@ __device__ __forceinline__ void mscclRunInterpreter(
|
||||
}
|
||||
|
||||
#define MSCCL_IMPL_KERNEL_ENTRY_FUNC_DEVREDOP_TYPE(devredop, type, fullOps) \
|
||||
__global__ void MSCCL_KERNEL_ENTRY_NAME(devredop, type, LL, fullOps)(struct ncclDevComm* comm, struct mscclAlgo* algo, struct mscclWork* work) { \
|
||||
__global__ void MSCCL_KERNEL_ENTRY_NAME(devredop, type, LL, fullOps)(struct ncclKernelComm* comm, struct mscclAlgo* algo, struct mscclWork* work) { \
|
||||
mscclRunInterpreter<type, Func##devredop<type>, ProtoLL, fullOps>(comm, algo, work); \
|
||||
} \
|
||||
__global__ void MSCCL_KERNEL_ENTRY_NAME(devredop, type, LL128, fullOps)(struct ncclDevComm* comm, struct mscclAlgo* algo, struct mscclWork* work) { \
|
||||
__global__ void MSCCL_KERNEL_ENTRY_NAME(devredop, type, LL128, fullOps)(struct ncclKernelComm* comm, struct mscclAlgo* algo, struct mscclWork* work) { \
|
||||
mscclRunInterpreter<type, Func##devredop<type>, ProtoLL128, fullOps>(comm, algo, work); \
|
||||
} \
|
||||
__global__ void MSCCL_KERNEL_ENTRY_NAME(devredop, type, Simple, fullOps)(struct ncclDevComm* comm, struct mscclAlgo* algo, struct mscclWork* work) { \
|
||||
__global__ void MSCCL_KERNEL_ENTRY_NAME(devredop, type, Simple, fullOps)(struct ncclKernelComm* comm, struct mscclAlgo* algo, struct mscclWork* work) { \
|
||||
mscclRunInterpreter<type, Func##devredop<type>, ProtoSimple<MSCCL_CHUNKSTEPS/MSCCL_SLICESTEPS, MSCCL_SLICESTEPS, 0, 2>, fullOps>(comm, algo, work); \
|
||||
}
|
||||
|
||||
|
||||
@@ -1,35 +1,36 @@
|
||||
// Modification Copyright (c) Advanced Micro Devices, Inc., or its affiliates.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "symmetric.h"
|
||||
#include "sym_kernels.h"
|
||||
#include "symmetric/kernel.h"
|
||||
#include "symmetric/primitives.h"
|
||||
|
||||
template<int BytePerPack, int UnrollPacks, int UnrollPeers>
|
||||
static __device__ void bcastDeep(
|
||||
ncclSymPrims& prim, int tn, int t, bool waitNeeded,
|
||||
char* inputHere, char* outputRank0, bool inPlace, int nIters
|
||||
ncclSymkArgsHandler const& handler, int tn, int t,
|
||||
bool waitNeeded, ncclLsaBarrierSession<ncclCoopCta>& bar,
|
||||
ncclSymPtr<char> input, ncclSymPtr<char> output, bool inPlace, int nIters
|
||||
) {
|
||||
using Pack = BytePack<BytePerPack>;
|
||||
int wn = tn/WARP_SIZE;
|
||||
int w = t/WARP_SIZE;
|
||||
int lane = t%WARP_SIZE;
|
||||
int const& rank = prim.rank;
|
||||
int const& nRanks = prim.nRanks;
|
||||
uint32_t const& stride4G = prim.stride4G;
|
||||
Pack* inpHere = (Pack*)inputHere + intptr_t(w)*UnrollPacks*WARP_SIZE + lane;
|
||||
Pack* outRank0 = (Pack*)outputRank0 + intptr_t(w)*UnrollPacks*WARP_SIZE + lane;
|
||||
int const& rank = handler.comm.rank;
|
||||
int const& nRanks = handler.comm.nRanks;
|
||||
|
||||
Pack* inpPacks = (Pack*)input.localPtr() + intptr_t(w)*UnrollPacks*WARP_SIZE + lane;
|
||||
ncclSymPtr<Pack> outPacks = (ncclSymPtr<Pack>)output + intptr_t(w)*UnrollPacks*WARP_SIZE + lane;
|
||||
Pack tmp[UnrollPacks];
|
||||
|
||||
nIters -= w;
|
||||
if (0 < nIters) {
|
||||
#pragma unroll
|
||||
for (int u=0; u < UnrollPacks; u++) {
|
||||
tmp[u] = inpHere[u*WARP_SIZE];
|
||||
tmp[u] = inpPacks[u*WARP_SIZE];
|
||||
}
|
||||
}
|
||||
|
||||
if (waitNeeded) prim.barrierWait(ncclCoopCta(), /*acquire=*/false);
|
||||
if (waitNeeded) bar.wait(ncclCoopCta(), cuda::memory_order_relaxed);
|
||||
|
||||
if (0 < nIters) {
|
||||
while (true) {
|
||||
@@ -47,21 +48,21 @@ static __device__ void bcastDeep(
|
||||
if (partial && dr == nRanks) break;
|
||||
#pragma unroll UnrollPacks
|
||||
for (int u=0; u < UnrollPacks; u++) {
|
||||
add4G(outRank0, r*stride4G)[u*WARP_SIZE] = tmp[u];
|
||||
outPacks.lsaPtr(r)[u*WARP_SIZE] = tmp[u];
|
||||
}
|
||||
if (++r == nRanks) r = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
inpHere += intptr_t(wn)*UnrollPacks*WARP_SIZE;
|
||||
outRank0 += intptr_t(wn)*UnrollPacks*WARP_SIZE;
|
||||
inpPacks += intptr_t(wn)*UnrollPacks*WARP_SIZE;
|
||||
outPacks += intptr_t(wn)*UnrollPacks*WARP_SIZE;
|
||||
nIters -= wn;
|
||||
if (nIters <= 0) break;
|
||||
|
||||
// Load data for next iteration.
|
||||
#pragma unroll
|
||||
for (int u=0; u < UnrollPacks; u++) {
|
||||
tmp[u] = inpHere[u*WARP_SIZE];
|
||||
tmp[u] = inpPacks[u*WARP_SIZE];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -69,18 +70,17 @@ static __device__ void bcastDeep(
|
||||
|
||||
template<int UnrollPeers, typename T>
|
||||
static __device__ void bcastEnds(
|
||||
ncclSymPrims& prim, int tn, int t,
|
||||
T* inputHere, T* outputRank0, bool inPlace, size_t nElts, uint32_t nPreElts, size_t nSufElts
|
||||
ncclSymkArgsHandler const& handler, int tn, int t,
|
||||
ncclSymPtr<T> input, ncclSymPtr<T> output, bool inPlace, size_t nElts, uint32_t nPreElts, size_t nSufElts
|
||||
) {
|
||||
int const& rank = prim.rank;
|
||||
int const& nRanks = prim.nRanks;
|
||||
uint32_t const& stride4G = prim.stride4G;
|
||||
BytePack<sizeof(T)>* inpHere = (BytePack<sizeof(T)>*)inputHere;
|
||||
BytePack<sizeof(T)>* outRank0 = (BytePack<sizeof(T)>*)outputRank0;
|
||||
int const& rank = handler.comm.rank;
|
||||
int const& nRanks = handler.comm.nRanks;
|
||||
BytePack<sizeof(T)>* inpPacks = (BytePack<sizeof(T)>*)input.localPtr();
|
||||
ncclSymPtr<BytePack<sizeof(T)>> outPacks = (ncclSymPtr<BytePack<sizeof(T)>>)output;
|
||||
#pragma unroll 1
|
||||
for (size_t i = t; i < nPreElts+nSufElts; i += tn) {
|
||||
size_t elt = i < nPreElts ? i : nElts-nPreElts-nSufElts+i;
|
||||
BytePack<sizeof(T)> tmp = inpHere[elt];
|
||||
BytePack<sizeof(T)> tmp = inpPacks[elt];
|
||||
int dr = inPlace ? 1 : 0;
|
||||
int r = rank + dr;
|
||||
if (r == nRanks) r = 0;
|
||||
@@ -88,14 +88,14 @@ static __device__ void bcastEnds(
|
||||
for (; dr + UnrollPeers <= nRanks; dr += UnrollPeers) {
|
||||
#pragma unroll UnrollPeers
|
||||
for (int u=0; u < UnrollPeers; u++) {
|
||||
*add4G(outRank0+elt, r*stride4G) = tmp;
|
||||
outPacks.lsaPtr(r)[elt] = tmp;
|
||||
if (++r == nRanks) r = 0;
|
||||
}
|
||||
}
|
||||
#pragma unroll UnrollPeers
|
||||
for (int u=0; u < UnrollPeers; u++) {
|
||||
if (dr+u == nRanks) break;
|
||||
*add4G(outRank0+elt, r*stride4G) = tmp;
|
||||
outPacks.lsaPtr(r)[elt] = tmp;
|
||||
if (++r == nRanks) r = 0;
|
||||
}
|
||||
}
|
||||
@@ -103,95 +103,95 @@ static __device__ void bcastEnds(
|
||||
|
||||
template<typename T>
|
||||
static __device__ void bcast(
|
||||
ncclSymPrims& prim, int tn, int t, bool waitNeeded, T* input, T* output, size_t nElts
|
||||
ncclSymkArgsHandler const& handler, int tn, int t, int nBlocks,
|
||||
bool waitNeeded, ncclLsaBarrierSession<ncclCoopCta>& bar,
|
||||
ncclSymPtr<T> input, ncclSymPtr<T> output, size_t nElts
|
||||
) {
|
||||
bool inPlace = (input == output);
|
||||
// Mpve to rank=0
|
||||
output = prim.peerPtr(0, output);
|
||||
|
||||
uintptr_t inputUptr = reinterpret_cast<uintptr_t>(input);
|
||||
uintptr_t outputUptr = reinterpret_cast<uintptr_t>(output);
|
||||
size_t nBytes = nElts*sizeof(T);
|
||||
uint32_t nBlocks_rcp32 = nccl::utility::idivRcp32_upto64(nBlocks);
|
||||
|
||||
uint32_t nPreBytes = (128u - inputUptr)%128u;
|
||||
uint32_t nPreBytes = (16 - input.offset)%16;
|
||||
nPreBytes = min((size_t)nPreBytes, nBytes);
|
||||
uintptr_t cursor = nPreBytes;
|
||||
|
||||
constexpr int MinWarpPerBlock = 4;
|
||||
|
||||
if ((inputUptr-outputUptr)%16 == 0) {
|
||||
constexpr int BytePerPack = 16, UnrollPacks = 1, UnrollPeers = 1;
|
||||
if ((input.offset - output.offset)%16 == 0) {
|
||||
constexpr int BytePerPack = 16, UnrollPacks = 4, UnrollPeers = 2;
|
||||
constexpr int BytePerChunk = MinWarpPerBlock*UnrollPacks*WARP_SIZE*BytePerPack;
|
||||
uint32_t chunks = (nBytes-cursor)/BytePerChunk;
|
||||
chunks -= imodFast32(chunks, prim.nBlocks, prim.nBlocks_rcp32);
|
||||
chunks -= imodFast32(chunks, nBlocks, nBlocks_rcp32);
|
||||
if (chunks != 0) {
|
||||
uintptr_t cursorAfter = cursor + uintptr_t(chunks)*BytePerChunk;
|
||||
bcastDeep<BytePerPack, UnrollPacks, UnrollPeers>(
|
||||
prim, tn, t, waitNeeded,
|
||||
(char*)input + cursor, (char*)output + cursor, inPlace,
|
||||
chunks*MinWarpPerBlock
|
||||
handler, tn, t, waitNeeded, bar,
|
||||
(ncclSymPtr<char>)input + cursor,
|
||||
(ncclSymPtr<char>)output + cursor,
|
||||
inPlace, chunks*MinWarpPerBlock
|
||||
);
|
||||
cursor = cursorAfter;
|
||||
waitNeeded = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (sizeof(T) == 4 || (sizeof(T) < 4 && (inputUptr-outputUptr)%4 == 0)) {
|
||||
constexpr int BytePerPack = 4, UnrollPacks = 1, UnrollPeers = 1;
|
||||
constexpr int BytePerChunk = MinWarpPerBlock*UnrollPacks*WARP_SIZE*BytePerPack;
|
||||
uint32_t chunks = (nBytes-cursor)/BytePerChunk;
|
||||
chunks -= imodFast32(chunks, prim.nBlocks, prim.nBlocks_rcp32);
|
||||
if (sizeof(T) == 4 || (sizeof(T) < 4 && (input.offset - output.offset)%4 == 0)) {
|
||||
chunks -= imodFast32(chunks, nBlocks, nBlocks_rcp32);
|
||||
if (chunks != 0) {
|
||||
uintptr_t cursorAfter = cursor + uintptr_t(chunks)*BytePerChunk;
|
||||
bcastDeep<(sizeof(T) <= BytePerPack ? BytePerPack : 0), UnrollPacks, UnrollPeers>(
|
||||
prim, tn, t, waitNeeded,
|
||||
(char*)input + cursor, (char*)output + cursor, inPlace,
|
||||
chunks*MinWarpPerBlock
|
||||
handler, tn, t, waitNeeded, bar,
|
||||
(ncclSymPtr<char>)input + cursor,
|
||||
(ncclSymPtr<char>)output + cursor,
|
||||
inPlace, chunks*MinWarpPerBlock
|
||||
);
|
||||
cursor = cursorAfter;
|
||||
waitNeeded = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (waitNeeded) prim.barrierWait(ncclCoopCta(), /*acquire=*/false);
|
||||
if (waitNeeded) bar.wait(ncclCoopCta(), cuda::memory_order_relaxed);
|
||||
|
||||
constexpr int UnrollPeers = 8;
|
||||
size_t nSufElts = (nBytes-cursor)/sizeof(T);
|
||||
bcastEnds<UnrollPeers>(prim, tn, t, input, output, inPlace, nElts, nPreBytes/sizeof(T), nSufElts);
|
||||
bcastEnds<UnrollPeers>(handler, tn, t, input, output, inPlace, nElts, nPreBytes/sizeof(T), nSufElts);
|
||||
}
|
||||
|
||||
__device__ __forceinline__ void ncclSymRun_AllGather_ST(ncclSymDevArgs const* args) {
|
||||
ncclSymPrims prim(args->comm, ncclSymPrims_UseBarrier);
|
||||
int const& rank = prim.rank;
|
||||
__device__ __forceinline__ void ncclSymkRun_AllGather_ST(ncclSymkDevWorkArgs const* args) {
|
||||
ncclSymkArgsHandler handler{args};
|
||||
ncclLsaBarrierSession<ncclCoopCta> bar{
|
||||
ncclCoopCta(), handler.comm, ncclTeamTagLsa(), blockIdx.x
|
||||
};
|
||||
int const& rank = handler.comm.rank;
|
||||
|
||||
// Threads numbered over rank.
|
||||
int bt = flattenIx(threadIdx.x%WARP_SIZE, WARP_SIZE,
|
||||
prim.block, prim.nBlocks,
|
||||
threadIdx.x/WARP_SIZE, blockDim.x/WARP_SIZE);
|
||||
int btn = prim.nBlocks*blockDim.x;
|
||||
bar.arrive(ncclCoopCta(), cuda::memory_order_relaxed);
|
||||
|
||||
prim.barrierArrive(ncclCoopCta(), /*release=*/false);
|
||||
//prim.barrierWait(ncclCoopCta(), /*acquire=*/false);
|
||||
bool waitNeeded = true;
|
||||
handler.forEachWork<char>(
|
||||
[&]__device__(int block, int nBlocks, size_t nElts, size_t nAllElts,
|
||||
ncclSymPtr<char> input, ncclSymPtr<char> output) {
|
||||
// Threads numbered over rank.
|
||||
int bt = flattenIx(threadIdx.x%WARP_SIZE, WARP_SIZE,
|
||||
block, nBlocks,
|
||||
threadIdx.x/WARP_SIZE, blockDim.x/WARP_SIZE);
|
||||
int btn = nBlocks*blockDim.x;
|
||||
|
||||
bcast(prim, btn, bt, /*waitNeeded=*/true, (char*)args->input, (char*)args->output + rank*args->nElts, args->nElts);
|
||||
bcast(handler, btn, bt, nBlocks, waitNeeded, bar, input, output + rank*nAllElts, nElts);
|
||||
|
||||
prim.barrierArrive(ncclCoopCta(), /*release=*/true);
|
||||
prim.barrierWait(ncclCoopCta(), /*acquire=*/false);
|
||||
waitNeeded = false;
|
||||
}
|
||||
);
|
||||
|
||||
bar.sync(ncclCoopCta(), cuda::memory_order_release);
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
static __device__ void bcastMultimem(
|
||||
ncclSymPrims& prim, int tn, int t, T* input, T* output, size_t nElts
|
||||
ncclSymkArgsHandler& handler, int tn, int t, ncclSymPtr<T> input, ncclSymPtr<T> output, size_t nElts
|
||||
) {
|
||||
// Move output to multimem
|
||||
output = prim.multimemPtr(output);
|
||||
|
||||
uintptr_t inputUptr = reinterpret_cast<uintptr_t>(input);
|
||||
uintptr_t outputUptr = reinterpret_cast<uintptr_t>(output);
|
||||
size_t nBytes = nElts*sizeof(T);
|
||||
|
||||
uint32_t nPreBytes = (16-inputUptr)%16;
|
||||
uintptr_t inputUptr = reinterpret_cast<uintptr_t>(input.localPtr());
|
||||
uintptr_t outputUptr = reinterpret_cast<uintptr_t>(output.multimemPtr(handler.comm.lsaMultimem));
|
||||
uint32_t nPreBytes = (16 - input.offset)%16;
|
||||
nPreBytes = min((size_t)nPreBytes, nBytes);
|
||||
uintptr_t nSufBytes;
|
||||
|
||||
@@ -230,51 +230,52 @@ static __device__ void bcastMultimem(
|
||||
uintptr_t cursor = i < nPreBytes ? i : nBytes-nSufBytes+(i-nPreBytes);
|
||||
BytePack<sizeof(T)> val = *reinterpret_cast<BytePack<sizeof(T)>*>(inputUptr + cursor);
|
||||
multimem_st_global(outputUptr + cursor, val);
|
||||
cursor += tn*sizeof(T);
|
||||
}
|
||||
}
|
||||
|
||||
__device__ __forceinline__ void ncclSymRun_AllGather_STMC(ncclSymDevArgs const* args) {
|
||||
ncclSymPrims prim(args->comm, ncclSymPrims_UseBarrier|ncclSymPrims_UseMultimem);
|
||||
int const& rank = prim.rank;
|
||||
__device__ __forceinline__ void ncclSymkRun_AllGather_STMC(ncclSymkDevWorkArgs const* args) {
|
||||
ncclSymkArgsHandler handler{args};
|
||||
ncclLsaBarrierSession<ncclCoopCta> bar(
|
||||
ncclCoopCta(), handler.comm, ncclTeamTagLsa(), blockIdx.x, /*multimem=*/true
|
||||
);
|
||||
int const& rank = handler.comm.rank;
|
||||
|
||||
char* input = args->input;
|
||||
char* output = args->output;
|
||||
size_t bytes = args->nElts;
|
||||
// Round robin memory to blocks.
|
||||
int t = flattenIx(threadIdx.x%WARP_SIZE, WARP_SIZE,
|
||||
prim.block, prim.nBlocks,
|
||||
threadIdx.x/WARP_SIZE, blockDim.x/WARP_SIZE);
|
||||
int tn = prim.nBlocks*blockDim.x;
|
||||
bar.sync(ncclCoopCta(), cuda::memory_order_relaxed);
|
||||
|
||||
prim.barrierArrive(ncclCoopCta(), /*release=*/false);
|
||||
prim.barrierWait(ncclCoopCta(), /*acquire=*/false);
|
||||
handler.forEachWork<char>(
|
||||
[&]__device__(int block, int nBlocks, size_t nElts, size_t nAllElts,
|
||||
ncclSymPtr<char> input, ncclSymPtr<char> output) {
|
||||
// Round robin memory to blocks.
|
||||
int t = flattenIx(threadIdx.x%WARP_SIZE, WARP_SIZE,
|
||||
block, nBlocks,
|
||||
threadIdx.x/WARP_SIZE, blockDim.x/WARP_SIZE);
|
||||
int tn = nBlocks*blockDim.x;
|
||||
|
||||
bcastMultimem(prim, tn, t, input, output + rank*bytes, bytes);
|
||||
bcastMultimem(handler, tn, t, input, output + rank*nAllElts, nElts);
|
||||
}
|
||||
);
|
||||
|
||||
prim.barrierArrive(ncclCoopCta(), /*release=*/true);
|
||||
prim.barrierWait(ncclCoopCta(), /*acquire=*/false);
|
||||
bar.sync(ncclCoopCta(), cuda::memory_order_release);
|
||||
}
|
||||
|
||||
template<typename EltType>
|
||||
static __device__ void allgather_LL_body(
|
||||
ncclSymPrims &prim, EltType* input, EltType* output, int nElts, int nPacks, int nStrideElts
|
||||
ncclSymkArgsHandler& handler, ncclLLA2ASession<ncclCoopCta>& lla2a,
|
||||
EltType* input, EltType* output, int nElts, int nPacks, int nStrideElts
|
||||
) {
|
||||
using Pack = BytePack<8>;
|
||||
constexpr int EltPerPack = 8/sizeof(EltType);
|
||||
|
||||
ncclCoopCta cta;
|
||||
int rank = prim.rank;
|
||||
int nRanks = prim.nRanks;
|
||||
constexpr int tn = ncclSymMaxThreads;
|
||||
int const& rank = handler.comm.rank;
|
||||
int const& nRanks = handler.comm.nRanks;
|
||||
int t = threadIdx.x;
|
||||
constexpr int tn = ncclSymkMaxThreads;
|
||||
|
||||
#pragma unroll 1
|
||||
while (0 < nElts) {
|
||||
int nIterPacks = min(nPacks, tn);
|
||||
if (t < nIterPacks) {
|
||||
Pack x = loadPack<Pack>(input, t*EltPerPack, nElts);
|
||||
prim.bcastLL(/*slot=*/nIterPacks*rank + t, x);
|
||||
lla2a.bcast(/*slot=*/nIterPacks*rank + t, x);
|
||||
}
|
||||
|
||||
int tn_div_nPacks = tn/nIterPacks;
|
||||
@@ -287,7 +288,7 @@ static __device__ void allgather_LL_body(
|
||||
#pragma unroll 1
|
||||
for (int i = t; i < (nRanks*nIterPacks & -(Unroll*tn)); i += Unroll*tn) {
|
||||
Pack got[Unroll];
|
||||
prim.template recvLL<Unroll, Unroll>(i, Unroll, tn, /*&*/got);
|
||||
lla2a.template recvUnrolled<Unroll, Unroll>(i, Unroll, tn, /*&*/got);
|
||||
#pragma unroll
|
||||
for (int u=0; u < Unroll; u++) {
|
||||
storePack<Pack>(output + peer*nStrideElts, pack*EltPerPack, nElts, got[u]);
|
||||
@@ -302,7 +303,7 @@ static __device__ void allgather_LL_body(
|
||||
if (i + n*tn < nRanks*nIterPacks) n += 1;
|
||||
if (n != 0) {
|
||||
Pack got[Unroll];
|
||||
prim.template recvLL<1, Unroll>(i, n, tn, /*&*/got);
|
||||
lla2a.template recvUnrolled<1, Unroll>(i, n, tn, /*&*/got);
|
||||
#pragma unroll
|
||||
for (int u=0; u < Unroll; u++) {
|
||||
if (u != 0 && u == n) break;
|
||||
@@ -316,7 +317,7 @@ static __device__ void allgather_LL_body(
|
||||
// The non-unrolled but "obviously correct" implementation for reference.
|
||||
#pragma unroll 1
|
||||
for (int i = t; i < nRanks*nIterPacks; i += tn) {
|
||||
Pack got = prim.template recvLL<Pack>(i);
|
||||
Pack got = lla2a.template recv<Pack>(i);
|
||||
storePack(output + peer*nStrideElts, pack*EltPerPack, nElts, got);
|
||||
peer += tn_div_nPacks;
|
||||
pack += tn_mod_nPacks;
|
||||
@@ -324,7 +325,7 @@ static __device__ void allgather_LL_body(
|
||||
}
|
||||
#endif
|
||||
|
||||
prim.endLL(cta);
|
||||
lla2a.endEpoch(ncclCoopCta());
|
||||
|
||||
input += tn*EltPerPack;
|
||||
output += tn*EltPerPack;
|
||||
@@ -333,38 +334,41 @@ static __device__ void allgather_LL_body(
|
||||
}
|
||||
}
|
||||
|
||||
static __device__ void ncclSymRun_AllGather_LL_impl(ncclSymDevArgs const* args, bool multimem) {
|
||||
ncclSymPrims prim(args->comm, ncclSymPrims_UseLL | multimem*ncclSymPrims_UseMultimem);
|
||||
static __device__ void ncclSymkRun_AllGather_LL_impl(ncclSymkDevWorkArgs const* args, bool multimem) {
|
||||
ncclSymkArgsHandler handler{args};
|
||||
ncclLLA2ASession<ncclCoopCta> lla2a(
|
||||
ncclCoopCta(), handler.comm, ncclTeamLsa(handler.comm), handler.lsaLLA2A, blockIdx.x, /*maxElts=*/ncclSymkMaxThreads, multimem, handler.comm.lsaMultimem
|
||||
);
|
||||
|
||||
using Pack = BytePack<8>;
|
||||
constexpr int BytePerPack = 8;
|
||||
int nElts = args->nElts;
|
||||
int nPacks = divUp(nElts, BytePerPack);
|
||||
|
||||
uint32_t nPackPerBlock, nPackModBlock;
|
||||
idivmodFast32(&nPackPerBlock, &nPackModBlock, nPacks, prim.nBlocks, prim.nBlocks_rcp32);
|
||||
int blockPackBegin = prim.block*nPackPerBlock + minval<int>(prim.block, nPackModBlock);
|
||||
int blockPackEnd = blockPackBegin + nPackPerBlock + (prim.block < nPackModBlock ? 1 : 0);
|
||||
int nBlockPacks = blockPackEnd - blockPackBegin;
|
||||
int nBlockElts = nElts - blockPackBegin*BytePerPack;
|
||||
nBlockElts = min(nBlockElts, nBlockPacks*BytePerPack);
|
||||
char* blockInput = args->input + blockPackBegin*BytePerPack;
|
||||
char* blockOutput = args->output + blockPackBegin*BytePerPack;
|
||||
handler.singleWork<char>(
|
||||
[&]__device__(int nElts, int nAllElts,
|
||||
ncclSymPtr<char> input, ncclSymPtr<char> output) {
|
||||
int nPacks = divUp(nElts, BytePerPack);
|
||||
|
||||
uint32_t lowBits = args->nElts;
|
||||
lowBits |= (uint32_t)reinterpret_cast<uintptr_t>(args->input);
|
||||
lowBits |= (uint32_t)reinterpret_cast<uintptr_t>(args->output);
|
||||
if (__builtin_expect(lowBits%8 == 0, true)) {
|
||||
// NOTE: Specializing for 8-byte alignment in one case help at size=65K: 8.9us vs 5.6us
|
||||
allgather_LL_body(prim, (BytePack<8>*)blockInput, (BytePack<8>*)blockOutput, nBlockElts/8, nBlockPacks, nElts/8);
|
||||
} else {
|
||||
allgather_LL_body(prim, blockInput, blockOutput, nBlockElts, nBlockPacks, nElts);
|
||||
}
|
||||
char* blockInput = input.localPtr();
|
||||
char* blockOutput = output.localPtr();
|
||||
|
||||
uint32_t lowBits = nElts;
|
||||
lowBits |= (uintptr_t)blockInput;
|
||||
lowBits |= (uintptr_t)blockOutput;
|
||||
if (__builtin_expect(lowBits%8 == 0, true)) {
|
||||
// NOTE: Specializing for 8-byte alignment in one case help at size=65K: 8.9us vs 5.6us
|
||||
allgather_LL_body(handler, lla2a, (BytePack<8>*)blockInput, (BytePack<8>*)blockOutput,
|
||||
nElts/8, nPacks, nAllElts/8);
|
||||
} else {
|
||||
allgather_LL_body(handler, lla2a, blockInput, blockOutput, nElts, nPacks, nAllElts);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
__device__ __forceinline__ void ncclSymRun_AllGather_LL(ncclSymDevArgs const* args) {
|
||||
ncclSymRun_AllGather_LL_impl(args, /*multimem=*/false);
|
||||
__device__ __forceinline__ void ncclSymkRun_AllGather_LL(ncclSymkDevWorkArgs const* args) {
|
||||
ncclSymkRun_AllGather_LL_impl(args, /*multimem=*/false);
|
||||
}
|
||||
|
||||
__device__ __forceinline__ void ncclSymRun_AllGather_LLMC(ncclSymDevArgs const* args) {
|
||||
ncclSymRun_AllGather_LL_impl(args, /*multimem=*/true);
|
||||
__device__ __forceinline__ void ncclSymkRun_AllGather_LLMC(ncclSymkDevWorkArgs const* args) {
|
||||
ncclSymkRun_AllGather_LL_impl(args, /*multimem=*/true);
|
||||
}
|
||||
|
||||
@@ -1,38 +1,41 @@
|
||||
// Modification Copyright (c) Advanced Micro Devices, Inc., or its affiliates.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "symmetric.h"
|
||||
#include "sym_kernels.h"
|
||||
#include "nccl_device.h"
|
||||
#include "symmetric/kernel.h"
|
||||
#include "symmetric/primitives.h"
|
||||
|
||||
template<int BytePerPack, int UnrollPacks, int UnrollPeers, typename T, typename Red>
|
||||
static __device__ __forceinline__ void allreduceDeep(
|
||||
ncclSymPrims& prim, int tn, int t, bool waitNeeded,
|
||||
Red red, char* inputRank0, char* outputRank0, int32_t nIters
|
||||
ncclSymkArgsHandler const& handler, int tn, int t,
|
||||
bool waitNeeded, ncclLsaBarrierSession<ncclCoopCta>& bar,
|
||||
Red red, ncclSymPtr<char> input, ncclSymPtr<char> output, int32_t nIters
|
||||
) {
|
||||
using Pack = BytePack<BytePerPack>;
|
||||
using Acc = typename Red::EltType;
|
||||
using AccPack = BytePack<BytePerPack*sizeof(Acc)/sizeof(T)>;
|
||||
|
||||
ncclTeam world = ncclTeamWorld(handler.comm);
|
||||
int wn = tn/WARP_SIZE;
|
||||
int w = t/WARP_SIZE;
|
||||
int lane = t%WARP_SIZE;
|
||||
int const& rank = prim.rank;
|
||||
int const& nRanks = prim.nRanks;
|
||||
uint32_t const& stride4G = prim.stride4G;
|
||||
Pack* inpRank0 = (Pack*)inputRank0 + intptr_t(w)*UnrollPacks*WARP_SIZE + lane;
|
||||
Pack* outRank0 = (Pack*)outputRank0 + intptr_t(w)*UnrollPacks*WARP_SIZE + lane;
|
||||
int const& rank = handler.comm.rank;
|
||||
int const& nRanks = handler.comm.nRanks;
|
||||
|
||||
ncclSymPtr<Pack> inpPacks = (ncclSymPtr<Pack>)input + intptr_t(w)*UnrollPacks*WARP_SIZE + lane;
|
||||
ncclSymPtr<Pack> outPacks = (ncclSymPtr<Pack>)output + intptr_t(w)*UnrollPacks*WARP_SIZE + lane;
|
||||
Pack acc0[UnrollPacks];
|
||||
|
||||
nIters -= w;
|
||||
if (0 < nIters) {
|
||||
#pragma unroll
|
||||
for (int u=0; u < UnrollPacks; u++) {
|
||||
acc0[u] = add4G(inpRank0, rank*stride4G)[u*WARP_SIZE];
|
||||
acc0[u] = inpPacks.peerPtr(world, rank)[u*WARP_SIZE];
|
||||
}
|
||||
}
|
||||
|
||||
if (waitNeeded) prim.barrierWait(ncclCoopCta(), /*acquire=*/false);
|
||||
if (waitNeeded) bar.wait(ncclCoopCta(), cuda::memory_order_relaxed);
|
||||
|
||||
if (0 < nIters) {
|
||||
while (true) {
|
||||
@@ -42,7 +45,7 @@ static __device__ __forceinline__ void allreduceDeep(
|
||||
{ Pack tmp1[UnrollPacks];
|
||||
#pragma unroll
|
||||
for (int u=0; u < UnrollPacks; u++) {
|
||||
tmp1[u] = add4G(inpRank0, r*stride4G)[u*WARP_SIZE];
|
||||
tmp1[u] = inpPacks.peerPtr(world, r)[u*WARP_SIZE];
|
||||
}
|
||||
#pragma unroll
|
||||
for (int u=0; u < UnrollPacks; u++) {
|
||||
@@ -67,7 +70,7 @@ static __device__ __forceinline__ void allreduceDeep(
|
||||
if (partial && ur!=0 && dr+ur == nRanks) break;
|
||||
#pragma unroll UnrollPacks
|
||||
for (int u=0; u < UnrollPacks; u++) {
|
||||
tmp1[ur][u] = add4G(inpRank0, r*stride4G)[u*WARP_SIZE];
|
||||
tmp1[ur][u] = inpPacks.peerPtr(world, r)[u*WARP_SIZE];
|
||||
}
|
||||
if (++r == nRanks) r = 0;
|
||||
}
|
||||
@@ -98,22 +101,22 @@ static __device__ __forceinline__ void allreduceDeep(
|
||||
if (partial && dr == nRanks) break;
|
||||
#pragma unroll UnrollPacks
|
||||
for (int u=0; u < UnrollPacks; u++) {
|
||||
add4G(outRank0, r*stride4G)[u*WARP_SIZE] = acc0[u];
|
||||
outPacks.peerPtr(world, r)[u*WARP_SIZE] = acc0[u];
|
||||
}
|
||||
if (++r == nRanks) r = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inpRank0 += intptr_t(wn)*UnrollPacks*WARP_SIZE;
|
||||
outRank0 += intptr_t(wn)*UnrollPacks*WARP_SIZE;
|
||||
inpPacks += intptr_t(wn)*UnrollPacks*WARP_SIZE;
|
||||
outPacks += intptr_t(wn)*UnrollPacks*WARP_SIZE;
|
||||
nIters -= wn;
|
||||
if (nIters <= 0) break;
|
||||
|
||||
// Load data for next iteration.
|
||||
#pragma unroll
|
||||
for (int u=0; u < UnrollPacks; u++) {
|
||||
acc0[u] = add4G(inpRank0, rank*stride4G)[u*WARP_SIZE];
|
||||
acc0[u] = inpPacks.peerPtr(world, rank)[u*WARP_SIZE];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -121,21 +124,23 @@ static __device__ __forceinline__ void allreduceDeep(
|
||||
|
||||
template<int UnrollPeers, typename Red, typename T>
|
||||
static __device__ __forceinline__ void allreduceEnds(
|
||||
ncclSymPrims& prim, int tn, int t, Red red,
|
||||
T* inputRank0, T* outputRank0, size_t nElts, uint32_t nPreElts, size_t nSufElts
|
||||
ncclSymkArgsHandler const& handler, int tn, int t, Red red,
|
||||
ncclSymPtr<T> input, ncclSymPtr<T> output,
|
||||
size_t nElts, uint32_t nPreElts, size_t nSufElts
|
||||
) {
|
||||
using Acc = typename Red::EltType;
|
||||
|
||||
int const& rank = prim.rank;
|
||||
int const& nRanks = prim.nRanks;
|
||||
uint32_t const& stride4G = prim.stride4G;
|
||||
BytePack<sizeof(T)>* inpRank0 = (BytePack<sizeof(T)>*)inputRank0;
|
||||
BytePack<sizeof(T)>* outRank0 = (BytePack<sizeof(T)>*)outputRank0;
|
||||
ncclTeam world = ncclTeamWorld(handler.comm);
|
||||
int const& rank = handler.comm.rank;
|
||||
int const& nRanks = handler.comm.nRanks;
|
||||
|
||||
ncclSymPtr<BytePack<sizeof(T)>> inpPacks = (ncclSymPtr<BytePack<sizeof(T)>>)input;
|
||||
ncclSymPtr<BytePack<sizeof(T)>> outPacks = (ncclSymPtr<BytePack<sizeof(T)>>)output;
|
||||
|
||||
#pragma unroll 1
|
||||
for (size_t i = t; i < nPreElts+nSufElts; i += tn) {
|
||||
size_t elt = i < nPreElts ? i : nElts-nSufElts-nPreElts+i;
|
||||
BytePack<sizeof(T)> acc0 = *add4G(inpRank0+elt, rank*stride4G);
|
||||
BytePack<sizeof(T)> acc0 = inpPacks.peerPtr(world, rank)[elt];
|
||||
BytePack<sizeof(Acc)> acc1;
|
||||
BytePack<sizeof(T)> tmp[UnrollPeers];
|
||||
int dr = 1;
|
||||
@@ -154,7 +159,7 @@ static __device__ __forceinline__ void allreduceEnds(
|
||||
#pragma unroll
|
||||
for (int u=0; u < UnrollPeers-partial; u++) {
|
||||
if (partial && u!=0 && dr+u == nRanks) break;
|
||||
tmp[u] = *add4G(inpRank0+elt, r*stride4G);
|
||||
tmp[u] = inpPacks.peerPtr(world, r)[elt];
|
||||
r += 1;
|
||||
if (r == nRanks) r = 0;
|
||||
}
|
||||
@@ -182,7 +187,7 @@ static __device__ __forceinline__ void allreduceEnds(
|
||||
#pragma unroll
|
||||
for (int u=0; u < UnrollPeers-partial; u++) {
|
||||
if (partial && dr+u == nRanks) break;
|
||||
*add4G(outRank0+elt, r*stride4G) = acc0;
|
||||
outPacks.peerPtr(world, r)[elt] = acc0;
|
||||
r += 1;
|
||||
if (r == nRanks) r = 0;
|
||||
}
|
||||
@@ -193,35 +198,33 @@ static __device__ __forceinline__ void allreduceEnds(
|
||||
|
||||
template<typename Red, typename T>
|
||||
static __device__ void allreduce(
|
||||
ncclSymPrims& prim, int tn, int t, bool waitNeeded,
|
||||
Red red, T* input, T* output, size_t nElts
|
||||
ncclSymkArgsHandler const& handler, int tn, int t, int nBlocks,
|
||||
bool waitNeeded, ncclLsaBarrierSession<ncclCoopCta>& bar,
|
||||
Red red, ncclSymPtr<T> input, ncclSymPtr<T> output, size_t nElts
|
||||
) {
|
||||
int nRanks = prim.nRanks;
|
||||
int nBlocks = prim.nBlocks;
|
||||
// Mpve to rank=0
|
||||
input = prim.peerPtr(0, input);
|
||||
output = prim.peerPtr(0, output);
|
||||
|
||||
uintptr_t inputUptr = reinterpret_cast<uintptr_t>(input);
|
||||
uintptr_t outputUptr = reinterpret_cast<uintptr_t>(output);
|
||||
int const& nRanks = handler.comm.nRanks;
|
||||
int const& nRanks_rcp32 = handler.nRanks_rcp32;
|
||||
size_t nBytes = nElts*sizeof(T);
|
||||
uint32_t nBlocks_rcp32 = nccl::utility::idivRcp32_upto64(nBlocks);
|
||||
uint32_t nRanks_nBlocks_rcp32 = nccl::utility::imulRcp32(nRanks, nRanks_rcp32, nBlocks, nBlocks_rcp32);
|
||||
|
||||
uint32_t nPreBytes = (16u - inputUptr)%16u;
|
||||
uint32_t nPreBytes = (16u - input.offset)%16u;
|
||||
nPreBytes = min((size_t)nPreBytes, nBytes);
|
||||
uintptr_t cursor = nPreBytes;
|
||||
|
||||
constexpr int MinWarpPerBlock = 4;
|
||||
|
||||
if ((inputUptr-outputUptr)%16 == 0) {
|
||||
if ((input.offset - output.offset)%16 == 0) {
|
||||
constexpr int BytePerPack = 16, UnrollPacks = 4, UnrollPeers = 2;
|
||||
constexpr int BytePerChunk = MinWarpPerBlock*UnrollPacks*WARP_SIZE*BytePerPack;
|
||||
uint32_t chunks = (nBytes-cursor)/BytePerChunk;
|
||||
chunks -= imodFast32(chunks, nRanks*nBlocks, prim.nRanks_nBlocks_rcp32);
|
||||
chunks -= imodFast32(chunks, nRanks*nBlocks, nRanks_nBlocks_rcp32);
|
||||
if (chunks != 0) {
|
||||
uintptr_t cursorAfter = cursor + uintptr_t(chunks)*BytePerChunk;
|
||||
allreduceDeep<BytePerPack, UnrollPacks, UnrollPeers, T>(
|
||||
prim, tn, t, waitNeeded, red,
|
||||
(char*)input + cursor, (char*)output + cursor,
|
||||
handler, tn, t, waitNeeded, bar, red,
|
||||
(ncclSymPtr<char>)input + cursor,
|
||||
(ncclSymPtr<char>)output + cursor,
|
||||
chunks*MinWarpPerBlock
|
||||
);
|
||||
cursor = cursorAfter;
|
||||
@@ -229,16 +232,17 @@ static __device__ void allreduce(
|
||||
}
|
||||
}
|
||||
|
||||
if (sizeof(T) == 4 || (sizeof(T) < 4 && (inputUptr-outputUptr)%4 == 0)) {
|
||||
if (sizeof(T) == 4 || (sizeof(T) < 4 && (input.offset - output.offset)%4 == 0)) {
|
||||
constexpr int BytePerPack = 4, UnrollPacks = 4, UnrollPeers = 4;
|
||||
constexpr int BytePerChunk = MinWarpPerBlock*UnrollPacks*WARP_SIZE*BytePerPack;
|
||||
uint32_t chunks = (nBytes-cursor)/BytePerChunk;
|
||||
chunks -= imodFast32(chunks, nRanks*nBlocks, prim.nRanks_nBlocks_rcp32);
|
||||
chunks -= imodFast32(chunks, nRanks*nBlocks, nRanks_nBlocks_rcp32);
|
||||
if (chunks != 0) {
|
||||
uintptr_t cursorAfter = cursor + uintptr_t(chunks)*BytePerChunk;
|
||||
allreduceDeep<(sizeof(T) <= BytePerPack ? BytePerPack : 0), UnrollPacks, UnrollPeers, T>(
|
||||
prim, tn, t, waitNeeded, red,
|
||||
(char*)input + cursor, (char*)output + cursor,
|
||||
handler, tn, t, waitNeeded, bar, red,
|
||||
(ncclSymPtr<char>)input + cursor,
|
||||
(ncclSymPtr<char>)output + cursor,
|
||||
chunks*MinWarpPerBlock
|
||||
);
|
||||
cursor = cursorAfter;
|
||||
@@ -246,46 +250,51 @@ static __device__ void allreduce(
|
||||
}
|
||||
}
|
||||
|
||||
if (waitNeeded) prim.barrierWait(ncclCoopCta(), /*acquire=*/false);
|
||||
if (waitNeeded) bar.wait(ncclCoopCta(), cuda::memory_order_relaxed);
|
||||
|
||||
constexpr int UnrollPeers = 8;
|
||||
size_t nSufElts = (nBytes-cursor)/sizeof(T);
|
||||
allreduceEnds<UnrollPeers>(prim, tn, t, red, input, output, nElts, nPreBytes/sizeof(T), nSufElts);
|
||||
allreduceEnds<UnrollPeers>(handler, tn, t, red, input, output, nElts, nPreBytes/sizeof(T), nSufElts);
|
||||
}
|
||||
|
||||
|
||||
template<template<typename> typename Red, typename T>
|
||||
__device__ __forceinline__ void ncclSymRun_AllReduce_RSxLD_AGxST(ncclSymDevArgs const* args) {
|
||||
ncclSymPrims prim(args->comm, ncclSymPrims_UseBarrier);
|
||||
int /*const&*/ rank = prim.rank;
|
||||
int /*const&*/ nRanks = prim.nRanks;
|
||||
Red<typename ncclSymAccumType<Red, T, /*nvls=*/false>::Type> red(args->redOpArg);
|
||||
__device__ __forceinline__ void ncclSymkRun_AllReduce_RSxLD_AGxST(ncclSymkDevWorkArgs const* args) {
|
||||
ncclSymkArgsHandler handler{args};
|
||||
ncclLsaBarrierSession<ncclCoopCta> bar{
|
||||
ncclCoopCta(), handler.comm, ncclTeamTagLsa(), blockIdx.x
|
||||
};
|
||||
|
||||
// Threads numbered globally such that we round robin warps by rank then block.
|
||||
int gt = flattenIx(threadIdx.x%WARP_SIZE, WARP_SIZE,
|
||||
rank, nRanks,
|
||||
prim.block, prim.nBlocks,
|
||||
threadIdx.x/WARP_SIZE, blockDim.x/WARP_SIZE);
|
||||
int gtn = nRanks*prim.nBlocks*blockDim.x;
|
||||
Red<typename ncclSymkAccumType<Red, T, /*nvls=*/false>::Type> red(handler.devWork->redOpArg);
|
||||
|
||||
prim.barrierArrive(ncclCoopCta(), /*release=*/false);
|
||||
//prim.barrierWait(ncclCoopCta(), /*acquire=*/false);
|
||||
int const& rank = handler.comm.rank;
|
||||
int const& nRanks = handler.comm.nRanks;
|
||||
|
||||
allreduce(prim, gtn, gt, /*waitNeeded=*/true, red, (T*)args->input, (T*)args->output, args->nElts);
|
||||
bar.arrive(ncclCoopCta(), cuda::memory_order_relaxed);
|
||||
|
||||
prim.barrierArrive(ncclCoopCta(), /*release=*/true);
|
||||
prim.barrierWait(ncclCoopCta(), /*acquire=*/false);
|
||||
bool waitNeeded = true;
|
||||
handler.forEachWork<T>(
|
||||
[&]__device__(int block, int nBlocks, size_t nElts, size_t nAllElts,
|
||||
ncclSymPtr<T> input, ncclSymPtr<T> output) {
|
||||
// Threads numbered globally such that we round robin warps by rank then block.
|
||||
int gt = flattenIx(threadIdx.x%WARP_SIZE, WARP_SIZE,
|
||||
rank, nRanks,
|
||||
block, nBlocks,
|
||||
threadIdx.x/WARP_SIZE, blockDim.x/WARP_SIZE);
|
||||
int gtn = nRanks*nBlocks*blockDim.x;
|
||||
|
||||
allreduce(handler, gtn, gt, nBlocks, waitNeeded, bar, red, input, output, nElts);
|
||||
|
||||
waitNeeded = false;
|
||||
}
|
||||
);
|
||||
|
||||
bar.sync(ncclCoopCta(), cuda::memory_order_release);
|
||||
}
|
||||
|
||||
|
||||
template<typename Red, typename T>
|
||||
static __device__ void allreduceMultimem(
|
||||
ncclSymPrims& prim, int tn, int t, Red red, T* input, T* output, size_t nElts
|
||||
int tn, int t, Red red, T* input, T* output, size_t nElts
|
||||
) {
|
||||
// Mpve to multimem
|
||||
input = prim.multimemPtr(input);
|
||||
output = prim.multimemPtr(output);
|
||||
|
||||
uintptr_t inputUptr = reinterpret_cast<uintptr_t>(input);
|
||||
uintptr_t outputUptr = reinterpret_cast<uintptr_t>(output);
|
||||
size_t nBytes = nElts*sizeof(T);
|
||||
@@ -330,106 +339,132 @@ static __device__ void allreduceMultimem(
|
||||
uintptr_t cursor = i < nPreBytes ? i : nBytes-nSufBytes+(i-nPreBytes);
|
||||
BytePack<sizeof(T)> val = applyLoadMultimem<Red, sizeof(T)>(red, inputUptr + cursor);
|
||||
multimem_st_global(outputUptr + cursor, val);
|
||||
cursor += tn*sizeof(T);
|
||||
}
|
||||
}
|
||||
|
||||
template<template<typename> typename Red, typename T>
|
||||
__device__ __forceinline__ void ncclSymRun_AllReduce_RSxLDMC_AGxSTMC(ncclSymDevArgs const* args) {
|
||||
ncclSymPrims prim(args->comm, ncclSymPrims_UseBarrier|ncclSymPrims_UseMultimem);
|
||||
Red<typename ncclSymAccumType<Red, T, /*nvls=*/true>::Type> red(args->redOpArg);
|
||||
__device__ __forceinline__ void ncclSymkRun_AllReduce_RSxLDMC_AGxSTMC(ncclSymkDevWorkArgs const* args) {
|
||||
ncclSymkArgsHandler handler{args};
|
||||
ncclLsaBarrierSession<ncclCoopCta> bar{
|
||||
ncclCoopCta(), handler.comm, ncclTeamTagLsa(), blockIdx.x, /*multimem=*/true
|
||||
};
|
||||
|
||||
// Threads numbered globally such that we round robin warps by rank then block.
|
||||
int gt = flattenIx(threadIdx.x%WARP_SIZE, WARP_SIZE,
|
||||
prim.rank, prim.nRanks,
|
||||
prim.block, prim.nBlocks,
|
||||
threadIdx.x/WARP_SIZE, blockDim.x/WARP_SIZE);
|
||||
int gtn = prim.nRanks*prim.nBlocks*blockDim.x;
|
||||
Red<typename ncclSymkAccumType<Red, T, /*nvls=*/true>::Type> red(handler.devWork->redOpArg);
|
||||
|
||||
prim.barrierArrive(ncclCoopCta(), /*release=*/false);
|
||||
prim.barrierWait(ncclCoopCta(), /*acquire=*/false);
|
||||
int const& rank = handler.comm.rank;
|
||||
int const& nRanks = handler.comm.nRanks;
|
||||
auto const& multimem = handler.comm.lsaMultimem;
|
||||
|
||||
allreduceMultimem(prim, gtn, gt, red, (T*)args->input, (T*)args->output, args->nElts);
|
||||
bar.sync(ncclCoopCta(), cuda::memory_order_relaxed);
|
||||
|
||||
prim.barrierArrive(ncclCoopCta(), /*release=*/true);
|
||||
prim.barrierWait(ncclCoopCta(), /*acquire=*/false);
|
||||
handler.forEachWork<T>(
|
||||
[&]__device__(int block, int nBlocks, size_t nElts, size_t nAllElts,
|
||||
ncclSymPtr<T> input, ncclSymPtr<T> output) {
|
||||
// Threads numbered globally such that we round robin warps by rank then block.
|
||||
int gt = flattenIx(threadIdx.x%WARP_SIZE, WARP_SIZE,
|
||||
rank, nRanks,
|
||||
block, nBlocks,
|
||||
threadIdx.x/WARP_SIZE, blockDim.x/WARP_SIZE);
|
||||
int gtn = nRanks*nBlocks*blockDim.x;
|
||||
|
||||
allreduceMultimem(gtn, gt, red, input.multimemPtr(multimem), output.multimemPtr(multimem), nElts);
|
||||
}
|
||||
);
|
||||
|
||||
bar.sync(ncclCoopCta(), cuda::memory_order_release);
|
||||
}
|
||||
|
||||
template<template<typename> typename Red, typename T>
|
||||
__device__ __forceinline__ void ncclSymRun_AllReduce_AGxLL_R_impl(ncclSymDevArgs const* args, bool multimem) {
|
||||
ncclSymPrims prim(args->comm, ncclSymPrims_UseLL | multimem*ncclSymPrims_UseMultimem);
|
||||
int /*const&*/ rank = prim.rank;
|
||||
using Acc = typename ncclSymAccumType<Red, T, /*nvls=*/false>::Type;
|
||||
Red<Acc> red(args->redOpArg);
|
||||
__device__ __forceinline__ void ncclSymkRun_AllReduce_AGxLL_R_impl(ncclSymkDevWorkArgs const* args, bool multimem) {
|
||||
ncclSymkArgsHandler handler{args};
|
||||
ncclLLA2ASession<ncclCoopCta> lla2a(
|
||||
ncclCoopCta(), handler.comm, ncclTeamLsa(handler.comm), handler.lsaLLA2A,
|
||||
blockIdx.x, ncclSymkMaxThreads, multimem, handler.comm.lsaMultimem
|
||||
);
|
||||
|
||||
int const& rank = handler.comm.rank;
|
||||
int const& nRanks = handler.comm.nRanks;
|
||||
using Acc = typename ncclSymkAccumType<Red, T, /*nvls=*/false>::Type;
|
||||
Red<Acc> red(handler.devWork->redOpArg);
|
||||
|
||||
using Pack = BytePack<8>;
|
||||
using AccPack = BytePack<8*sizeof(Acc)/sizeof(T)>;
|
||||
constexpr int EltPerPack = 8/sizeof(T);
|
||||
int nElts = args->nElts;
|
||||
int nPacks = divUp(nElts, EltPerPack);
|
||||
|
||||
bool packAligned = 8 <= alignof(T) || (
|
||||
args->nElts*sizeof(T) |
|
||||
(uint32_t)reinterpret_cast<uintptr_t>(args->input) |
|
||||
(uint32_t)reinterpret_cast<uintptr_t>(args->output)
|
||||
)%8 == 0;
|
||||
handler.singleWork<T>(
|
||||
[&]__device__(int nElts, int nAllElts,
|
||||
ncclSymPtr<T> inputPtr, ncclSymPtr<T> outputPtr) {
|
||||
int nPacks = divUp(nElts, EltPerPack);
|
||||
|
||||
uint32_t nPackPerBlock, nPackModBlock;
|
||||
idivmodFast32(&nPackPerBlock, &nPackModBlock, nPacks, prim.nBlocks, prim.nBlocks_rcp32);
|
||||
int begin = prim.block*nPackPerBlock + minval<int>(prim.block, nPackModBlock);
|
||||
int end = begin + nPackPerBlock + (prim.block < nPackModBlock ? 1 : 0);
|
||||
T* input = (T*)inputPtr.localPtr();
|
||||
T* output = (T*)outputPtr.localPtr();
|
||||
|
||||
nPacks = end - begin;
|
||||
nElts -= begin*EltPerPack;
|
||||
nElts = min(nElts, nPacks*EltPerPack);
|
||||
T* input = (T*)args->input + begin*EltPerPack;
|
||||
T* output = (T*)args->output + begin*EltPerPack;
|
||||
bool packAligned = 8 <= alignof(T) || (nElts*sizeof(T) | (uintptr_t)input | (uintptr_t)output)%8 == 0;
|
||||
|
||||
ncclCoopCta cta;
|
||||
int t = threadIdx.x;
|
||||
int tn = ncclSymMaxThreads;
|
||||
ncclCoopCta cta;
|
||||
int t = threadIdx.x;
|
||||
int tn = ncclSymkMaxThreads;
|
||||
|
||||
if (__builtin_expect(packAligned, true)) {
|
||||
#pragma unroll 1
|
||||
while (0 < nPacks) {
|
||||
if (t < nPacks) {
|
||||
int nIterPacks = min(nPacks, tn);
|
||||
Pack inp = loadPack<Pack>((Pack*)input, t, nPacks);
|
||||
prim.bcastLL(/*slot=*/nIterPacks*rank + t, inp);
|
||||
Pack out = prim.template recvReduceLL<Pack, T>(t, nIterPacks, red);
|
||||
storePack((Pack*)output, t, nPacks, out);
|
||||
if (__builtin_expect(packAligned, true)) {
|
||||
#pragma unroll 1
|
||||
while (0 < nPacks) {
|
||||
if (t < nPacks) {
|
||||
int nIterPacks = min(nPacks, tn);
|
||||
Pack inp = loadPack<Pack>((Pack*)input, t, nPacks);
|
||||
lla2a.bcast(/*slot=*/nIterPacks*rank + t, inp);
|
||||
AccPack out = lla2a.template recvReduce</*Unroll=*/8, Pack>(
|
||||
/*slotStart=*/t, /*slotCount=*/nRanks, /*slotStride=*/nIterPacks,
|
||||
/*eltToAcc=*/[&] __device__ (Pack x)->AccPack {
|
||||
return applyCast<T, Acc>(x);
|
||||
},
|
||||
/*reduce=*/[&] __device__ (AccPack a, AccPack b)->AccPack {
|
||||
return applyReduce(red, a, b);
|
||||
}
|
||||
);
|
||||
storePack((Pack*)output, t, nPacks, applyCast<Acc, T>(out));
|
||||
}
|
||||
lla2a.endEpoch(cta);
|
||||
|
||||
input += tn*EltPerPack;
|
||||
output += tn*EltPerPack;
|
||||
nPacks -= tn;
|
||||
}
|
||||
} else {
|
||||
#pragma unroll 1
|
||||
while (0 < nElts) {
|
||||
if (t*EltPerPack < nElts) {
|
||||
int nIterPacks = min(nPacks, tn);
|
||||
Pack inp = loadPack<Pack>(input, t*EltPerPack, nElts);
|
||||
lla2a.bcast(/*slot=*/nIterPacks*rank + t, inp);
|
||||
AccPack out = lla2a.template recvReduce</*Unroll=*/8, Pack>(
|
||||
/*slotStart=*/t, /*slotCount=*/nRanks, /*slotStride=*/nIterPacks,
|
||||
/*eltToAcc=*/[&] __device__ (Pack x)->AccPack {
|
||||
return applyCast<T, Acc>(x);
|
||||
},
|
||||
/*reduce=*/[&] __device__ (AccPack a, AccPack b)->AccPack {
|
||||
return applyReduce(red, a, b);
|
||||
}
|
||||
);
|
||||
storePack(output, t*EltPerPack, nElts, applyCast<Acc, T>(out));
|
||||
}
|
||||
lla2a.endEpoch(cta);
|
||||
|
||||
input += tn*EltPerPack;
|
||||
output += tn*EltPerPack;
|
||||
nElts -= tn*EltPerPack;
|
||||
nPacks -= tn;
|
||||
}
|
||||
}
|
||||
}
|
||||
prim.endLL(cta);
|
||||
|
||||
input += tn*EltPerPack;
|
||||
output += tn*EltPerPack;
|
||||
nPacks -= tn;
|
||||
}
|
||||
} else {
|
||||
#pragma unroll 1
|
||||
while (0 < nElts) {
|
||||
if (t*EltPerPack < nElts) {
|
||||
int nIterPacks = min(nPacks, tn);
|
||||
Pack inp = loadPack<Pack>(input, t*EltPerPack, nElts);
|
||||
prim.bcastLL(/*slot=*/nIterPacks*rank + t, inp);
|
||||
Pack out = prim.template recvReduceLL<Pack, T>(t, nIterPacks, red);
|
||||
storePack(output, t*EltPerPack, nElts, out);
|
||||
}
|
||||
prim.endLL(cta);
|
||||
|
||||
input += tn*EltPerPack;
|
||||
output += tn*EltPerPack;
|
||||
nElts -= tn*EltPerPack;
|
||||
nPacks -= tn;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
template<template<typename> typename Red, typename T>
|
||||
__device__ __forceinline__ void ncclSymRun_AllReduce_AGxLL_R(ncclSymDevArgs const* args) {
|
||||
ncclSymRun_AllReduce_AGxLL_R_impl<Red, T>(args, /*multimem=*/false);
|
||||
__device__ __forceinline__ void ncclSymkRun_AllReduce_AGxLL_R(ncclSymkDevWorkArgs const* args) {
|
||||
ncclSymkRun_AllReduce_AGxLL_R_impl<Red, T>(args, /*multimem=*/false);
|
||||
}
|
||||
|
||||
template<template<typename> typename Red, typename T>
|
||||
__device__ __forceinline__ void ncclSymRun_AllReduce_AGxLLMC_R(ncclSymDevArgs const* args) {
|
||||
ncclSymRun_AllReduce_AGxLL_R_impl<Red, T>(args, /*multimem=*/true);
|
||||
__device__ __forceinline__ void ncclSymkRun_AllReduce_AGxLLMC_R(ncclSymkDevWorkArgs const* args) {
|
||||
ncclSymkRun_AllReduce_AGxLL_R_impl<Red, T>(args, /*multimem=*/true);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
|
||||
################################################################################
|
||||
# The first command line argument is the path to the directory to generate and
|
||||
@@ -13,8 +14,11 @@ gensrc = sys.argv[1]
|
||||
|
||||
if os.path.exists(gensrc):
|
||||
for name in os.listdir(gensrc):
|
||||
os.remove(os.path.join(gensrc, name))
|
||||
#os.truncate(os.path.join(gensrc, name), 0)
|
||||
path = os.path.join(gensrc, name)
|
||||
if os.path.isfile(path):
|
||||
os.remove(path)
|
||||
elif os.path.isdir(path):
|
||||
shutil.rmtree(path)
|
||||
else:
|
||||
os.mkdir(gensrc)
|
||||
|
||||
@@ -97,7 +101,7 @@ def enumerate_kernels():
|
||||
yield Rec(coll="ReduceScatter", algo=algo, red=red, ty=ty)
|
||||
|
||||
def required_cuda(k):
|
||||
cudart, arch, specific_sms = 0, 0, None
|
||||
cudart, arch, specific_sms = 0, 600, None
|
||||
is_nvls = k.algo in nvls_algos_by_coll.get(k.coll, [])
|
||||
if is_nvls:
|
||||
cudart = max(cudart, 12010)
|
||||
@@ -136,13 +140,13 @@ def kernel_gencode(k):
|
||||
|
||||
def kernel_cname(k):
|
||||
if k.coll in reductions:
|
||||
return paste("_", "ncclSymDevKernel", k.coll, k.algo, k.red, k.ty)
|
||||
return paste("_", "ncclSymkDevKernel", k.coll, k.algo, k.red, k.ty)
|
||||
else:
|
||||
return paste("_", "ncclSymDevKernel", k.coll, k.algo)
|
||||
return paste("_", "ncclSymkDevKernel", k.coll, k.algo)
|
||||
|
||||
def kernel_conds(k):
|
||||
cudart, arch, specific_sms = required_cuda(k)
|
||||
if cudart == 0: return (None, None)
|
||||
if cudart == 0 and arch == 0: return (None, None)
|
||||
|
||||
cudart_cond = "CUDART_VERSION >= %d"%cudart
|
||||
if not specific_sms:
|
||||
@@ -153,13 +157,13 @@ def kernel_conds(k):
|
||||
|
||||
def instantiate(k):
|
||||
form_red_ty = (
|
||||
"__global__ void {cname}(ncclSymDevArgs NCCL_GRID_CONSTANT const *args) {{\n"
|
||||
" ncclSymRun_{id}<{red}, {ty}>(args);\n"
|
||||
"__global__ void {cname}(ncclSymkDevWorkArgs4K NCCL_GRID_CONSTANT const *args4K) {{\n"
|
||||
" ncclSymkRun_{id}<{red}, {ty}>(args4K->args);\n"
|
||||
"}}"
|
||||
)
|
||||
form = (
|
||||
"__global__ void {cname}(ncclSymDevArgs NCCL_GRID_CONSTANT const *args) {{\n"
|
||||
" ncclSymRun_{id}(args);\n"
|
||||
"__global__ void {cname}(ncclSymkDevWorkArgs4K NCCL_GRID_CONSTANT const *args4K) {{\n"
|
||||
" ncclSymkRun_{id}(args4K->args);\n"
|
||||
"}}"
|
||||
)
|
||||
|
||||
@@ -172,7 +176,7 @@ def instantiate(k):
|
||||
return inst
|
||||
|
||||
def prototype(k):
|
||||
return "__global__ void {cname}(ncclSymDevArgs const *args);".format(cname=kernel_cname(k))
|
||||
return "__global__ void {cname}(ncclSymkDevWorkArgs4K const *args4K);".format(cname=kernel_cname(k))
|
||||
|
||||
################################################################################
|
||||
|
||||
@@ -194,20 +198,22 @@ for coll in set(k.coll for k in enumerate_kernels()):
|
||||
if (fname, coll) not in kernels_by_file:
|
||||
kernels_by_file[fname, coll] = []
|
||||
|
||||
files_to_print = ""
|
||||
# Generate each kernel instantiation file
|
||||
for (fname, coll), ks in kernels_by_file.items():
|
||||
files_to_print += fname + ";"
|
||||
with open(os.path.join(gensrc, fname), "w") as f:
|
||||
print("-- Generating %s" % os.path.join(gensrc, fname))
|
||||
emitln(f, '#include "symmetric.h"')
|
||||
emitln(f, '#include "sym_kernels.h"')
|
||||
emitln(f, '#include "symmetric/kernel.h"')
|
||||
emitln(f, '#include "symmetric/{coll}.h"'.format(coll=coll_to_lower[coll]))
|
||||
for k in ks:
|
||||
emitln(f, instantiate(k))
|
||||
|
||||
# Generate <gensrc>/symmetric_host.cc
|
||||
with open(os.path.join(gensrc, "symmetric_kernels.cc"), "w") as f:
|
||||
# Generate <gensrc>/sym_kernels_host.cc
|
||||
with open(os.path.join(gensrc, "sym_kernels_host.cc"), "w") as f:
|
||||
print("-- Generating %s" % os.path.join(gensrc, "symmetric_kernels.cc"))
|
||||
emitln(f, '#include "symmetric.h"')
|
||||
emitln(f, '#include "sym_kernels.h"')
|
||||
emitln(f, '#include "device.h"')
|
||||
emitln(f, '')
|
||||
|
||||
@@ -215,19 +221,19 @@ with open(os.path.join(gensrc, "symmetric_kernels.cc"), "w") as f:
|
||||
emitln(f, prototype(k))
|
||||
emitln(f, '')
|
||||
|
||||
emitln(f, 'extern int const ncclSymKernelCount = %d;' % len(list(enumerate_kernels())))
|
||||
emitln(f, 'extern void* const ncclSymKernelList[] = {')
|
||||
emitln(f, 'extern int const ncclSymkKernelCount = %d;' % len(list(enumerate_kernels())))
|
||||
emitln(f, 'extern void* const ncclSymkKernelList[] = {')
|
||||
for k in enumerate_kernels():
|
||||
emitln(f, '(void*){cname},'.format(cname=kernel_cname(k)))
|
||||
emitln(f, 'nullptr};')
|
||||
emitln(f, '')
|
||||
|
||||
emitln(f, 'void* ncclSymGetKernelPtr(ncclSymKernelId id, int red, ncclDataType_t ty) {')
|
||||
emitln(f, 'void* ncclSymkGetKernelPtr(ncclSymkKernelId id, int red, ncclDataType_t ty) {')
|
||||
indents += 1
|
||||
emitln(f, 'switch (id) {')
|
||||
emitln(f, 'default: return nullptr;')
|
||||
for (coll, algo), coll_algo_ks in partition(enumerate_kernels(), lambda k: (k.coll, k.algo)).items():
|
||||
emitln(f, 'case ncclSymKernelId_'+coll+'_'+algo+':')
|
||||
emitln(f, 'case ncclSymkKernelId_'+coll+'_'+algo+':')
|
||||
indents += 1
|
||||
if len(coll_algo_ks) == 1:
|
||||
emitln(f, 'return (void*)&'+kernel_cname(coll_algo_ks[0])+';')
|
||||
|
||||
@@ -4,27 +4,27 @@
|
||||
#ifndef NCCL_DEVICE_SYMMETRIC_KERNEL_H_
|
||||
#define NCCL_DEVICE_SYMMETRIC_KERNEL_H_
|
||||
|
||||
#include "symmetric.h"
|
||||
#include "sym_kernels.h"
|
||||
|
||||
template<template<typename> typename Red, typename T>
|
||||
__device__ __forceinline__ void ncclSymRun_AllReduce_AGxLL_R(struct ncclSymDevArgs const* args);
|
||||
__device__ __forceinline__ void ncclSymkRun_AllReduce_AGxLL_R(struct ncclSymkDevWorkArgs const* args);
|
||||
template<template<typename> typename Red, typename T>
|
||||
__device__ __forceinline__ void ncclSymRun_AllReduce_AGxLLMC_R(struct ncclSymDevArgs const* args);
|
||||
__device__ __forceinline__ void ncclSymkRun_AllReduce_AGxLLMC_R(struct ncclSymkDevWorkArgs const* args);
|
||||
|
||||
template<template<typename> typename Red, typename T>
|
||||
__device__ __forceinline__ void ncclSymRun_AllReduce_RSxLD_AGxST(struct ncclSymDevArgs const* args);
|
||||
__device__ __forceinline__ void ncclSymkRun_AllReduce_RSxLD_AGxST(struct ncclSymkDevWorkArgs const* args);
|
||||
template<template<typename> typename Red, typename T>
|
||||
__device__ __forceinline__ void ncclSymRun_AllReduce_RSxLDMC_AGxSTMC(struct ncclSymDevArgs const* args);
|
||||
__device__ __forceinline__ void ncclSymkRun_AllReduce_RSxLDMC_AGxSTMC(struct ncclSymkDevWorkArgs const* args);
|
||||
|
||||
__device__ __forceinline__ void ncclSymRun_AllGather_LL(struct ncclSymDevArgs const* args);
|
||||
__device__ __forceinline__ void ncclSymRun_AllGather_LLMC(struct ncclSymDevArgs const* args);
|
||||
__device__ __forceinline__ void ncclSymRun_AllGather_ST(struct ncclSymDevArgs const* args);
|
||||
__device__ __forceinline__ void ncclSymRun_AllGather_STMC(struct ncclSymDevArgs const* args);
|
||||
__device__ __forceinline__ void ncclSymkRun_AllGather_LL(struct ncclSymkDevWorkArgs const* args);
|
||||
__device__ __forceinline__ void ncclSymkRun_AllGather_LLMC(struct ncclSymkDevWorkArgs const* args);
|
||||
__device__ __forceinline__ void ncclSymkRun_AllGather_ST(struct ncclSymkDevWorkArgs const* args);
|
||||
__device__ __forceinline__ void ncclSymkRun_AllGather_STMC(struct ncclSymkDevWorkArgs const* args);
|
||||
|
||||
template<template<typename> typename Red, typename T>
|
||||
__device__ __forceinline__ void ncclSymRun_ReduceScatter_LL(struct ncclSymDevArgs const* args);
|
||||
__device__ __forceinline__ void ncclSymkRun_ReduceScatter_LL(struct ncclSymkDevWorkArgs const* args);
|
||||
template<template<typename> typename Red, typename T>
|
||||
__device__ __forceinline__ void ncclSymRun_ReduceScatter_LD(struct ncclSymDevArgs const* args);
|
||||
__device__ __forceinline__ void ncclSymkRun_ReduceScatter_LD(struct ncclSymkDevWorkArgs const* args);
|
||||
template<template<typename> typename Red, typename T>
|
||||
__device__ __forceinline__ void ncclSymRun_ReduceScatter_LDMC(struct ncclSymDevArgs const* args);
|
||||
__device__ __forceinline__ void ncclSymkRun_ReduceScatter_LDMC(struct ncclSymkDevWorkArgs const* args);
|
||||
#endif
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#ifndef NCCL_DEVICE_SYMMETRIC_PRIMITIVES_H_
|
||||
#define NCCL_DEVICE_SYMMETRIC_PRIMITIVES_H_
|
||||
|
||||
#include "symmetric.h"
|
||||
#include "sym_kernels.h"
|
||||
#include "bitops.h"
|
||||
#include "collectives.h"
|
||||
#include "op128.h"
|
||||
@@ -28,453 +28,124 @@ static __device__ Int0 flattenIx(Int0 pos, Int1 size, Ints ...more) {
|
||||
return pos + size*flattenIx(more...);
|
||||
}
|
||||
|
||||
// Precomputed integer reciprocoals for denominator values 1..64 inclusive.
|
||||
// Pass these to idivFast64() for fast division on the GPU.
|
||||
static __device__ uint64_t idivRcp64_upto64(int x) {
|
||||
static constexpr uint64_t table[65] = {
|
||||
idivRcp64(0x01), idivRcp64(0x01), idivRcp64(0x02), idivRcp64(0x03),
|
||||
idivRcp64(0x04), idivRcp64(0x05), idivRcp64(0x06), idivRcp64(0x07),
|
||||
idivRcp64(0x08), idivRcp64(0x09), idivRcp64(0x0a), idivRcp64(0x0b),
|
||||
idivRcp64(0x0c), idivRcp64(0x0d), idivRcp64(0x0e), idivRcp64(0x0f),
|
||||
idivRcp64(0x10), idivRcp64(0x11), idivRcp64(0x12), idivRcp64(0x13),
|
||||
idivRcp64(0x14), idivRcp64(0x15), idivRcp64(0x16), idivRcp64(0x17),
|
||||
idivRcp64(0x18), idivRcp64(0x19), idivRcp64(0x1a), idivRcp64(0x1b),
|
||||
idivRcp64(0x1c), idivRcp64(0x1d), idivRcp64(0x1e), idivRcp64(0x1f),
|
||||
idivRcp64(0x20), idivRcp64(0x21), idivRcp64(0x22), idivRcp64(0x23),
|
||||
idivRcp64(0x24), idivRcp64(0x25), idivRcp64(0x26), idivRcp64(0x27),
|
||||
idivRcp64(0x28), idivRcp64(0x29), idivRcp64(0x2a), idivRcp64(0x2b),
|
||||
idivRcp64(0x2c), idivRcp64(0x2d), idivRcp64(0x2e), idivRcp64(0x2f),
|
||||
idivRcp64(0x30), idivRcp64(0x31), idivRcp64(0x32), idivRcp64(0x33),
|
||||
idivRcp64(0x34), idivRcp64(0x35), idivRcp64(0x36), idivRcp64(0x37),
|
||||
idivRcp64(0x38), idivRcp64(0x39), idivRcp64(0x3a), idivRcp64(0x3b),
|
||||
idivRcp64(0x3c), idivRcp64(0x3d), idivRcp64(0x3e), idivRcp64(0x3f),
|
||||
idivRcp64(0x40)
|
||||
};
|
||||
return table[x];
|
||||
}
|
||||
|
||||
static __device__ uint32_t idivRcp32_upto64(int x) {
|
||||
return idivRcp64_upto64(x)>>32;
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct ncclCoopCta {
|
||||
__device__ void sync() { __syncthreads(); }
|
||||
__device__ int self() { return threadIdx.x; }
|
||||
__device__ int count() { return blockDim.x; }
|
||||
};
|
||||
struct ncclCoopWarps {
|
||||
int log2_nWarps;
|
||||
__device__ void sync() {
|
||||
asm volatile("barrier.sync %0, %1;" :: "r"(1 + (threadIdx.x>>(5+log2_nWarps))), "r"(32<<log2_nWarps) : "memory");
|
||||
}
|
||||
__device__ int self() { return threadIdx.x & ((32<<log2_nWarps)-1); }
|
||||
__device__ int count() { return 32<<log2_nWarps; }
|
||||
};
|
||||
struct ncclCoopWarp {
|
||||
__device__ void sync() { __syncwarp(); }
|
||||
__device__ int self() { return threadIdx.x%32; }
|
||||
__device__ int count() { return 32; }
|
||||
};
|
||||
}
|
||||
struct ncclSymkArgsHandler {
|
||||
ncclDevComm const& comm;
|
||||
ncclLLA2AHandle const& lsaLLA2A;
|
||||
struct ncclSymkChannelWorkRange* channelWorkRange;
|
||||
struct ncclSymkDevWork* devWork;
|
||||
uint32_t nRanks_rcp32;
|
||||
|
||||
namespace {
|
||||
static constexpr int ncclSymPrims_UseBarrier = 1;
|
||||
static constexpr int ncclSymPrims_UseLL = 2;
|
||||
static constexpr int ncclSymPrims_UseMultimem = 4;
|
||||
struct ncclSymPrims {
|
||||
int flags;
|
||||
int const &rank;
|
||||
int const &nRanks;
|
||||
uint32_t const &nRanks_rcp32;
|
||||
int block, nBlocks;
|
||||
uint32_t nBlocks_rcp32;
|
||||
uint32_t nBlocks_nWarps_rcp32;
|
||||
uint32_t nRanks_nBlocks_rcp32;
|
||||
uint32_t nWarpPerRank, nWarpPerRank_rcp32;
|
||||
struct ncclSymDevBase* const &base;
|
||||
uintptr_t offsetMc;
|
||||
__device__ ncclSymkArgsHandler(ncclSymkDevWorkArgs const* args):
|
||||
comm(args->kcomm.devComm),
|
||||
lsaLLA2A(args->kcomm.lsaLLA2A) {
|
||||
channelWorkRange = args->getWorkRange();
|
||||
|
||||
uint32_t const &stride4G;
|
||||
uint32_t barEpoch;
|
||||
uint32_t llEpoch;
|
||||
|
||||
__device__ ncclSymPrims(ncclSymDevComm const &comm, int flags):
|
||||
flags(flags),
|
||||
rank(comm.rank),
|
||||
nRanks(comm.nRanks),
|
||||
nRanks_rcp32(comm.nRanks_rcp32),
|
||||
block(blockIdx.x),
|
||||
nBlocks(gridDim.x),
|
||||
nBlocks_rcp32(idivRcp32_upto64(nBlocks)),
|
||||
nBlocks_nWarps_rcp32(imulRcp32(nBlocks, nBlocks_rcp32, blockDim.x/32, idivRcp32_upto64(blockDim.x/32))),
|
||||
nRanks_nBlocks_rcp32(imulRcp32(nRanks, nRanks_rcp32, gridDim.x, nBlocks_rcp32)),
|
||||
nWarpPerRank(idivFast32(nBlocks*blockDim.x/32, nRanks, nRanks_rcp32)),
|
||||
nWarpPerRank_rcp32(idivRcp32_upto64(nWarpPerRank)),
|
||||
base(comm.base),
|
||||
offsetMc((flags & ncclSymPrims_UseMultimem) ? (char*)comm.baseMc - (char*)base : 0x0),
|
||||
stride4G(comm.stride4G) {
|
||||
|
||||
#if CUDART_VERSION >= 12030 && __CUDA_ARCH__ >= 900
|
||||
cudaGridDependencySynchronize();
|
||||
#endif
|
||||
|
||||
if ((flags & ncclSymPrims_UseBarrier) && threadIdx.x < nRanks) {
|
||||
barEpoch = (flags & ncclSymPrims_UseMultimem) ? base->barEpochMc[block] : base->barEpochUc[block];
|
||||
}
|
||||
if (flags & ncclSymPrims_UseLL) llEpoch = base->llEpoch[block] + 2;
|
||||
}
|
||||
__device__ ~ncclSymPrims() {
|
||||
if (threadIdx.x == 0) {
|
||||
if (flags & ncclSymPrims_UseBarrier) {
|
||||
((flags & ncclSymPrims_UseMultimem) ? base->barEpochMc : base->barEpochUc)[block] = barEpoch;
|
||||
}
|
||||
if (flags & ncclSymPrims_UseLL) base->llEpoch[block] = llEpoch - 2;
|
||||
}
|
||||
devWork = args->getWorks(args->nMaxChannels);
|
||||
nRanks_rcp32 = comm.nRanks_rcp32;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
__device__ T* peerPtr(int peer, T* selfPtr) {
|
||||
return add4G(selfPtr, (peer-rank)*stride4G);
|
||||
__device__ void getWorkRange(int block,
|
||||
uint16_t& workLo, size_t& indexLo, uint16_t& workHi, size_t& indexHi) {
|
||||
constexpr int EltPerCell = NCCL_SYM_KERNEL_CELL_SIZE / sizeof(T);
|
||||
uint32_t fracLo, fracHi;
|
||||
|
||||
// Where the work begins
|
||||
workLo = (block==0) ? 0 : channelWorkRange[block-1].workHi; // start where predecessor ends
|
||||
fracLo = (block==0) ? 0 : channelWorkRange[block-1].fracHi + 1;
|
||||
// If the predecessor ended on the work boundary, then we step to the beginning of the next work.
|
||||
// This ensures we never have empty parts.
|
||||
if (fracLo == 0x10000) {
|
||||
workLo++;
|
||||
fracLo = 0;
|
||||
}
|
||||
struct ncclSymkDevWork const& dw = devWork[workLo];
|
||||
indexLo = ((fracLo * divUp(dw.nElts, EltPerCell)) >> 16) * EltPerCell;
|
||||
|
||||
// Where the work ends
|
||||
workHi = channelWorkRange[block].workHi;
|
||||
fracHi = channelWorkRange[block].fracHi + 1;
|
||||
indexHi = min(((fracHi * divUp(dw.nElts, EltPerCell)) >> 16) * EltPerCell, dw.nElts);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
__device__ T* multimemPtr(T* selfPtr) {
|
||||
return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(selfPtr) + offsetMc);
|
||||
__device__ void getWorkRangeFused(int blockIdx, int w,
|
||||
int& block, int& nBlocks, size_t& indexLo, size_t& indexHi) {
|
||||
constexpr int EltPerCell = NCCL_SYM_KERNEL_CELL_SIZE / sizeof(T);
|
||||
struct ncclSymkDevWork const& dw = devWork[w];
|
||||
uint32_t fracLo, fracHi;
|
||||
int lastBlock;
|
||||
|
||||
block = blockIdx - dw.sChannelId;
|
||||
nBlocks = dw.nChannels;
|
||||
lastBlock = dw.sChannelId+dw.nChannels-1;
|
||||
|
||||
// Where the work begins
|
||||
fracLo = (dw.sChannelId==0) ? 0 : ((channelWorkRange[dw.sChannelId-1].fracHi + 1) & 0xFFFF);
|
||||
indexLo = ((fracLo * divUp(dw.nElts, EltPerCell)) >> 16) * EltPerCell;
|
||||
fracHi = (channelWorkRange[lastBlock].workHi == w) ? channelWorkRange[lastBlock].fracHi + 1 : 0x10000;
|
||||
indexHi = min(((fracHi * divUp(dw.nElts, EltPerCell)) >> 16) * EltPerCell, dw.nElts);
|
||||
}
|
||||
|
||||
__device__ void barrierArrive(ncclCoopCta cta, bool release) {
|
||||
cta.sync();
|
||||
#if __CUDA_ARCH__ < 700
|
||||
if (release) {
|
||||
if (cta.self() == 0) __threadfence_system();
|
||||
cta.sync();
|
||||
}
|
||||
#endif
|
||||
if (flags & ncclSymPrims_UseMultimem) {
|
||||
#if __CUDA_ARCH__ >= 900 && CUDART_VERSION >= 12010
|
||||
if (cta.self() == 0) {
|
||||
uint32_t* inbox = &multimemPtr(base)->barInboxMc[block];
|
||||
if (release) {
|
||||
asm volatile("multimem.red.release.sys.add.u32 [%0],1;" :: "l"(inbox));
|
||||
template<typename T, typename Fn>
|
||||
__device__ void forEachWork(Fn const& fn) {
|
||||
uint16_t workLo, workHi;
|
||||
size_t indexLo, indexHi;
|
||||
|
||||
getWorkRange<T>(blockIdx.x, workLo, indexLo, workHi, indexHi);
|
||||
|
||||
size_t currentIndexLo = indexLo;
|
||||
#pragma unroll 1
|
||||
for (int w = workLo; w <= workHi; w++) {
|
||||
struct ncclSymkDevWork const& dw = devWork[w];
|
||||
size_t const& nAllElts = dw.nElts;
|
||||
size_t currentIndexHi;
|
||||
int block, nBlocks;
|
||||
if (blockIdx.x >= dw.sChannelId && blockIdx.x < dw.sChannelId + dw.nChannels) {
|
||||
getWorkRangeFused<T>(blockIdx.x, w, block, nBlocks, currentIndexLo, currentIndexHi);
|
||||
} else {
|
||||
asm volatile("multimem.red.relaxed.sys.add.u32 [%0],1;" :: "l"(inbox));
|
||||
currentIndexHi = (w < workHi) ? nAllElts : indexHi;
|
||||
block = 0;
|
||||
nBlocks = 1;
|
||||
}
|
||||
|
||||
fn(block, nBlocks, currentIndexHi - currentIndexLo, nAllElts,
|
||||
ncclSymPtr<T>(dw.inputWin, dw.inputOff) + currentIndexLo,
|
||||
ncclSymPtr<T>(dw.outputWin, dw.outputOff) + currentIndexLo);
|
||||
|
||||
currentIndexLo = 0;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
int r = cta.self();
|
||||
if (r != rank && r < nRanks) {
|
||||
uint32_t* inbox = &peerPtr(r, base)->barInboxPerPeer[block*nRanks + rank];
|
||||
#if __CUDA_ARCH__ >= 700
|
||||
if (release) {
|
||||
asm volatile("st.release.sys.u32 [%0],%1;" :: "l"(inbox), "r"(barEpoch+1));
|
||||
} else {
|
||||
asm volatile("st.relaxed.sys.u32 [%0],%1;" :: "l"(inbox), "r"(barEpoch+1));
|
||||
}
|
||||
#else
|
||||
if (release) {
|
||||
__atomic_store_n(inbox, barEpoch + 1, __ATOMIC_RELEASE);
|
||||
} else {
|
||||
__atomic_store_n(inbox, barEpoch + 1, __ATOMIC_RELAXED);
|
||||
}
|
||||
// asm volatile("st.volatile.u32 [%0],%1;" :: "l"(inbox), "r"(barEpoch+1));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__device__ void barrierWait(ncclCoopCta cta, bool acquire) {
|
||||
if (flags & ncclSymPrims_UseMultimem) {
|
||||
#if __CUDA_ARCH__ >= 900
|
||||
if (cta.self() == 0) {
|
||||
uint32_t* inbox = &base->barInboxMc[block];
|
||||
while (true) {
|
||||
uint32_t got;
|
||||
if (acquire) {
|
||||
asm volatile("ld.acquire.sys.u32 %0,[%1];" : "=r"(got) : "l"(inbox));
|
||||
} else {
|
||||
asm volatile("ld.relaxed.sys.u32 %0,[%1];" : "=r"(got) : "l"(inbox));
|
||||
}
|
||||
if (got-(barEpoch+nRanks) <= uint32_t(-1)>>1) break;
|
||||
}
|
||||
barEpoch += nRanks;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
int r = cta.self();
|
||||
if (r != rank && r < nRanks) {
|
||||
uint32_t* inbox = &base->barInboxPerPeer[block*nRanks + r];
|
||||
while (true) {
|
||||
uint32_t got;
|
||||
#if __CUDA_ARCH__ >= 700
|
||||
if (acquire) {
|
||||
asm volatile("ld.acquire.sys.u32 %0,[%1];" : "=r"(got) : "l"(inbox));
|
||||
} else {
|
||||
asm volatile("ld.relaxed.sys.u32 %0,[%1];" : "=r"(got) : "l"(inbox));
|
||||
}
|
||||
#else
|
||||
if (acquire) {
|
||||
got = __atomic_load_n(inbox, __ATOMIC_ACQUIRE);
|
||||
} else {
|
||||
got = __atomic_load_n(inbox, __ATOMIC_RELAXED);
|
||||
}
|
||||
// asm volatile("ld.volatile.u32 %0,[%1];" : "=r"(got) : "l"(inbox));
|
||||
#endif
|
||||
if (got-(barEpoch+1) <= uint32_t(-1)>>1) break;
|
||||
}
|
||||
}
|
||||
#if __CUDA_ARCH__ < 700
|
||||
if (acquire) {
|
||||
cta.sync();
|
||||
if (cta.self() == 0) __threadfence();
|
||||
}
|
||||
#endif
|
||||
barEpoch += 1;
|
||||
}
|
||||
cta.sync();
|
||||
}
|
||||
template<typename T, typename Fn>
|
||||
__device__ void singleWork(Fn const& fn) {
|
||||
uint16_t w;
|
||||
size_t indexLo, indexHi;
|
||||
|
||||
__device__ void endLL(ncclCoopCta cta) {
|
||||
if (__builtin_expect(llEpoch >= -2u, false)) {
|
||||
cta.sync();
|
||||
uint4* buf = ncclSymDevBase_getLLBuf(base, nRanks, block, llEpoch);
|
||||
int epochSize = ncclSymLLEpochSize(nRanks);
|
||||
#pragma unroll 4
|
||||
for (int i=cta.self(); i*16 < epochSize; i += cta.count()) {
|
||||
buf[i] = uint4{0, 0, 0, 0};
|
||||
}
|
||||
}
|
||||
cta.sync();
|
||||
llEpoch += (llEpoch == -1u) ? 3 : 1;
|
||||
}
|
||||
getWorkRange<T>(blockIdx.x, w, indexLo, w, indexHi);
|
||||
|
||||
template<typename T>
|
||||
__device__ void sendLL(int peer, int slot, T val) {
|
||||
union { T tmp; uint32_t u32[divUp(sizeof(T),8)][2]; };
|
||||
tmp = val;
|
||||
uint4* buf = ncclSymDevBase_getLLBuf(peerPtr(peer, base), nRanks, block, llEpoch) + slot;
|
||||
#pragma unroll
|
||||
for (int u=0; u < divUp(sizeof(T),8); u++) {
|
||||
using Vec = uint32_t __attribute__((ext_vector_type(4)));
|
||||
Vec i4;
|
||||
i4[0] = u32[u][0];
|
||||
i4[1] = llEpoch;
|
||||
i4[2] = u32[u][1];
|
||||
i4[3] = llEpoch;
|
||||
#if defined(__gfx950__)
|
||||
asm volatile ("flat_store_dwordx4 %0, %1 sc0 sc1 nt" :: "v"(buf + ncclSymLLMaxSlots(sizeof(T))*u), "v"(i4));
|
||||
#else
|
||||
__builtin_nontemporal_store(i4, (Vec*)(buf + ncclSymLLMaxSlots(sizeof(T))*u));
|
||||
#endif
|
||||
// asm volatile("st.volatile.v4.u32 [%0],{%1,%3,%2,%3};" :: "l"(buf + ncclSymLLMaxSlots(sizeof(T))*u), "r"(u32[u][0]), "r"(u32[u][1]), "r"(llEpoch));
|
||||
}
|
||||
}
|
||||
struct ncclSymkDevWork const& dw = devWork[w];
|
||||
|
||||
template<typename T>
|
||||
__device__ void bcastLL(int slot, T val) {
|
||||
if (flags & ncclSymPrims_UseMultimem) {
|
||||
union { T tmp; uint32_t u32[divUp(sizeof(T),8)][2]; };
|
||||
tmp = val;
|
||||
uint4* bufmc = ncclSymDevBase_getLLBuf(multimemPtr(base), nRanks, block, llEpoch) + slot;
|
||||
#pragma unroll
|
||||
for (int u=0; u < divUp(sizeof(T),8); u++) {
|
||||
using Vec = uint32_t __attribute__((ext_vector_type(4)));
|
||||
Vec i4;
|
||||
i4[0] = u32[u][0];
|
||||
i4[1] = llEpoch;
|
||||
i4[2] = u32[u][1];
|
||||
i4[3] = llEpoch;
|
||||
#if defined(__gfx950__)
|
||||
asm volatile ("flat_store_dwordx4 %0, %1 sc0 sc1 nt" :: "v"(bufmc + ncclSymLLMaxSlots(sizeof(T))*u), "v"(i4));
|
||||
#else
|
||||
__builtin_nontemporal_store(i4, (Vec*)(bufmc + ncclSymLLMaxSlots(sizeof(T))*u));
|
||||
#endif
|
||||
// asm volatile("st.volatile.v4.u32 [%0],{%1,%3,%2,%3};" :: "l"(bufmc + ncclSymLLMaxSlots(sizeof(T))*u), "r"(u32[u][0]), "r"(u32[u][1]), "r"(llEpoch));
|
||||
}
|
||||
} else {
|
||||
union { T tmp; uint32_t u32[divUp(sizeof(T),8)][2]; };
|
||||
tmp = val;
|
||||
uint4* buf0 = ncclSymDevBase_getLLBuf(peerPtr(0, base), nRanks, block, llEpoch) + slot;
|
||||
int dr = 0;
|
||||
int r = rank;
|
||||
#pragma unroll 1
|
||||
for (; dr+8 <= nRanks; dr += 8) {
|
||||
#pragma unroll
|
||||
for (int ur=0; ur < 8; ur++) {
|
||||
uint4* buf = add4G(buf0, r*stride4G);
|
||||
#pragma unroll
|
||||
for (int u=0; u < divUp(sizeof(T),8); u++) {
|
||||
using Vec = uint32_t __attribute__((ext_vector_type(4)));
|
||||
Vec i4;
|
||||
i4[0] = u32[u][0];
|
||||
i4[1] = llEpoch;
|
||||
i4[2] = u32[u][1];
|
||||
i4[3] = llEpoch;
|
||||
#if defined(__gfx950__)
|
||||
asm volatile ("flat_store_dwordx4 %0, %1 sc0 sc1 nt" :: "v"(buf + ncclSymLLMaxSlots(sizeof(T))*u), "v"(i4));
|
||||
#else
|
||||
__builtin_nontemporal_store(i4, (Vec*)((buf + ncclSymLLMaxSlots(sizeof(T))*u)));
|
||||
#endif
|
||||
// asm volatile("st.volatile.v4.u32 [%0],{%1,%3,%2,%3};" :: "l"(buf + ncclSymLLMaxSlots(sizeof(T))*u), "r"(u32[u][0]), "r"(u32[u][1]), "r"(llEpoch));
|
||||
}
|
||||
r += 1;
|
||||
if (r == nRanks) r = 0;
|
||||
}
|
||||
}
|
||||
#pragma unroll
|
||||
for (int ur=0; ur < 8; ur++, dr++) {
|
||||
if (dr == nRanks) break;
|
||||
uint4* buf = add4G(buf0, r*stride4G);
|
||||
#pragma unroll
|
||||
for (int u=0; u < divUp(sizeof(T),8); u++) {
|
||||
using Vec = uint32_t __attribute__((ext_vector_type(4)));
|
||||
Vec i4;
|
||||
i4[0] = u32[u][0];
|
||||
i4[1] = llEpoch;
|
||||
i4[2] = u32[u][1];
|
||||
i4[3] = llEpoch;
|
||||
#if defined(__gfx950__)
|
||||
asm volatile ("flat_store_dwordx4 %0, %1 sc0 sc1 nt" :: "v"(buf + ncclSymLLMaxSlots(sizeof(T))*u), "v"(i4));
|
||||
#else
|
||||
__builtin_nontemporal_store(i4, (Vec*)(buf + ncclSymLLMaxSlots(sizeof(T))*u));
|
||||
#endif
|
||||
// asm volatile("st.volatile.v4.u32 [%0],{%1,%3,%2,%3};" :: "l"(buf + ncclSymLLMaxSlots(sizeof(T))*u), "r"(u32[u][0]), "r"(u32[u][1]), "r"(llEpoch));
|
||||
}
|
||||
r += 1;
|
||||
if (r == nRanks) r = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<int nSlotsMin, int nSlotsMax, typename T>
|
||||
__device__ void recvLL(int slot0, int nSlots, int stride, T(&elts)[nSlotsMax]) {
|
||||
uint4* buf = ncclSymDevBase_getLLBuf(base, nRanks, block, llEpoch) + slot0;
|
||||
uint4 tmp[nSlotsMax][divUp(sizeof(T),8)];
|
||||
//int spins=0;
|
||||
while (true) {
|
||||
#pragma unroll
|
||||
for (int u=0; u < nSlotsMax; u++) {
|
||||
if (u < nSlotsMin || u < nSlots) {
|
||||
#pragma unroll
|
||||
for (int v=0; v < divUp(sizeof(T),8); v++) {
|
||||
tmp[u][v] = *(buf + u * stride + v * ncclSymLLMaxSlots(sizeof(T)));
|
||||
// asm volatile("ld.volatile.v4.u32 {%0,%1,%2,%3},[%4];" : "=r"(tmp[u][v].x), "=r"(tmp[u][v].y), "=r"(tmp[u][v].z), "=r"(tmp[u][v].w) : "l"(buf + u*stride + v*ncclSymLLMaxSlots(sizeof(T))));
|
||||
}
|
||||
}
|
||||
}
|
||||
bool okAll = true;
|
||||
#pragma unroll
|
||||
for (int u=0; u < nSlotsMax; u++) {
|
||||
#pragma unroll
|
||||
for (int v=0; v < divUp(sizeof(T),8); v++) {
|
||||
if (u < nSlotsMin || u < nSlots) {
|
||||
bool ok = tmp[u][v].y == llEpoch &&
|
||||
tmp[u][v].w == llEpoch;
|
||||
okAll &= ok;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (__builtin_expect(okAll, true)) break;
|
||||
//if (spins++ == 10<<20) spins=0;
|
||||
}
|
||||
#pragma unroll
|
||||
for (int u=0; u < nSlotsMax; u++) {
|
||||
if (nSlotsMin <= u && u == nSlots) break;
|
||||
union { T val; uint32_t u32[divUp(sizeof(T),8)][2]; };
|
||||
#pragma unroll
|
||||
for (int v=0; v < divUp(sizeof(T),8); v++) {
|
||||
u32[v][0] = tmp[u][v].x;
|
||||
u32[v][1] = tmp[u][v].z;
|
||||
}
|
||||
elts[u] = val;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Pack, typename T, typename Red, int Unroll=8>
|
||||
__device__ Pack recvReduceLL(int slot, int stride, Red red) {
|
||||
using Acc = typename Red::EltType;
|
||||
using AccPack = BytePack<sizeof(Pack)*sizeof(Acc)/sizeof(T)>;
|
||||
AccPack acc;
|
||||
bool first = true;
|
||||
int r = 0;
|
||||
#pragma unroll 1
|
||||
for (; r+Unroll <= nRanks; r += Unroll) {
|
||||
Pack got[Unroll];
|
||||
this->template recvLL</*Min=*/Unroll>(slot + r*stride, Unroll, stride, got);
|
||||
AccPack acc0 = applyCast<T, Acc>(got[0]);
|
||||
acc = first ? acc0 : applyReduce(red, acc, acc0);
|
||||
first = false;
|
||||
#pragma unroll
|
||||
for (int i=1; i < Unroll; i++) acc = applyReduce(red, acc, applyCast<T, Acc>(got[i]));
|
||||
}
|
||||
if (r < nRanks) {
|
||||
Pack got[Unroll];
|
||||
this->template recvLL</*Min=*/1>(slot + r*stride, nRanks-r, stride, got);
|
||||
AccPack acc0 = applyCast<T, Acc>(got[0]);
|
||||
acc = first ? acc0 : applyReduce(red, acc, acc0);
|
||||
#pragma unroll
|
||||
for (int i=1; i < Unroll-1; i++) {
|
||||
if (r+i < nRanks) acc = applyReduce(red, acc, applyCast<T, Acc>(got[i]));
|
||||
}
|
||||
}
|
||||
return applyCast<Acc, T>(acc);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
__device__ T recvLL(int slot) {
|
||||
T one[1];
|
||||
this->template recvLL<1, 1, T>(slot, 1, 0, one);
|
||||
return one[0];
|
||||
}
|
||||
|
||||
template<typename Coop, typename T>
|
||||
__device__ void coopRecvLL(Coop coop, int slot0, int nSlots, T* dst) {
|
||||
int me = coop.self();
|
||||
if (me < nSlots) {
|
||||
uint4* buf = ncclSymDevBase_getLLBuf(base, nRanks, block, llEpoch) + slot0 + me;
|
||||
uint4 got[divUp(sizeof(T), 8)];
|
||||
//int spins=0;
|
||||
#pragma unroll 1
|
||||
while (true) {
|
||||
#pragma unroll
|
||||
for (int u=0; u < divUp(sizeof(T), 8); u++) {
|
||||
got[u] = *((buf + u * ncclSymLLMaxSlots(sizeof(T))));
|
||||
// asm volatile("ld.volatile.v4.u32 {%0,%1,%2,%3},[%4];" : "=r"(got[u].x), "=r"(got[u].y), "=r"(got[u].z), "=r"(got[u].w) : "l"(buf + u*ncclSymLLMaxSlots(sizeof(T))));
|
||||
}
|
||||
bool ok = true;
|
||||
#pragma unroll
|
||||
for (int u=0; u < divUp(sizeof(T), 8); u++) {
|
||||
ok &= got[u].y == llEpoch;
|
||||
ok &= got[u].w == llEpoch;
|
||||
}
|
||||
if (__builtin_expect(ok, true)) break;
|
||||
//if (++spins == 10<<20) { spins=0; printf("r=%d LL spin @ ix=%d got=%d want=%d\n", rank, slot0+me, got[0].y, llEpoch); }
|
||||
}
|
||||
union { T val; uint32_t u32[divUp(sizeof(T), 8)][2]; };
|
||||
#pragma unroll
|
||||
for (int u=0; u < divUp(sizeof(T), 8); u++) {
|
||||
u32[u][0] = got[u].x;
|
||||
u32[u][1] = got[u].z;
|
||||
}
|
||||
dst[slot0 + me] = val;
|
||||
}
|
||||
fn(indexHi - indexLo, dw.nElts,
|
||||
ncclSymPtr<T>(dw.inputWin, dw.inputOff) + indexLo,
|
||||
ncclSymPtr<T>(dw.outputWin, dw.outputOff) + indexLo);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template<template<typename> typename Red, typename T, bool nvls>
|
||||
struct ncclSymAccumType { using Type = T; };
|
||||
struct ncclSymkAccumType { using Type = T; };
|
||||
|
||||
// Only Red's whose opArg is invariant w.r.t. the datatype can have a different
|
||||
// accumulator type. At the moment this excludes integer min/max, sumpostdiv,
|
||||
// and premulsum.
|
||||
template<> struct ncclSymAccumType<FuncSum, __half, false> { using Type = float; };
|
||||
template<> struct ncclSymkAccumType<FuncSum, __half, false> { using Type = float; };
|
||||
#if defined(__CUDA_BF16_TYPES_EXIST__)
|
||||
template<> struct ncclSymAccumType<FuncSum, __nv_bfloat16, false> { using Type = float; };
|
||||
template<> struct ncclSymkAccumType<FuncSum, __nv_bfloat16, false> { using Type = float; };
|
||||
#endif
|
||||
#if defined(__CUDA_FP8_TYPES_EXIST__)
|
||||
template<> struct ncclSymAccumType<FuncSum, __nv_fp8_e4m3, false> { using Type = float; };
|
||||
template<> struct ncclSymAccumType<FuncSum, __nv_fp8_e5m2, false> { using Type = float; };
|
||||
template<> struct ncclSymkAccumType<FuncSum, __nv_fp8_e4m3, false> { using Type = float; };
|
||||
template<> struct ncclSymkAccumType<FuncSum, __nv_fp8_e5m2, false> { using Type = float; };
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -1,38 +1,39 @@
|
||||
// Modification Copyright (c) Advanced Micro Devices, Inc., or its affiliates.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "symmetric.h"
|
||||
#include "sym_kernels.h"
|
||||
#include "symmetric/kernel.h"
|
||||
#include "symmetric/primitives.h"
|
||||
|
||||
template<int BytePerPack, int UnrollPacks, int UnrollPeers, typename T, typename Red>
|
||||
static __device__ void reduceDeep(
|
||||
ncclSymPrims& prim, int tn, int t, bool waitNeeded,
|
||||
Red red, char* inputRank0, char* outputHere, int32_t nIters
|
||||
ncclSymkArgsHandler const& handler, int tn, int t,
|
||||
bool waitNeeded, ncclLsaBarrierSession<ncclCoopCta>& bar,
|
||||
Red red, ncclSymPtr<char> input, ncclSymPtr<char> output, int32_t nIters
|
||||
) {
|
||||
using Pack = BytePack<BytePerPack>;
|
||||
using Acc = typename Red::EltType;
|
||||
using AccPack = BytePack<BytePerPack*sizeof(Acc)/sizeof(T)>;
|
||||
|
||||
ncclTeam world = ncclTeamWorld(handler.comm);
|
||||
int wn = tn/WARP_SIZE;
|
||||
int w = t/WARP_SIZE;
|
||||
int lane = t%WARP_SIZE;
|
||||
int const& rank = prim.rank;
|
||||
int const& nRanks = prim.nRanks;
|
||||
uint32_t const& stride4G = prim.stride4G;
|
||||
Pack* inpRank0 = (Pack*)inputRank0 + intptr_t(w)*UnrollPacks*WARP_SIZE + lane;
|
||||
Pack* outHere = (Pack*)outputHere + intptr_t(w)*UnrollPacks*WARP_SIZE + lane;
|
||||
int const& rank = handler.comm.rank;
|
||||
int const& nRanks = handler.comm.nRanks;
|
||||
ncclSymPtr<Pack> inpPacks = (ncclSymPtr<Pack>)input + intptr_t(w)*UnrollPacks*WARP_SIZE + lane;
|
||||
ncclSymPtr<Pack> outPacks = (ncclSymPtr<Pack>)output + intptr_t(w)*UnrollPacks*WARP_SIZE + lane;
|
||||
Pack acc0[UnrollPacks];
|
||||
|
||||
nIters -= w;
|
||||
if (0 < nIters) {
|
||||
#pragma unroll
|
||||
for (int u=0; u < UnrollPacks; u++) {
|
||||
acc0[u] = add4G(inpRank0, rank*stride4G)[u*WARP_SIZE];
|
||||
acc0[u] = inpPacks.peerPtr(world, rank)[u*WARP_SIZE];
|
||||
}
|
||||
}
|
||||
|
||||
if (waitNeeded) prim.barrierWait(ncclCoopCta(), /*acquire=*/false);
|
||||
if (waitNeeded) bar.wait(ncclCoopCta(), cuda::memory_order_relaxed);
|
||||
|
||||
if (0 < nIters) {
|
||||
while (true) {
|
||||
@@ -42,7 +43,7 @@ static __device__ void reduceDeep(
|
||||
{ Pack tmp1[UnrollPacks];
|
||||
#pragma unroll
|
||||
for (int u=0; u < UnrollPacks; u++) {
|
||||
tmp1[u] = add4G(inpRank0, r*stride4G)[u*WARP_SIZE];
|
||||
tmp1[u] = inpPacks.peerPtr(world, r)[u*WARP_SIZE];
|
||||
}
|
||||
#pragma unroll
|
||||
for (int u=0; u < UnrollPacks; u++) {
|
||||
@@ -68,7 +69,7 @@ static __device__ void reduceDeep(
|
||||
if (partial && ur!=0 && dr+ur == nRanks) break;
|
||||
#pragma unroll UnrollPacks
|
||||
for (int u=0; u < UnrollPacks; u++) {
|
||||
tmp1[ur][u] = add4G(inpRank0, r*stride4G)[u*WARP_SIZE];
|
||||
tmp1[ur][u] = inpPacks.peerPtr(world, r)[u*WARP_SIZE];
|
||||
}
|
||||
r += 1;
|
||||
if (r == nRanks) r = 0;
|
||||
@@ -88,17 +89,17 @@ static __device__ void reduceDeep(
|
||||
for (int u=0; u < UnrollPacks; u++) acc0[u] = applyCast<Acc, T>(acc1[u]);
|
||||
|
||||
#pragma unroll UnrollPacks
|
||||
for (int u=0; u < UnrollPacks; u++) outHere[u*WARP_SIZE] = acc0[u];
|
||||
for (int u=0; u < UnrollPacks; u++) outPacks.localPtr()[u*WARP_SIZE] = acc0[u];
|
||||
|
||||
inpRank0 += intptr_t(wn)*UnrollPacks*WARP_SIZE;
|
||||
outHere += intptr_t(wn)*UnrollPacks*WARP_SIZE;
|
||||
inpPacks += intptr_t(wn)*UnrollPacks*WARP_SIZE;
|
||||
outPacks += intptr_t(wn)*UnrollPacks*WARP_SIZE;
|
||||
nIters -= wn;
|
||||
if (nIters <= 0) break;
|
||||
|
||||
// Load data for next iteration.
|
||||
#pragma unroll
|
||||
for (int u=0; u < UnrollPacks; u++) {
|
||||
acc0[u] = add4G(inpRank0, rank*stride4G)[u*WARP_SIZE];
|
||||
acc0[u] = inpPacks.peerPtr(world, rank)[u*WARP_SIZE];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -106,20 +107,22 @@ static __device__ void reduceDeep(
|
||||
|
||||
template<int UnrollPeers, typename Red, typename T>
|
||||
static __device__ void reduceEnds(
|
||||
ncclSymPrims& prim, int tn, int t, Red red,
|
||||
T* inputRank0, T* outputHere, size_t nElts, uint32_t nPreElts, size_t nSufElts
|
||||
ncclSymkArgsHandler const& handler, int tn, int t, Red red,
|
||||
ncclSymPtr<T> input, ncclSymPtr<T> output,
|
||||
size_t nElts, uint32_t nPreElts, size_t nSufElts
|
||||
) {
|
||||
using Acc = typename Red::EltType;
|
||||
|
||||
int const& rank = prim.rank;
|
||||
int const& nRanks = prim.nRanks;
|
||||
uint32_t const& stride4G = prim.stride4G;
|
||||
BytePack<sizeof(T)>* inpRank0 = (BytePack<sizeof(T)>*)inputRank0;
|
||||
BytePack<sizeof(T)>* outHere = (BytePack<sizeof(T)>*)outputHere;
|
||||
ncclTeam world = ncclTeamWorld(handler.comm);
|
||||
int const& rank = handler.comm.rank;
|
||||
int const& nRanks = handler.comm.nRanks;
|
||||
|
||||
ncclSymPtr<BytePack<sizeof(T)>> inpPacks = (ncclSymPtr<BytePack<sizeof(T)>>)input;
|
||||
ncclSymPtr<BytePack<sizeof(T)>> outPacks = (ncclSymPtr<BytePack<sizeof(T)>>)output;
|
||||
#pragma unroll 1
|
||||
for (size_t i = t; i < nPreElts+nSufElts; i += tn) {
|
||||
size_t elt = i < nPreElts ? i : nElts-nSufElts-nPreElts+i;
|
||||
BytePack<sizeof(T)> acc0 = *add4G(inpRank0+elt, rank*stride4G);
|
||||
BytePack<sizeof(T)> acc0 = inpPacks.peerPtr(world, rank)[elt];
|
||||
BytePack<sizeof(Acc)> acc1;
|
||||
BytePack<sizeof(T)> tmp[UnrollPeers];
|
||||
int dr = 1;
|
||||
@@ -138,7 +141,7 @@ static __device__ void reduceEnds(
|
||||
#pragma unroll
|
||||
for (int u=0; u < UnrollPeers-partial; u++) {
|
||||
if (partial && u!=0 && dr+u == nRanks) break;
|
||||
tmp[u] = *add4G(inpRank0+elt, r*stride4G);
|
||||
tmp[u] = inpPacks.peerPtr(world, r)[elt];
|
||||
r += 1;
|
||||
if (r == nRanks) r = 0;
|
||||
}
|
||||
@@ -155,26 +158,25 @@ static __device__ void reduceEnds(
|
||||
}
|
||||
|
||||
acc0 = applyCast<Acc, T>(acc1);
|
||||
outHere[elt] = acc0;
|
||||
outPacks.localPtr()[elt] = acc0;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Red, typename T>
|
||||
static __device__ void reduce(
|
||||
ncclSymPrims& prim, int tn, int t, bool waitNeeded,
|
||||
Red red, T* input, T* output, size_t nElts
|
||||
ncclSymkArgsHandler const& handler, int tn, int t, int nBlocks,
|
||||
bool waitNeeded, ncclLsaBarrierSession<ncclCoopCta>& bar,
|
||||
Red red, ncclSymPtr<T> input, ncclSymPtr<T> output, size_t nElts
|
||||
) {
|
||||
int nRanks = prim.nRanks;
|
||||
int nBlocks = prim.nBlocks;
|
||||
// Mpve input to rank=0
|
||||
input = prim.peerPtr(0, input);
|
||||
int const& nRanks = handler.comm.nRanks;
|
||||
int const& nRanks_rcp32 = handler.nRanks_rcp32;
|
||||
uint32_t nBlocks_rcp32 = nccl::utility::idivRcp32_upto64(nBlocks);
|
||||
uint32_t nRanks_nBlocks_rcp32 = nccl::utility::imulRcp32(nRanks, nRanks_rcp32, nBlocks, nBlocks_rcp32);
|
||||
|
||||
uintptr_t inputUptr = reinterpret_cast<uintptr_t>(input);
|
||||
uintptr_t outputUptr = reinterpret_cast<uintptr_t>(output);
|
||||
uint32_t alignment = uint32_t(inputUptr - outputUptr);
|
||||
uint32_t alignment = uint32_t(input.offset - output.offset);
|
||||
size_t nBytes = nElts*sizeof(T);
|
||||
|
||||
uint32_t nPreBytes = (16u - inputUptr)%16u;
|
||||
uint32_t nPreBytes = (16u - input.offset)%16u;
|
||||
nPreBytes = min((size_t)nPreBytes, nBytes);
|
||||
uintptr_t cursor = nPreBytes;
|
||||
|
||||
@@ -184,12 +186,12 @@ static __device__ void reduce(
|
||||
constexpr int BytePerPack = 16, UnrollPacks = 4, UnrollPeers = 2;
|
||||
constexpr int BytePerChunk = MinWarpPerBlock*UnrollPacks*WARP_SIZE*BytePerPack;
|
||||
uint32_t chunks = (nBytes-cursor)/BytePerChunk;
|
||||
chunks -= imodFast32(chunks, nRanks*nBlocks, prim.nRanks_nBlocks_rcp32);
|
||||
chunks -= imodFast32(chunks, nRanks*nBlocks, nRanks_nBlocks_rcp32);
|
||||
if (chunks != 0) {
|
||||
uintptr_t cursorAfter = cursor + uintptr_t(chunks)*BytePerChunk;
|
||||
reduceDeep<BytePerPack, UnrollPacks, UnrollPeers, T>(
|
||||
prim, tn, t, waitNeeded, red,
|
||||
(char*)input + cursor, (char*)output + cursor,
|
||||
handler, tn, t, waitNeeded, bar, red,
|
||||
(ncclSymPtr<char>)input + cursor, (ncclSymPtr<char>)output + cursor,
|
||||
chunks*MinWarpPerBlock
|
||||
);
|
||||
cursor = cursorAfter;
|
||||
@@ -201,12 +203,12 @@ static __device__ void reduce(
|
||||
constexpr int BytePerPack = 4, UnrollPacks = 4, UnrollPeers = 4;
|
||||
constexpr int BytePerChunk = MinWarpPerBlock*UnrollPacks*WARP_SIZE*BytePerPack;
|
||||
uint32_t chunks = (nBytes-cursor)/BytePerChunk;
|
||||
chunks -= imodFast32(chunks, nRanks*nBlocks, prim.nRanks_nBlocks_rcp32);
|
||||
chunks -= imodFast32(chunks, nRanks*nBlocks, nRanks_nBlocks_rcp32);
|
||||
if (chunks != 0) {
|
||||
uintptr_t cursorAfter = cursor + uintptr_t(chunks)*BytePerChunk;
|
||||
reduceDeep<(sizeof(T) <= BytePerPack ? BytePerPack : 0), UnrollPacks, UnrollPeers, T>(
|
||||
prim, tn, t, waitNeeded, red,
|
||||
(char*)input + cursor, (char*)output + cursor,
|
||||
handler, tn, t, waitNeeded, bar, red,
|
||||
(ncclSymPtr<char>)input + cursor, (ncclSymPtr<char>)output + cursor,
|
||||
chunks*MinWarpPerBlock
|
||||
);
|
||||
cursor = cursorAfter;
|
||||
@@ -214,42 +216,47 @@ static __device__ void reduce(
|
||||
}
|
||||
}
|
||||
|
||||
if (waitNeeded) prim.barrierWait(ncclCoopCta(), /*acquire=*/false);
|
||||
if (waitNeeded) bar.wait(ncclCoopCta(), cuda::memory_order_relaxed);
|
||||
|
||||
constexpr int UnrollPeers = 8;
|
||||
size_t nSufElts = (nBytes-cursor)/sizeof(T);
|
||||
reduceEnds<UnrollPeers>(prim, tn, t, red, input, output, nElts, nPreBytes/sizeof(T), nSufElts);
|
||||
reduceEnds<UnrollPeers>(handler, tn, t, red, input, output, nElts, nPreBytes/sizeof(T), nSufElts);
|
||||
}
|
||||
|
||||
|
||||
template<template<typename> typename Red, typename T>
|
||||
__device__ __forceinline__ void ncclSymRun_ReduceScatter_LD(ncclSymDevArgs const* args) {
|
||||
ncclSymPrims prim(args->comm, ncclSymPrims_UseBarrier);
|
||||
Red<typename ncclSymAccumType<Red, T, /*nvls=*/false>::Type> red(args->redOpArg);
|
||||
__device__ __forceinline__ void ncclSymkRun_ReduceScatter_LD(ncclSymkDevWorkArgs const* args) {
|
||||
ncclSymkArgsHandler handler{args};
|
||||
ncclLsaBarrierSession<ncclCoopCta> bar{
|
||||
ncclCoopCta(), handler.comm, ncclTeamTagLsa(), blockIdx.x
|
||||
};
|
||||
Red<typename ncclSymkAccumType<Red, T, /*nvls=*/false>::Type> red(handler.devWork->redOpArg);
|
||||
int const& rank = handler.comm.rank;
|
||||
|
||||
// Round robin warps over blocks.
|
||||
int t = flattenIx(threadIdx.x%WARP_SIZE, WARP_SIZE,
|
||||
prim.block, prim.nBlocks,
|
||||
threadIdx.x/WARP_SIZE, blockDim.x/WARP_SIZE);
|
||||
int tn = prim.nBlocks*blockDim.x;
|
||||
bar.arrive(ncclCoopCta(), cuda::memory_order_relaxed);
|
||||
|
||||
prim.barrierArrive(ncclCoopCta(), /*release=*/false);
|
||||
//prim.barrierWait(ncclCoopCta(), /*acquire=*/false);
|
||||
bool waitNeeded = true;
|
||||
handler.forEachWork<T>(
|
||||
[&]__device__(int block, int nBlocks, size_t nElts, size_t nAllElts,
|
||||
ncclSymPtr<T> input, ncclSymPtr<T> output) {
|
||||
// Round robin warps over blocks.
|
||||
int t = flattenIx(threadIdx.x%WARP_SIZE, WARP_SIZE,
|
||||
block, nBlocks,
|
||||
threadIdx.x/WARP_SIZE, blockDim.x/WARP_SIZE);
|
||||
int tn = nBlocks*blockDim.x;
|
||||
|
||||
reduce(prim, tn, t, /*waitNeeded=*/true, red, (T*)args->input + prim.rank*args->nElts, (T*)args->output, args->nElts);
|
||||
reduce(handler, tn, t, nBlocks, waitNeeded, bar, red, input + rank*nElts, output, nElts);
|
||||
|
||||
prim.barrierArrive(ncclCoopCta(), /*release=*/false);
|
||||
prim.barrierWait(ncclCoopCta(), /*acquire=*/false);
|
||||
waitNeeded = false;
|
||||
}
|
||||
);
|
||||
|
||||
bar.sync(ncclCoopCta(), cuda::memory_order_relaxed);
|
||||
}
|
||||
|
||||
|
||||
template<typename Red, typename T>
|
||||
static __device__ void reduceMultimem(
|
||||
ncclSymPrims& prim, int tn, int t, Red red, T* input, T* output, size_t nElts
|
||||
int tn, int t, Red red, T* input, T* output, size_t nElts
|
||||
) {
|
||||
// Mpve input to multimem
|
||||
input = prim.multimemPtr(input);
|
||||
|
||||
uintptr_t inputUptr = reinterpret_cast<uintptr_t>(input);
|
||||
uintptr_t outputUptr = reinterpret_cast<uintptr_t>(output);
|
||||
size_t nBytes = nElts*sizeof(T);
|
||||
@@ -294,41 +301,52 @@ static __device__ void reduceMultimem(
|
||||
uintptr_t cursor = i < nPreBytes ? i : nBytes-nSufBytes+(i-nPreBytes);
|
||||
BytePack<sizeof(T)> val = applyLoadMultimem<Red, sizeof(T)>(red, inputUptr + cursor);
|
||||
*reinterpret_cast<BytePack<sizeof(T)>*>(outputUptr + cursor) = val;
|
||||
cursor += tn*sizeof(T);
|
||||
}
|
||||
}
|
||||
|
||||
template<template<typename> typename Red, typename T>
|
||||
__device__ __forceinline__ void ncclSymRun_ReduceScatter_LDMC(ncclSymDevArgs const* args) {
|
||||
ncclSymPrims prim(args->comm, ncclSymPrims_UseBarrier|ncclSymPrims_UseMultimem);
|
||||
Red<typename ncclSymAccumType<Red, T, /*nvls=*/true>::Type> red(args->redOpArg);
|
||||
__device__ __forceinline__ void ncclSymkRun_ReduceScatter_LDMC(ncclSymkDevWorkArgs const* args) {
|
||||
ncclSymkArgsHandler handler{args};
|
||||
ncclLsaBarrierSession<ncclCoopCta> bar{
|
||||
ncclCoopCta(), handler.comm, ncclTeamTagLsa(), blockIdx.x, /*multimem=*/true
|
||||
};
|
||||
Red<typename ncclSymkAccumType<Red, T, /*nvls=*/true>::Type> red(handler.devWork->redOpArg);
|
||||
|
||||
// Round robin warps over blocks.
|
||||
int t = flattenIx(threadIdx.x%WARP_SIZE, WARP_SIZE,
|
||||
prim.block, prim.nBlocks,
|
||||
threadIdx.x/WARP_SIZE, blockDim.x/WARP_SIZE);
|
||||
int tn = prim.nBlocks*blockDim.x;
|
||||
int const& rank = handler.comm.rank;
|
||||
auto const& multimem = handler.comm.lsaMultimem;
|
||||
|
||||
prim.barrierArrive(ncclCoopCta(), /*release=*/false);
|
||||
prim.barrierWait(ncclCoopCta(), /*acquire=*/false);
|
||||
bar.sync(ncclCoopCta(), cuda::memory_order_relaxed);
|
||||
|
||||
reduceMultimem(prim, tn, t, red, (T*)args->input + prim.rank*args->nElts, (T*)args->output, args->nElts);
|
||||
handler.forEachWork<T>(
|
||||
[&]__device__(int block, int nBlocks, size_t nElts, size_t nAllElts,
|
||||
ncclSymPtr<T> input, ncclSymPtr<T> output) {
|
||||
// Round robin warps over blocks.
|
||||
int t = flattenIx(threadIdx.x%WARP_SIZE, WARP_SIZE,
|
||||
block, nBlocks,
|
||||
threadIdx.x/WARP_SIZE, blockDim.x/WARP_SIZE);
|
||||
int tn = nBlocks*blockDim.x;
|
||||
|
||||
prim.barrierArrive(ncclCoopCta(), /*release=*/false);
|
||||
prim.barrierWait(ncclCoopCta(), /*acquire=*/false);
|
||||
reduceMultimem(tn, t, red, input.multimemPtr(multimem) + rank*nElts, output.localPtr(), nElts);
|
||||
}
|
||||
);
|
||||
|
||||
bar.sync(ncclCoopCta(), cuda::memory_order_relaxed);
|
||||
}
|
||||
|
||||
// T is user type, EltType is the most aligned type
|
||||
template<typename T, typename Red, typename EltType>
|
||||
__device__ __forceinline__ void ncclSymRun_ReduceScatter_LL_body(
|
||||
ncclSymPrims &prim, Red red, EltType* input, EltType* output, int nElts, int nPacks, int nStrideElts) {
|
||||
__device__ __forceinline__ void ncclSymkRun_ReduceScatter_LL_body(
|
||||
ncclSymkArgsHandler& handler, ncclLLA2ASession<ncclCoopCta>& lla2a,
|
||||
Red red, EltType* input, EltType* output, int nElts, int nPacks, int nStrideElts) {
|
||||
using Pack = BytePack<8>;
|
||||
using Acc = typename Red::EltType;
|
||||
using AccPack = BytePack<8*sizeof(Acc)/sizeof(T)>;
|
||||
constexpr int EltPerPack = 8/sizeof(EltType);
|
||||
|
||||
int nRanks = prim.nRanks;
|
||||
int rank = prim.rank;
|
||||
int const& nRanks = handler.comm.nRanks;
|
||||
int const& rank = handler.comm.rank;
|
||||
int t = threadIdx.x;
|
||||
int tn = ncclSymMaxThreads;
|
||||
constexpr int tn = ncclSymkMaxThreads;
|
||||
ncclCoopCta cta;
|
||||
|
||||
#pragma unroll 1
|
||||
@@ -342,17 +360,25 @@ __device__ __forceinline__ void ncclSymRun_ReduceScatter_LL_body(
|
||||
#pragma unroll 1
|
||||
for (int i = t; i < nRanks*nIterPacks; i += tn) {
|
||||
Pack got = loadPack<Pack>(input + peer*nStrideElts, pack*EltPerPack, nElts);
|
||||
prim.sendLL(peer, rank*nIterPacks + pack, got);
|
||||
lla2a.send(peer, rank*nIterPacks + pack, got);
|
||||
peer += tn_div_nPacks;
|
||||
pack += tn_mod_nPacks;
|
||||
if (nIterPacks <= pack) { peer += 1; pack -= nIterPacks; }
|
||||
}
|
||||
|
||||
if (t < nIterPacks) {
|
||||
Pack got = prim.template recvReduceLL<Pack, T>(t, nIterPacks, red);
|
||||
storePack(output, t*EltPerPack, nElts, got);
|
||||
AccPack got = lla2a.template recvReduce</*Unroll=*/8, Pack>(
|
||||
/*slotStart=*/t, /*slotCount=*/nRanks, /*slotStride=*/nIterPacks,
|
||||
/*eltToAcc=*/[&] __device__ (Pack x)->AccPack {
|
||||
return applyCast<T, Acc>(x);
|
||||
},
|
||||
/*reduce=*/[&] __device__ (AccPack a, AccPack b)->AccPack {
|
||||
return applyReduce(red, a, b);
|
||||
}
|
||||
);
|
||||
storePack(output, t*EltPerPack, nElts, applyCast<Acc, T>(got));
|
||||
}
|
||||
prim.endLL(cta);
|
||||
lla2a.endEpoch(cta);
|
||||
|
||||
input += tn*EltPerPack;
|
||||
output += tn*EltPerPack;
|
||||
@@ -360,31 +386,34 @@ __device__ __forceinline__ void ncclSymRun_ReduceScatter_LL_body(
|
||||
nPacks -= tn;
|
||||
}
|
||||
}
|
||||
template<template<typename> typename Red, typename T>
|
||||
__device__ __forceinline__ void ncclSymRun_ReduceScatter_LL(ncclSymDevArgs const* args) {
|
||||
ncclSymPrims prim(args->comm, ncclSymPrims_UseLL);
|
||||
Red<typename ncclSymAccumType<Red, T, /*nvls=*/false>::Type> red(args->redOpArg);
|
||||
|
||||
template<template<typename> typename Red, typename T>
|
||||
__device__ __forceinline__ void ncclSymkRun_ReduceScatter_LL(ncclSymkDevWorkArgs const* args) {
|
||||
ncclSymkArgsHandler handler{args};
|
||||
ncclLLA2ASession<ncclCoopCta> lla2a(
|
||||
ncclCoopCta(), handler.comm, ncclTeamLsa(handler.comm), handler.lsaLLA2A, blockIdx.x, ncclSymkMaxThreads
|
||||
);
|
||||
Red<typename ncclSymkAccumType<Red, T, /*nvls=*/false>::Type> red(handler.devWork->redOpArg);
|
||||
using Pack = BytePack<8>;
|
||||
constexpr int EltPerPack = 8/sizeof(T);
|
||||
int nAllElts = args->nElts;
|
||||
int nAllPacks = divUp(nAllElts, EltPerPack);
|
||||
uint32_t nPackPerBlock, nPackModBlock;
|
||||
idivmodFast32(&nPackPerBlock, &nPackModBlock, nAllPacks, prim.nBlocks, prim.nBlocks_rcp32);
|
||||
int blockPackBegin = prim.block*nPackPerBlock + minval<int>(prim.block, nPackModBlock);
|
||||
int blockPackEnd = blockPackBegin + nPackPerBlock + (prim.block < nPackModBlock ? 1 : 0);
|
||||
int nPacks = blockPackEnd - blockPackBegin;
|
||||
int nElts = nAllElts - blockPackBegin*EltPerPack;
|
||||
nElts = min(nElts, nPacks*EltPerPack);
|
||||
T* input = (T*)args->input + blockPackBegin*EltPerPack;
|
||||
T* output = (T*)args->output + blockPackBegin*EltPerPack;
|
||||
|
||||
uint32_t lowBits = args->nElts*sizeof(T);
|
||||
lowBits |= (uint32_t)reinterpret_cast<uintptr_t>(args->input);
|
||||
lowBits |= (uint32_t)reinterpret_cast<uintptr_t>(args->output);
|
||||
if (__builtin_expect(lowBits%8 == 0, true)) {
|
||||
ncclSymRun_ReduceScatter_LL_body<T>(prim, red, (Pack*)input, (Pack*)output, nPacks, nPacks, nAllElts/EltPerPack);
|
||||
} else {
|
||||
ncclSymRun_ReduceScatter_LL_body<T>(prim, red, input, output, nElts, nPacks, nAllElts);
|
||||
}
|
||||
handler.singleWork<T>(
|
||||
[&]__device__(int nElts, int nAllElts,
|
||||
ncclSymPtr<T> inputPtr, ncclSymPtr<T> outputPtr) {
|
||||
int nPacks = divUp(nElts, EltPerPack);
|
||||
|
||||
T* input = (T*)inputPtr.localPtr();
|
||||
T* output = (T*)outputPtr.localPtr();
|
||||
|
||||
uint32_t lowBits = nElts*sizeof(T);
|
||||
lowBits |= (uintptr_t)input;
|
||||
lowBits |= (uintptr_t)output;
|
||||
if (__builtin_expect(lowBits%8 == 0, true)) {
|
||||
ncclSymkRun_ReduceScatter_LL_body<T>(handler, lla2a, red, (Pack*)input, (Pack*)output,
|
||||
nPacks, nPacks, divUp(nAllElts, EltPerPack));
|
||||
} else {
|
||||
ncclSymkRun_ReduceScatter_LL_body<T>(handler, lla2a, red, input, output, nElts, nPacks, nAllElts);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
Odkázat v novém úkolu
Zablokovat Uživatele