Unit 8 · AI infrastructure · Lesson 5 of 10
Input pipelines, host-device transfer and overlap
Step 1: Learn the ideas
- Pipeline view: storage, decode or tokenize, batch, host memory, device, compute, communication
- DataLoader workers, prefetching and persistent workers
- Collation, padding, length bucketing and sequence packing
- Pageable versus pinned memory and non-blocking copies
- CUDA streams, events and synchronization points
- Overlapping gradient all-reduce with backward and parameter all-gather with compute
- Sharded storage formats and streaming datasets
- Shuffle buffers, determinism and resumable state
- Reading timelines for exposed data and communication time
Why it matters
Accelerators do useful work only while data and communication keep up with them. When input loading, host-to-device copies or collectives are not overlapped with compute, the most expensive component waits. "Training is slower than expected" is often an input-pipeline or synchronization problem, and interviewers want to see a measured diagnosis rather than a list of tricks.
Core ideas
A pipeline view. Training is a chain: storage, decode and augment (or tokenize and pack), batch, host memory, device, compute, communication. The slowest stage sets throughput; buffers and prefetching hide variability but cannot fix a producer that is slower on average. The quickest test is to replace the dataset with synthetic tensors already on the GPU: if the step gets much faster, you are input-bound.
DataLoader mechanics. Worker processes load and transform samples in parallel, outside the main Python thread. Each worker prefetches a few batches; persistent workers avoid restarting processes every epoch. More workers help until CPU cores, host memory or storage bandwidth saturate, and each worker holds its own copy of dataset state, which can inflate host memory. For sequences, padding to the longest item in a batch wastes compute; bucketing by length or packing several sequences into one row reduces the waste.
Pinned memory and copies. The GPU's copy engines read host memory directly only when it is page-locked (pinned); a copy from ordinary pageable memory first passes through a pinned staging buffer. With non_blocking=True the host thread does not wait for the copy, but the copy overlaps with kernels on the GPU only if the source is pinned, the copy runs on a non-default stream and a copy engine is free. Pinning a tensor right before copying it adds work and can be slower than a plain copy; the benefit comes from producing batches directly in pinned memory (DataLoader pin_memory=True pins in a background thread) or reusing pinned buffers. Non-blocking device-to-host copies need an explicit synchronization before the CPU reads the result, or it may read incomplete data.
Streams and synchronization. Kernels on one stream run in order; work on different streams may overlap. Hidden synchronization points serialize the pipeline: .item(), .cpu(), printing tensor values, operations whose output shape depends on data (nonzero, boolean masking) and explicit synchronize calls. Logging the loss value every step is a classic stall.
Communication overlap. DDP launches bucketed gradient all-reduces during backward; FSDP prefetches the next layer's parameter all-gather. Overlap helps only when enough independent compute exists to cover the transfer and when communication and compute do not compete for the same resources.
What to look for in a timeline. GPU idle gaps at the start of each step (waiting for data), host-to-device copy durations, CPU time inside the DataLoader's next call, and collectives not covered by compute.
Common misconceptions
- "More DataLoader workers are always faster." Beyond the available cores, memory and I/O bandwidth, workers contend and startup cost grows.
- "non_blocking=True makes every copy asynchronous and overlapped." It frees the host thread; overlap with kernels needs pinned memory and a separate stream, and unsynchronized device-to-host results can be wrong.
- "High GPU utilization means the input pipeline is fine." Utilization counts any running kernel; compare step time with a synthetic-data baseline.
- "Overlap happens automatically." It needs independent work, separate streams and no synchronization points in between.
- "Data loading only matters for images." Tokenization, packing, decompression and remote object storage bottleneck text pipelines too.
How to approach it
Profile first, then change one thing at a time: workers, pinned memory, batch layout, synchronization points, communication overlap. In interviews, describe the synthetic-data test, the timeline signals you would look for, and the fix for each signal. Senior discussions add dataset sharding across many hosts, deterministic shuffling with resumable state, and storage throughput planning.
Suggested order
- 1Read the single- and multi-process loading and memory-pinning sections of the PyTorch torch.utils.data documentation.
- 2Read the PyTorch guide on non_blocking and pin_memory() and reproduce one of its timing comparisons.
- 3Read the asynchronous execution and CUDA streams sections of PyTorch CUDA semantics.
- 4Read the CUDA Best Practices sections on pinned memory and on overlapping transfers with computation.
- 5Do the profile-a-training-step project, confirm or rule out an input bottleneck with the synthetic-data test, and inspect the timeline in Nsight Systems if available.
Step 2: Check off what you can do
Tap each one you can genuinely do or explain. Each earns 10 XP.
Step 3: Go deeper
PyTorch: torch.utils.data (Dataset and DataLoader)Start here
PyTorch documentationSection: Single- and Multi-process Data Loading
How workers, batching, collation and memory pinning behave in the standard data-loading API.
Open(opens in a new tab)Link responded, 5 Oct 2026A guide on good usage of non_blocking and pin_memory() in PyTorchStart here
PyTorch tutorialsWhen pinned memory and non-blocking copies help, when they cost more than they save, and the device-to-host synchronization pitfall.
Open(opens in a new tab)Link responded, 5 Oct 2026PyTorch: CUDA semantics
PyTorch documentationSection: CUDA streams
Stream ordering, synchronization rules and asynchronous execution as PyTorch implements them.
Open(opens in a new tab)Link responded, 5 Oct 2026CUDA C++ Best Practices Guide
NVIDIA DocumentationSection: Asynchronous and Overlapping Transfers with Computation
The hardware view of copy-compute overlap and pinned memory.
Open(opens in a new tab)Link responded, 5 Oct 2026NVIDIA Nsight Systems User Guide
NVIDIA DocumentationSystem-wide timelines that show data waits, copies, kernels and communication together.
Open(opens in a new tab)Link responded, 5 Oct 2026
Step 4: Practice
Take a training script you know, record a baseline step time, then run with synthetic on-device data to bound the possible gain. Profile, label the timeline, and change one setting at a time (workers, pin_memory, removing per-step .item() calls, length bucketing), recording step time and GPU idle time after each change. The table of changes and effects is the evidence you will cite in an interview.
- Profile a training step and attribute its timeOpen(opens in a new tab)Not attemptedSelf-directed (PyTorch profiler)
Record attempts on each problem’s page (+15 XP, +10 more when solved on your own).
Step 5: Test yourself
Answer each one out loud or on paper before you finish the lesson.
- Question 1What does the synthetic-data test tell you, and what can it miss?
- Question 2Which conditions must hold for a host-to-device copy to overlap with a kernel?
- Question 3Name four operations that force host-device synchronization.
- Question 4How do length bucketing and sequence packing change wasted compute?
- Question 5Why can adding DataLoader workers make a job slower?
You’re done when you can
- Determine whether a training script is input-bound using a synthetic-data comparison and a profiler timeline.
- Explain when pinned memory and non-blocking copies help, when they do not, and the device-to-host synchronization pitfall.
- Remove at least one hidden synchronization point from a training loop and show its effect in a trace.
- Describe how DDP overlaps gradient communication with backward and what limits that overlap.


