Overlap work only while its data remains valid
Construct a CPU, transfer, and GPU pipeline with explicit completion events, buffer lifetimes, and measured concurrency.
On this page
The question for today
What you’ll understand
- Separate asynchronous submission from completed work and guaranteed overlap.
- Compute an ideal finite-batch pipeline time.
- Specify safe ownership transitions for reused host and device buffers.
A CPU can prepare the next input while a GPU processes an earlier one, but correctness depends on which buffers each operation still owns. Returning from an asynchronous copy call is not permission to overwrite its source immediately. Likewise, launching a kernel does not make its output ready for a CPU consumer. Build a dependency timeline first, then introduce streams, completion events, and enough buffer slots to preserve that timeline. The goal is cooperative execution with proven data lifetimes, followed by measurement of whatever overlap the actual platform supports.
Make submission and completion different states
Track preparation, host-to-device copy, device computation, device-to-host copy, and consumption as distinct stages. In CUDA 12.8.1, operations submitted to one stream follow its ordering rules; work in different streams can require explicit dependencies and is not guaranteed to execute concurrently. Record an event after the operation whose completion transfers ownership. A CPU consumer waits for the relevant completion rather than assuming that later wall-clock time or a successful enqueue implies the result is available.
Keep source and destination storage alive while operations may access it. A host input may be reused after its transfer completes if no other operation reads it; a device input may need to survive through kernel completion. Output buffers have different lifetimes because a later copy or consumer may still read them. Use separate ownership states for each allocation. A single global ready flag rarely describes a multi-stage ring accurately, especially during cancellation, error handling, or variable-sized requests.
Model overlap with resources and sufficient buffering
Pinned host memory and suitable device capabilities are important for CUDA host-transfer overlap, but an Async API name alone does not establish nonblocking behavior for every argument combination. Pageable memory can require staging or synchronization. Query supported engine capabilities and inspect the actual timeline. Independent streams create scheduling opportunities while copy engines, memory bandwidth, kernel resource use, and default-stream interactions can still serialize portions of the intended pipeline.
An ideal pipeline processes independent chunks on separate stage resources with enough buffers and no extra stalls. Its steady interval is set by the slowest stage, while the first chunk still traverses every stage. Buffer capacity must support the chosen overlap; two composite slots held until final output completion may be insufficient for a four-stage ideal. Derive lifetimes and maximum simultaneously live chunks instead of claiming that double buffering always attains the bottleneck rate.
Preserve the cooperative advantage under real load
Choose chunk size using both transfer amortization and pipeline occupancy. Very small chunks increase scheduling and transfer overhead; very large chunks reduce opportunities for overlap and can increase latency. Bound the number of in-flight chunks so CPU preparation cannot fill all pinned memory while the GPU is delayed. Backpressure should reserve the buffers and metadata needed to finish already accepted work, including output consumption and cancellation cleanup.
Measure CPU preparation, copy activity, GPU execution, and consumer stalls on a shared timeline. Compare isolated kernel time with complete request latency and loaded throughput. Synchronize only at the dependency boundary required for correctness; synchronizing the entire device after every stage can destroy intended overlap. Conversely, removing synchronization without an ownership proof creates races rather than speedup. Reproduce output checks across repeated ring wraparound and error paths because buffer reuse faults often remain hidden in a single successful chunk.
Work it through
A four-stage ideal with enough independent buffers
A fictional chunk uses CPU preparation 3 ms, host-to-device transfer 2 ms, GPU compute 5 ms, and return transfer 1 ms. Assume four independent stage resources, enough separate buffers, no contention, and no other costs. Compare four serial chunks with the ideal pipelined batch.
One chunk traverses 3+2+5+1=11 ms of stages. Four fully serial chunks therefore take 4×11=44 ms.
The slowest stage is GPU compute at 5 ms, giving the ideal pipeline interval. First-chunk latency remains 11 ms even though later chunks can overlap.
The ideal four-chunk completion time is 11+(4−1)×5=26 ms, a batch speedup of 44/26≈1.69. The average 6.5 ms per completed chunk is not the first request’s latency.
This result requires the stated resource and buffering assumptions. A real implementation must establish event dependencies and measure overlap rather than infer 26 ms from asynchronous API usage.
The living notebook
Explore the model
Interactive lab · queue
Bound in-flight preparation when service slows
Reset, set Arrival rate λ to 4 and Service rate μ to 3. Predict fluid backlog 4 initially and 5 after two Steps at t=1 s. Reset, explicitly set λ=2 and μ=3, then take two Steps: predict backlog 3. This constant-rate fluid toy illustrates pressure on an in-flight limit; it does not implement CUDA streams, buffers, copy engines, or an actual latency distribution. Its M/M/1 readout has separate Poisson/exponential/stability assumptions.
- Fluid backlog
- 4
- Net rate · λ − μ
- −1 /s
- M/M/1 mean residence
- 1 s
Fluid backlog starts at 4 and follows max(0, 4 + (λ − μ)t). The separate M/M/1 residence-time formula assumes Poisson arrivals, exponential service and λ < μ. Parameter changes pause playback and recompute the model at the current elapsed time. Reset restores both parameters and returns time to zero. Playback starts paused. Adjust either parameter, then choose Play or Step.
Look forCooperation needs bounded in-flight work as well as overlap opportunities.
Pause & reason
Check your understanding
Your turn
Try it yourself
An ideal three-stage pipeline has times 2,4,3 ms on independent resources and enough buffers. Find the completion time for five chunks and compare with serial execution. What changes if an input buffer is overwritten before its queued transfer finishes?
Take a moment to make a prediction or work it out on paper. Open a hint when you need one.
Give me a hint
Use fill time plus four bottleneck intervals, then consider correctness separately from timing.
Compare with the solution
Fill time is 9 ms and bottleneck interval is 4 ms, so five chunks take 9+4×4=25 ms ideally, versus 5×9=45 ms serially. Premature overwrite creates a data-lifetime race; a shorter observed runtime under that race is not a valid optimization or evidence that the pipeline model was achieved.
Keep the idea
Take it with you
- Treat asynchronous submission and completion as separate events.
- Derive each buffer’s lifetime and provision enough slots for the intended overlap.
- Measure actual engine concurrency and apply bounded backpressure.
Read further
Original sources and deeper explanations to follow at your own pace.
Optional progress, saved in this browser when available. Revisit any lesson at any time.
Lesson not marked complete.