Asynchronous copy and double buffering
Separate architecture-specific copy mechanisms from the synchronization and capacity needed for a correct tensor pipeline.
On this page
The question for today
What you’ll understand
- Distinguish Ampere asynchronous global-to-shared copy from Hopper TMA.
- State producer, completion, consumer, and reuse dependencies.
- Calculate an ideal pipeline bound without promising measured overlap.
A matrix kernel can load the next tensor tile while calculating on the current tile. Two storage slots make that schedule possible, but they do not establish that the next tile is ready or that the old tile is safe to overwrite. This lesson scopes mechanisms to CUDA 12.8.1: Ampere SM80 asynchronous global-to-shared copying and Hopper SM90 Tensor Memory Accelerator operations have distinct contracts. Actual overlap depends on instructions, resources, synchronization, and workload.
Choose a supported movement mechanism
Ampere-class hardware can accelerate appropriate global-to-shared asynchronous copies without the ordinary intermediate per-thread register path. The underlying cp.async family supports documented sizes and alignment requirements, including four-, eight-, and sixteen-byte forms. High-level copy APIs still need suitable trivially copyable types, addresses, sizes, and target support to use their accelerated path. An asynchronous name in source code does not establish which instruction the compiler emitted or whether useful independent work overlaps its execution. Inspect the target code and capability requirements before attributing a speedup to a mechanism.
Hopper adds TMA for supported bulk and multidimensional tensor transfers, using tensor descriptions and architecture-specific layout, alignment, and synchronization rules. It is not merely a larger spelling of every Ampere copy instruction. A kernel port must account for descriptor preparation, valid tile boundaries, and the appropriate completion barrier. For an AI activation tile, preserving the logical tensor mapping matters as much as moving the bytes quickly; a fast copy of a wrongly described stride produces a fast incorrect computation.
Two buffers still require ownership transitions
A producer acquires a reusable slot, starts its copy, and publishes completion through the mechanism’s required synchronization. A consumer waits for that completion before reading the tile, performs its computation, and releases the slot only after all relevant readers finish. The producer must not overwrite the slot merely because it issued the next copy. Barrier participation and visibility requirements depend on the API and scope. A block-wide barrier placed at a convenient line is not automatically a substitute for the copy operation’s completion protocol.
Double buffering doubles the tile-storage footprint when each stage holds a full tile. That shared-memory cost can reduce resident blocks, while the asynchronous copy path can change register use. More pipeline stages may tolerate longer movement latency but consume additional resources and complicate producer-consumer coordination. Tail tiles, early exits, and divergent participation need particular care because they can break an otherwise regular steady-state schedule. Validate numerical results and synchronization before comparing timing; an occasional stale tile is a correctness failure even when average throughput improves.
Measure the schedule that actually executes
Compare a correct sequential baseline with the pipelined kernel on the same GPU, tensor shapes, and numerical precision. Record shared-memory allocation, registers, occupancy constraints, instruction selection, and completion dependencies. Small tile counts spend more time filling and draining; large counts can approach the slower stage’s rate only if resource contention does not invalidate the independent-stage assumption. A timeline or profiler can show whether movement overlaps arithmetic. Merely observing a shorter host submission interval does not demonstrate shorter device execution.
The queue lab offers only an analogy for outstanding tiles. Reset, set arrival rate to 3 and service rate to 2, predict the backlog after one second, then Step twice. Compare a fresh Reset with both arrival rate at 3 and service rate at 4 before two Steps. Its backlog starts at four and changes continuously with fixed rates. It has no copy instructions, buffer slots, barriers, occupancy, or timeline of overlapping stages, so it cannot validate a CUDA pipeline.
Work it through
Fill, overlap, and drain
Assume eight identical tiles, a three-microsecond copy stage and a five-microsecond compute stage, independent resources, and perfect two-stage overlap without overhead. Compare sequential and ideal pipelined execution.
Sequential execution costs 8 × (3 + 5) = 64 microseconds. A second buffer changes scheduling possibilities, not the amount of useful tile computation.
The ideal pipeline takes 3 + 5 + 7 × max(3,5) = 43 microseconds. The first tile fills the pipeline and the last computation drains it.
The modeled speedup is 64/43, approximately 1.49. If one tile uses S bytes of shared storage, two complete slots need 2S bytes, potentially changing the resource constraints ignored by this time model.
The ideal schedule saves 21 microseconds under explicit assumptions; actual device overlap and capacity effects require measurement.
The living notebook
Explore the model
Interactive lab · queue
An analogy for outstanding tiles
Reset, set λ = 3 and μ = 2, predict Q at one second, then Step twice: Q = 5. Compare a fresh Reset with both λ = 3 and μ = 4 before two Steps: Q = 3. This fixed-rate fluid queue starts at four; it does not implement asynchronous copies or pipeline barriers.
- 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 forA faster consumer can reduce outstanding work, but buffer ownership and device overlap require a separate implementation.
Pause & reason
Check your understanding
Your turn
Try it yourself
For four tiles with two-microsecond copies and six-microsecond computation, calculate sequential and ideal two-stage times under the same perfect-overlap assumptions. Explain why adding a second buffer alone does not guarantee the ideal value.
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 one fill-and-first-compute interval followed by three intervals at the slower stage rate.
Compare with the solution
Sequential time is 4 × 8 = 32 microseconds. Ideal time is 2 + 6 + 3 × 6 = 26 microseconds. Correct synchronization, independent resource capacity, sufficient useful work, and the extra shared-memory footprint still matter. The second slot permits a schedule but does not create a copy engine or prove a compiler used the desired asynchronous instruction.
Keep the idea
Take it with you
- Architecture-specific copy APIs require their own alignment, layout, and completion contracts.
- Consumers wait for copied data, and producers wait before reusing occupied storage.
- Ideal pipeline equations expose assumptions that measured kernels must test.
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.