Use the RCCL device API and GIN#
The experimental RCCL device API lets GPU kernels communicate through a
device communicator (ncclDevComm). GPU-initiated networking (GIN) extends
that API with one-sided puts, signals, counters, and barriers across nodes.
RCCL 2.31 incorporates Device API and GIN enhancements from upstream
NCCL 2.31.2. This page describes those APIs and the limits of the AMD
host-proxy backend.
To supply a GIN backend rather than consume one, see Using the RCCL GIN plugin API.
NCCL_GIN_TYPE values for AMD backends are not compatible with 2.30.7:
rocSHMEM GDA moved from 5 to 6 and Anvil SDMA from 6 to 7 because NCCL 2.31
placed EFA GDA at 5. The IB proxy remains NCCL_GIN_TYPE=2.
Requirements#
GIN requires a supported network backend, symmetric memory, and collective
communicator and window setup. The validated AMD path is GIN_IB_PROXY:
export NCCL_GIN_TYPE=2
export NCCL_CUMEM_ENABLE=1
export NCCL_DMABUF_ENABLE=1
export NCCL_IB_MERGE_NICS=0
The ROCm runtime and NIC driver must support exporting and registering VMM
allocations. NCCL_DMABUF_ENABLE=1 is the validated registration path;
peer-memory registration can also be available on supported systems. Full GIN
connections require cross-rail reachability, so do not force
NCCL_CROSS_NIC=0 for world-team operations. Leave ginQueueDepth at its
default value of zero; the AMD host-proxy backend does not support a custom QP
depth.
The following snippets abbreviate error handling with NCCLCHECK. Replace it
with the application’s normal ncclResult_t error handling.
Before using GIN, query communicator properties and confirm that the selected backend and Device API are available:
ncclCommProperties properties = NCCL_COMM_PROPERTIES_INITIALIZER;
NCCLCHECK(ncclCommQueryProperties(comm, &properties));
if (!properties.deviceApiSupport ||
properties.ginType != NCCL_GIN_TYPE_PROXY) {
// Select a non-GIN path or report that this configuration is unsupported.
}
Allocate and register every communication buffer collectively:
void* buffer = nullptr;
ncclWindow_t window = nullptr;
NCCLCHECK(ncclMemAlloc(&buffer, bytes));
NCCLCHECK(ncclCommWindowRegister(
comm, buffer, bytes, &window, NCCL_WIN_COLL_SYMMETRIC));
Create a device communicator#
Always initialize requirements with
NCCL_DEV_COMM_REQUIREMENTS_INITIALIZER. The initializer records the header
version used to compile the application.
ncclDevCommRequirements reqs = NCCL_DEV_COMM_REQUIREMENTS_INITIALIZER;
reqs.ginConnectionType = NCCL_GIN_CONNECTION_FULL;
reqs.ginContextCount = 4; // Hint; inspect devComm.ginContextCount.
reqs.ginSignalCount = 1;
reqs.ginCounterCount = 1;
reqs.barrierCount = 1; // Generic ncclBarrierSession.
reqs.worldGinBarrierCount = 1;
ncclDevComm devComm{};
NCCLCHECK(ncclDevCommCreate(comm, &reqs, &devComm));
GIN contexts and their signal, counter, and queue resources are allocated per
device communicator. Two device communicators backed by the same host
communicator therefore don’t alias those resources. ginContextCount is a
request; use the value returned in devComm.ginContextCount when assigning
work to contexts.
ginTrafficClass overrides the host communicator traffic class for this
device communicator. The service level of the GIN connections comes from
NCCL_IB_SL when it is set, otherwise from ginTrafficClass or the host
communicator. Their RoCE traffic class comes from NCCL_GIN_IB_TC when it is
set, then from NCCL_IB_TC, and only then from ginTrafficClass or the
host communicator. Setting NCCL_GIN_IB_TC moves GIN traffic to its own RoCE
traffic class while the collective and point-to-point connections keep
NCCL_IB_TC. InfiniBand link layers don’t carry the traffic class, so
NCCL_GIN_IB_TC has no effect there; GIN traffic can be separated only
through the service level, by setting ginTrafficClass and leaving
NCCL_IB_SL unset. Set these values only when the fabric administrator
provides appropriate values.
Device code creates an ncclGin object for one returned context:
ncclGin gin{devComm, contextIndex, NCCL_GIN_RESOURCE_SHARING_GPU};
For example, a CTA can issue a put and wait for local queue completion:
__global__ void putKernel(
ncclDevComm devComm, ncclWindow_t source, ncclWindow_t destination,
size_t bytes, int peer) {
ncclGin gin{devComm, /*contextIndex=*/0};
if (threadIdx.x == 0) {
gin.put(ncclTeamWorld(devComm), peer,
destination, /*destinationOffset=*/0,
source, /*sourceOffset=*/0, bytes,
ncclGin_WeakSignalInc{/*signal=*/0});
}
gin.flush(ncclCoopCta());
}
flush makes the local source buffer reusable. It does not by itself prove
remote visibility; the peer must wait for the associated signal before using
the destination bytes.
NCCL_GIN_RESOURCE_SHARING_GPU permits sharing across the GPU;
NCCL_GIN_RESOURCE_SHARING_CTA limits sharing to a CTA. These modes select
resource-sharing behavior on direct device backends. The AMD GIN host-proxy
backend uses the same proxy queue behavior for both modes.
Choose strong or weak signal semantics#
Explicit signal types state which earlier puts become visible when a peer observes the signal:
A weak signal guarantees visibility of only the put carrying that signal. Use it for independent per-operation completion, as in the example above. It makes no guarantee about earlier puts, although a backend can order them.
A strong signal also guarantees visibility of every preceding put issued to the same peer through the same GIN context. Use it as a publication point for a sequence of puts.
For example, one strong signal can publish two buffers without attaching a signal to the first put:
if (threadIdx.x == 0) {
auto team = ncclTeamWorld(devComm);
gin.put(team, peer, destination, 0, source, 0, firstBytes,
ncclGin_None{});
gin.put(team, peer, destination, secondOffset,
source, secondOffset, secondBytes,
ncclGin_StrongSignalInc{/*signal=*/0});
}
gin.flush(ncclCoopCta());
After the peer’s waitSignal returns, both puts are visible. This ordering is
limited to the same peer and GIN context. A strong signal does not publish puts
issued through another context or to another peer.
The explicit indexed actions are ncclGin_StrongSignalInc,
ncclGin_StrongSignalAdd, ncclGin_WeakSignalInc, and
ncclGin_WeakSignalAdd. Equivalent StrongVASignal and WeakVASignal
actions target a window and offset; their window must be registered with
NCCL_WIN_STRICT_ORDERING.
ginStrongSignalsRequired defaults to true in
NCCL_DEV_COMM_REQUIREMENTS_INITIALIZER. The runtime rejects device
communicator creation if the selected backend cannot provide strong signals.
A kernel that uses only weak signals can set the field to false to permit
such a backend:
ncclDevCommRequirements reqs = NCCL_DEV_COMM_REQUIREMENTS_INITIALIZER;
reqs.ginConnectionType = NCCL_GIN_CONNECTION_FULL;
reqs.ginSignalCount = 1;
reqs.ginStrongSignalsRequired = false; // Kernel issues only weak signals.
Do not issue a strong action from a device communicator created with
ginStrongSignalsRequired=false; that usage is undefined. The older
ncclGin_SignalInc, ncclGin_SignalAdd, and VA equivalents are
deprecated. Their strength is selected globally by
ginStrongSignalsRequired; new code should use an explicit type.
Use world-team barriers and timeouts#
Reserve worldGinBarrierCount slots to construct a world-team GIN barrier
without manually allocating a barrier handle:
ncclGinBarrierSession<ncclCoopCta> barrier{
ncclCoopCta(), gin, ncclTeamTagWorld{}, barrierIndex};
ncclResult_t result = barrier.sync(
ncclCoopCta(), cuda::memory_order_acq_rel,
ncclGinFenceLevel::None, timeoutCycles);
The timeout overload returns ncclTimeout if all team members don’t arrive
within timeoutCycles. ncclGinFenceLevel is a bitmask: None is arrival
only, Put makes inbound (and self) puts visible, Get drains local gets,
and omitting the fence argument is Put | Get. Relaxed is a deprecated
alias for None. Pass ncclGinAllContexts(devComm) instead of a single
ncclGin when puts or gets were issued on more than one GIN context.
Barrier resources for ncclGinBarrierSession,
ncclLsaBarrierSession, and ncclBarrierSession are separate. Reserve
barrierCount for generic ncclBarrierSession objects; use
lsaBarrierCount, railGinBarrierCount, or worldGinBarrierCount for
the corresponding specialized session. barrierIndex must be smaller than
the selected count. Concurrent CTAs must use distinct indices, typically
blockIdx.x.
Destroy resources#
Destroy the device communicator before deregistering its application windows, then free the backing allocations. These calls are collective where their corresponding create or registration operation is collective.
NCCLCHECK(ncclDevCommDestroy(comm, &devComm));
NCCLCHECK(ncclCommWindowDeregister(comm, window));
NCCLCHECK(ncclMemFree(buffer));
Version and backend notes#
ncclDevComm is versioned. The upstream NCCL 2.30.3 and 2.30.4 release notes
require applications using GIN APIs to be rebuilt with the matching release.
The upstream NCCL 2.30.7 release notes describe explicit Strong and Weak
signals and add ncclGinFenceLevel semantics for GIN barriers (None,
Put, Get, default Put | Get). The explicit signal API is marked
available since NCCL 2.30.5. RCCL accepts compatible layouts within the 2.30
family, but applications using pre-2.30 GIN device code must be rebuilt with
compatible RCCL headers. The runtime rejects pre-2.30 requirements that request
indexed GIN resources.
NCCL_GIN_IB_TC, added in upstream NCCL 2.30.7, applies to the connections
of the IB proxy backend (NCCL_GIN_TYPE=2). The one-sided host RMA
operations run on the same backend and use the same traffic class. The
rocSHMEM GDA backend (NCCL_GIN_TYPE=6) doesn’t read NCCL_GIN_IB_TC,
NCCL_IB_TC, or ginTrafficClass; its queue pairs take their traffic
class from ROCSHMEM_GDA_TRAFFIC_CLASS. Anvil SDMA (NCCL_GIN_TYPE=7)
moves data within a node without a NIC, so traffic class doesn’t apply to it.
The 128-byte, versioned GIN proxy descriptor and per-context proxy progress are
internal implementation details and require no application configuration.
The max_rd_atomic and max_dest_rd_atomic changes and the
doca-gpunetio update in the NCCL 2.30 release apply to NVIDIA’s GDAKI
backend, not the AMD GIN host-proxy backend.
The source includes experimental proxy get and nonblocking-flush paths, but
they are not yet validated for production use on the AMD host-proxy backend.
GIN access to elastic and multi-segment windows is also unavailable on that
backend until a complete multi-segment VMM range can be exported for DMA-BUF
registration. Use the validated put/signal path with single-segment symmetric
windows.