← learn

networking · storage · multi-node

GPUDirect: getting data to the GPU without the CPU

Training moves data from storage to GPUs, and between GPUs on different servers. Normally every byte detours through the CPU and host RAM. GPUDirect RDMA and GPUDirect Storage remove the detour. What that costs, what it needs, and at what scale it starts to matter.

Before you start

This article assumes you know:

Ideas it leans on:

PCIe
· the bus inside a server that connects the CPU, GPUs, network cards and SSDs.
DMA
· direct memory access: a device copying data to a memory address by itself, without the CPU.
RDMA
· remote direct memory access: a network card writing straight into another machine's memory.
All-reduce
· every GPU averaging its gradients with every other GPU, once per training step.

The use case: one training step

A training step moves data along three paths. Only one of them is the GPU doing maths:

  1. Storage → GPU. The next batch of training data comes from storage (a bucket like GCS, a parallel file system like Lustre, or a local SSD). Every so often, a checkpoint goes the other way, and after a crash the whole checkpoint is read back.
  2. The step itself. Forward and backward passes, inside GPU memory (HBM, high-bandwidth memory stacked beside the GPU chip).
  3. GPU ↔ GPU. With data parallelism, every GPU runs the same model on its own slice of the batch and works out its gradients (how each weight should change). Before the weights are updated, all GPUs average their gradients: an all-reduce. Inside one server that runs over NVLink, NVIDIA’s direct GPU-to-GPU link. Across servers it goes through network cards (NICs).

GPU memory is fast: an H100 reads its HBM3 at 3.35 TB/s and a B200 at 8 TB/s (Google Cloud’s machine table). Getting data into it is the hard part. GPUDirect is NVIDIA’s family of technologies that let other devices read and write GPU memory directly. Two matter for training: GPUDirect RDMA (NIC ↔ GPU memory) and GPUDirect Storage (storage ↔ GPU memory).

Where this lab stands

Our GPT-2 run on 8× H100 was one server with 8 GPUs, so its gradients never left NVLink and none of this was needed. Frontier models train on thousands to tens of thousands of GPUs (Meta trained Llama 3 405B on 16,384 H100s). This article is about what changes once training spills past one server.

These paths are steps 3 and 5–7 of the ten in AI training at scale, which puts them in the context of a whole run.

The problem: the long way round

Inside a server, the GPU, the NIC and the SSD all hang off PCIe, the server’s internal bus. A device moves data by DMA (direct memory access): it copies bytes to an address it’s given, without the CPU. For decades that address was in host RAM, the CPU’s memory, because that is where the operating system and the application live. GPU memory sits on the far side of another PCIe link. So data from the network or a disk takes the long way: into host RAM, through the CPU’s root complex (where its PCIe lanes start), and only then across PCIe again into the GPU.

Watch one batch arrive from storage. Each stop is something the batch has to wait for:

Storage → GPU
GCS · LUSTRE OVER TCPGPU SERVERDRIVESSTORAGESERVERNICNVMe SSDLOCALCPU+ PCIe ROOTNICRDMAHOST RAMDRAMPCIe SWITCHGPUHBMNETWORK · IB / RoCE
TCP, the usual way: follow the dot.
copies of the data
0
CPU work on the data
0
PCIe hops
0
through host RAM
no

One storage server and one GPU server shown. In practice every GPU server does this at once, and a parallel file system stripes each read across dozens of storage servers.

The usual path costs four things. On one server they are a nuisance. Once training spans many servers, every NIC pays them at the same time:

Two layers: RDMA fixes the network, GPUDirect fixes the host

The two fixes are separate, and you need both for the straight path (switch scenes in the figure above). In both, the CPU still sets up every transfer (the control path). What changes is that it no longer touches the data.

Layer 1 · RDMA: no kernel network stack

RDMA (remote direct memory access) is DMA across the network: a NIC reads and writes memory on another machine without that machine’s CPU. The application registers a buffer once, then hands work straight to the NIC: no system call, no TCP stack, no socket copy. For one-sided reads and writes the remote CPU isn’t even interrupted. RDMA runs over InfiniBand (a network built for it), over ordinary Ethernet as RoCE v2 (RDMA over Converged Ethernet), or over iWARP (RDMA carried inside TCP). Clouds add their own transports (below); the NVIDIA GPU machines in this article use ConnectX NICs.

For storage, this is the difference between a Lustre client talking to its servers over TCP and over RDMA. RDMA removes the kernel socket copy and most of the CPU work. But plain RDMA still targets host RAM, so the data lands in a bounce buffer there and is copied again into the GPU.

Layer 2 · GPUDirect: no bounce through host RAM

GPUDirect lets the NIC’s DMA target GPU memory instead. The GPU driver tells the NIC driver where a GPU buffer lives, and the NIC writes into it peer to peer across PCIe (the driver pieces are under what you need). GPUDirect RDMA has done this since NVIDIA’s Kepler GPUs and CUDA 5.0 (CUDA is NVIDIA’s GPU programming toolkit).

GPUDirect Storage applies the same idea to files: you call cuFileRead with a GPU buffer, and a supported file system (NVIDIA names DDN EXAScaler, VAST NFS, WekaFS and Lustre clients among them) places the bytes there. If the path isn’t supported, cuFile falls back to a compatibility mode that stages through host RAM: the same code still runs, just without the benefit.

Local NVMe: the same trick inside one server

Local SSDs sit on the same PCIe fabric as the GPU, so the detour is pure waste: data leaves the drive, crosses into host RAM (the operating system’s page cache), gets copied by the CPU, and crosses back. With GPUDirect Storage the drive DMAs straight into GPU memory through the shared PCIe switch. Files should be opened with O_DIRECT, which tells the operating system not to cache them in RAM. Since CUDA 12.2, cuFile also takes the direct path without it, as long as buffers, sizes and offsets are aligned.

Local NVMe → GPU
GPU SERVERNVMe SSDLOCALCPU+ PCIe ROOTNICRDMAHOST RAMDRAMPCIe SWITCHGPUHBM
The usual way: follow the dot.
copies of the data
0
CPU work on the data
0
PCIe hops
0
through host RAM
no

GPU to GPU across servers

The all-reduce has the same problem, at both ends. Over TCP sockets, gradients are copied out of the GPU, through the operating system on both machines, and back into the other GPU. RDMA removes the operating system and the remote CPU. GPUDirect RDMA removes host RAM: the NIC reads straight out of one GPU’s memory and the far NIC writes straight into another’s. NCCL, the library PyTorch uses for all-reduce, does this by default whenever the topology allows it. Inference servers use the same path to hand a KV cache from one server to another (prefill–decode disaggregation).

GPU → GPU, across servers
SERVER ASERVER BHOST RAMDRAMCPU+ PCIe ROOTGPUHBMNVMe SSDLOCALPCIe SWITCHNICRDMANVMe SSDLOCALCPU+ PCIe ROOTNICRDMAHOST RAMDRAMPCIe SWITCHGPUHBMNETWORK · IB / RoCE
TCP sockets: follow the dot.
copies of the data
0
CPU work on the data
0
PCIe hops
0
through host RAM
no

One pair of servers and one GPU each shown. In a real all-reduce every GPU in every server does this at the same time, through its own NIC.

At what scale does it matter?

Not inside one NVLink domain: the 8 GPUs of a DGX, or the 72 of a GB200 or Rubin NVL72 rack, share NVLink, and the NICs sit idle during an all-reduce. Once training spans two servers (or racks), every step’s gradients cross the network, and the host RAM detour stops being a few extra copies. It starts capping how fast every NIC can run. The arithmetic for a DGX H100, whose user guide lists eight 400 Gb/s ConnectX-7 NICs for the cluster network and two Xeon 8480C CPUs:

Change the cluster and see where the line falls:

Servers

NIC per GPU

Training data and checkpoints

Lustre size (throughput as Managed Lustre's 1,000 MBps-per-TiB tier)

Model

GPU (H100)NICGPU → NIC directly (GPUDirect RDMA)LustreRDMA · GDS8 RAIL SWITCHES · ConnectX-7 · 400 Gb/sserver 1CPU · RAMNVLINKserver 2CPU · RAMNVLINKserver 3CPU · RAMNVLINK+5 moreCPU · RAMNVLINK

Does GPUDirect matter? It matters. With 8 servers, every step pushes 31 GB through each NIC. GPUDirect RDMA does that in 613 ms; staged through host RAM it would take at least 1.7 s.

Host RAM, if the network is staged through it

needed for 8 NICs at line rate: 1,600 GB/s
2× Xeon 8480C peak: 563 GB/s

8 NICs × 50 GB/s × 2 directions × 2 memory passes (the NIC writes each byte, the GPU copy reads it). At best each NIC gets 17.6 GB/s, 35% of its line rate, before the CPU copies anything. Memory: DDR5-4400, the rated speed with 2 DIMMs per channel as in the DGX H100.

Gradient exchange per training step

GPUDirect RDMA: 613 ms
staged through host RAM: ≥ 1.7 s

140 GB of bf16 gradients. Reduce-scatter on NVLink, then each GPU’s 1/8 shard crosses servers through its own NIC (a ring sends 2·(n−1)/n of it), then all-gather on NVLink. Ignores latency and overlap with the backward pass. Large models don’t fit in 8 GPUs with plain data parallelism and are sharded (FSDP, ZeRO); the bytes each NIC carries stay about the same. Real jobs at thousands of GPUs combine tensor, pipeline and data parallelism (Llama 3 405B trained on 16,384 H100s), so read this as the data-parallel share of the traffic.

Restoring a checkpoint from Lustre

9.8 s

980 GB (14 bytes per parameter: bf16 weights, fp32 master weights, two Adam moments), read at best at 100 GB/s, what this file system can serve. With RDMA and GPUDirect Storage the bytes land in GPU memory directly. (Google's Managed Lustre docs are used only for the throughput per TiB; they don't describe GPUDirect Storage.)

Where it starts to matter

NVLink only300 ms1.0 s8321285124096

Communication time per step for 70B, from 8 to 4,096 GPUs. Solid: GPUDirect RDMA; dashed: staged through host RAM. It climbs to a plateau: past a few servers each NIC carries about twice its shard however many GPUs you add, so NIC speed sets the cost, and staging multiplies it at every size.

Sources: NVIDIA DGX H100 user guide · NVIDIA H100 · Sapphire Rapids: 8 DDR5 channels per CPU · Xeon 8480: DDR5-4400 at 2 DIMMs per channel · NVIDIA ConnectX adapters · Managed Lustre performance tiers (1,000 MBps per TiB) · Google Cloud Managed Lustre: up to 10 TBps · NVIDIA GPUDirect RDMA documentation · NVIDIA GPUDirect Storage overview. Memory bandwidth is the CPUs’ theoretical peak (channels × transfer rate × 8 bytes); real systems reach less.

When it matters, and when it doesn’t

Many GPUs: real systems

Large clusters are built so that GPUDirect always has a short path. Each GPU gets its own NIC behind the same PCIe switch, and GPU i of every server is cabled to the same leaf switch (the first layer of network switches), its rail. Traffic between GPUs in one server stays on NVLink; traffic between servers goes rail by rail, all eight at once, and a layer of spine switches above the leaves joins groups of servers into clusters of thousands of GPUs.

rail 1rail 2rail 3rail 4rail 5rail 6rail 7rail 8leaf switchesserver 18 GPUs · 8 NICsGPU0GPU1GPU2GPU3GPU4GPU5GPU6GPU7server 28 GPUs · 8 NICsGPU0GPU1GPU2GPU3GPU4GPU5GPU6GPU7server 38 GPUs · 8 NICsGPU0GPU1GPU2GPU3GPU4GPU5GPU6GPU7server 48 GPUs · 8 NICsGPU0GPU1GPU2GPU3GPU4GPU5GPU6GPU7NVLink inside each server · GPU → NIC through the shared PCIe switch · NIC i → rail i
Each GPU has its own NIC behind the same PCIe switch, so GPUDirect RDMA moves its data without touching the CPU. GPU i of every server is cabled to rail i; all eight rails carry traffic at the same time, and traffic between GPUs inside one server stays on NVLink. A DGX H100 or DGX B200 has exactly this shape: eight GPUs and eight cluster NICs. Shown: 4 servers. A real cluster groups a few dozen servers (hundreds of GPUs) under each set of rail switches, and a further layer of spine switches joins those groups into clusters of thousands of GPUs.
SystemGPUs on one NVLinkNetwork to other serversHBM per GPU
DGX H1008, 900 GB/s per GPU8× ConnectX-7, 400 Gb/s (guide), plus 2 dual-port ConnectX-7 for storage80 GB HBM3, 3.35 TB/s
DGX B2008, 1.8 TB/s per GPU8× ConnectX-7, 400 Gb/s (guide), plus 2 BlueField-3 DPUs (NICs with their own processors) for storage180 GB HBM3e, 8 TB/s
GB200 NVL7272 in one rackOn Google Cloud (A4X): 4 ConnectX-7 per 4-GPU machine, one per GPU, RDMA over RoCE (GCP)186 GB HBM3e, 8 TB/s
Vera Rubin NVL7272 in one rackConnectX-9 SuperNICs (NVIDIA’s name for its AI-cluster NICs; NVIDIA)288 GB HBM4, 19.2 TB/s

Two trends meet here. Rack-scale NVLink (72 GPUs) keeps more of the all-reduce off the network, but the traffic that remains is larger and HBM keeps getting faster: 3.35 TB/s on H100, 8 TB/s on Blackwell, 19.2 TB/s on Rubin. Host RAM has not kept that pace, so staging through it falls further behind every generation. On Google Cloud you meet both variants (Google’s network bandwidth table): A3 High, A3 Edge and A3 Mega (H100) move GPU data with GPUDirect-TCPX and TCPXO, a GPUDirect path over TCP; A3 Ultra (H200) and A4 (B200) have eight 400 Gb/s ConnectX-7 NICs with GPUDirect RDMA over RoCE, as in a DGX; A4X has four ConnectX-7 and A4X Max four 800 Gb/s ConnectX-8, one per GPU.

Beyond NVIDIA and Google

The same problem, a NIC that has to reach accelerator memory without a detour, is solved differently across the market:

WhoHow accelerator memory reaches the network
AWSEFA, using AWS’s own SRD transport, does direct transfers between the NIC and GPU memory (GPUDirect RDMA) on P5-class and newer GPU instances, and the equivalent for Trainium. FSx for Lustre supports EFA and GPUDirect Storage, up to 1,200 Gb/s per P5 client.
Microsoft AzureND H100 v5: a dedicated 400 Gb/s InfiniBand link per GPU (3.2 Tb/s per VM), with GPUDirect RDMA.
AMDRCCL, AMD’s counterpart to NCCL, runs collectives between Instinct GPUs over InfiniBand and RoCE across servers, and over xGMI and PCIe inside one.
Intel Gaudi 3Puts the network on the accelerator itself: 24 × 200 Gb/s Ethernet ports with RoCE on chip, so there is no PCIe hop to a separate NIC.
Google TPUChips in a slice talk over their own inter-chip interconnect (ICI); only traffic between slices uses the data-center network (TPU architecture).

Clouds also build their own transports. Besides AWS’s SRD, Google designed Falcon, a hardware transport that carries RDMA and NVMe; it first shipped in Intel’s IPU E2000, and Google said it would contribute the spec to the Open Compute Project.

Which storage can do this?

StorageHow bytes reach the GPUGPUDirect?Limits
Object storage
GCS, S3
HTTPS over TCP → CPU → host RAM → GPUNo standard path: HTTP and TLS need the CPULatency per request, so throughput comes from many parallel reads. Caching helps: GCS Rapid Cache starts at 100 Gb/s and scales to 20 Tb/s per project and zone (quotas). The durable home of datasets and checkpoints.
Parallel and high-performance file systems
Parallel: Lustre (incl. DDN EXAScaler), WekaFS, IBM Storage Scale; AWS FSx for Lustre. Over NFS: VAST (disaggregated shared-everything) and scale-out NAS such as NetApp ONTAP (compared here)
RDMA (InfiniBand or RoCE) to the client, striped across many serversYes, GPUDirect Storage, on supported clients (NVIDIA)Provisioned capacity and cost. Google lists Managed Lustre at up to 10 TB/s; its docs don’t describe GPUDirect Storage for it. AWS documents GPUDirect Storage for FSx for Lustre on P5 instances.
Local NVMe SSDPCIe in the same serverYes, GPUDirect StorageOne server’s worth (a DGX H100 has 8 × 3.84 TB for cache), not shared, must be filled first. In the cloud it’s scratch: Google warns the data may be lost if the instance stops (Local SSD docs).
NVRAM / persistent memory
Intel Optane PMem
Sits on the CPU’s memory bus, like DRAMNot applicable: to the GPU it is host memory, so it’s still the detourSlower than DRAM, tied to one CPU socket, and discontinued by Intel.
CXL memory
CXL expanders
More host memory, attached over PCIe-class linksSame as host RAMAdds capacity and some bandwidth to the host; doesn’t remove the bounce.
KV cache transfer engines
Mooncake
RDMA between GPU memory, host RAM and SSD across serversYes: its Transfer Engine moves KV caches over RDMA and can target accelerator memoryFor inference, not training data: Mooncake serves Kimi and moves KV caches between prefill and decode servers (why they move); vLLM, SGLang and TensorRT-LLM integrate it (Mooncake explained).

GCS vs Lustre, in one line

Keep the durable copy in GCS; train from a parallel file system (or a local SSD cache) when the read rate or checkpoint size makes the CPU detour the bottleneck. Try both in the configurator.

What you need

Topology: good and bad
SERVER ASERVER BHOST RAMDRAMCPU+ PCIe ROOTGPUHBMNVMe SSDLOCALPCIe SWITCHNICRDMANVMe SSDLOCALCPU+ PCIe ROOTNICRDMAHOST RAMDRAMPCIe SWITCHGPUHBMNETWORK · IB / RoCE
GPUDirect RDMA: follow the dot.
copies of the data
0
CPU work on the data
0
PCIe hops
0
through host RAM
no

Ask about this article

Something unclear, or something we got wrong? Use the Ask button at the bottom right; it stays with you as you read. Answers come from this article, sometimes with a small chart, and each shows what it cost. To flag a passage, select it.

Sources