CUDA Streams and Asynchronous GPU Execution are the programming mechanisms that enable overlapping of multiple GPU operations — kernel execution, memory transfers, and host computation — through independent queues (streams) of work, extracting maximum hardware utilization by keeping all GPU subsystems (compute, copy engines, host CPU) busy simultaneously rather than executing operations one-after-another.
The Default (Synchronous) Problem
Without streams, GPU operations execute sequentially: copy H→D, kernel launch, copy D→H, repeat. The GPU compute units are idle during transfers, and the copy engines are idle during kernel execution. For a kernel taking 5 ms and transfers taking 2 ms each, utilization is only 5/(5+2+2) = 56%.
Stream Semantics
- A stream is an in-order queue of GPU operations. Operations within the same stream execute sequentially.
- Operations in different streams may execute concurrently if hardware resources are available.
- The default stream (stream 0) synchronizes with all other streams (legacy behavior) or is per-thread independent (per-thread default stream).
Overlap Patterns
Double Buffering (Ping-Pong):
Stream A: [Copy H→D chunk 0] [Kernel chunk 0] [Copy D→H chunk 0]
Stream B: [Copy H→D chunk 1] [Kernel chunk 1] [Copy D→H chunk 1]
While the kernel processes chunk 0, the copy engine simultaneously transfers chunk 1 to the device. Total time approaches max(compute, transfer) instead of compute + transfer.
Multi-Stream Pipeline: With N streams and N data chunks, all pipeline stages run simultaneously. The steady-state throughput is limited by the slowest stage.
Hardware Requirements
- Separate Copy Engines: Modern GPUs have 2-3 independent copy engines (H→D, D→H, D→D / P2P). Bidirectional transfers overlap with each other AND with kernel execution.
- Concurrent Kernels: GPUs can execute multiple small kernels simultaneously on different SMs when a single kernel doesn't saturate all SMs. Up to 16-128 concurrent kernels depending on architecture.
- Hyper-Q (MPS): Multiple CPU processes or threads can submit to independent hardware queues, enabling fine-grained concurrent kernel execution without false dependencies.
Synchronization Primitives
- cudaStreamSynchronize(stream): Host blocks until all operations in the stream complete.
- cudaEventRecord / cudaStreamWaitEvent: Fine-grained inter-stream dependencies. Stream B can wait for a specific event recorded in Stream A without synchronizing with the host.
- cudaEventElapsedTime: Measure GPU-side elapsed time between two events for accurate kernel timing.
Common Pitfalls
- Implicit Synchronization: Some CUDA API calls (cudaMalloc, cudaMemcpy without Async) implicitly synchronize the device, serializing all streams. Use Async variants exclusively in multi-stream code.
- Page-Locked Memory: Async transfers require pinned (page-locked) host memory. Pageable memory forces synchronous copies.
CUDA Streams are the concurrency abstraction that converts GPU programming from sequential batch processing into pipelined, overlapped execution — extracting the last 20-40% of hardware utilization that separates a well-optimized GPU application from a naive one.
Related Topics
Explore 500+ Semiconductor & AI Topics
From EUV lithography to CUDA optimization — search the full knowledge base or chat with our AI assistant.