Shared-memory reuse: stage a tile and prove who may overwrite it
Calculate block-level operand reuse while respecting synchronization, staging capacity, and the difference between logical loads and device-memory traffic.
On this page
The question for today
What you’ll understand
- Derive operand-stage intensity for a square shared tile.
- Explain load, consume, and overwrite ordering among cooperating threads.
- Budget staged bytes without assuming maximum occupancy or perfect overlap.
Several output dot products need the same input values. Shared memory can turn repeated requests into a cooperative load followed by local reuse. The optimization is useful only if every consumer sees initialized data and no producer overwrites it too early. Treat the shared tile as a resource with a precise lifetime and access protocol, not merely a faster place to put an array.
One loaded operand can serve many outputs
For a square output tile of width T and a reduction chunk of width T, the block needs one T-by-T tile of A and one of B. The resulting T cubed multiply-add contributions reuse each A value across output columns and each B value across output rows. Cooperative loads assign parts of those inputs to threads, which then share the staged values. This counts mathematical reuse inside one block. It does not imply each input is fetched from device memory exactly once across all blocks or that every local access is equally cheap.
A simple stage-load intensity counts only the bytes bringing A and B into the staging region and the arithmetic using that stage. It deliberately excludes output stores and any old-C reads, so it must not be compared directly with a whole-operation tensor-byte count without adjustment. Data type conversion, padding, alignment, and implementation metadata can increase staging storage. Neighboring blocks can still load overlapping operands, with caches potentially satisfying some traffic. Name the load boundary and counting assumptions every time the ratio is used.
Synchronization protects producer and consumer phases
In a straightforward CUDA block algorithm, cooperating threads finish loading shared input before dependent threads consume it. Before the same storage is reused for the next K chunk, all relevant consumers must have finished reading the old tile. A barrier can establish these phase relationships when every required thread follows the protocol. An out-of-range output thread may still be needed to perform a cooperative load or participate in a barrier, so an early return based solely on its output coordinate can break correctness.
Asynchronous copies and double buffering change the schedule rather than eliminating the need for ordering. A copy must be issued with the supported alignment and size conditions, and consumers must wait for the corresponding stage to become available. Before producers reuse a buffer slot, the consumers of its prior generation must be done. CUDA 12.8 documents architecture-dependent asynchronous mechanisms; do not assume identical support or synchronization semantics on every GPU. Draw the stage-generation timeline and match each wait or barrier to the data it protects.
Reuse can consume the resources needed to hide latency
Increasing tile size or stage count can improve reuse or overlap while consuming more shared memory and registers per block. That can reduce resident blocks, limit independent warps, or prevent a launch if resource limits are exceeded. Occupancy itself is a constraint and diagnostic, not a universal performance score: a lower-occupancy kernel can still be faster if it reduces traffic or uses instructions more effectively. Measure the complete kernel with architecture-specific counters and a correctness check after each schedule change.
The roofline lab illustrates only the potential benefit of higher intensity. Reset, set I=4 and B=100, predict ceiling 400 GFLOP/s, then Play or Step. Separately Reset, set I=8 and B=100, predict 800 GFLOP/s, then Play or Step. Its fixed compute ceiling is 1,000 GFLOP/s, and the controls do not allocate shared memory or tune stages. Use the doubled synthetic bound to motivate a reuse hypothesis, then explain why extra synchronization, resource pressure, or actual traffic can prevent that gain in a real kernel.
Work it through
A thirty-two-wide FP32 input stage
A teaching block uses square tiles of width 32 with four-byte staged A and B elements. It holds two complete input stages simultaneously. Exclude padding and output storage.
One stage loads two 32×32 tiles, totaling 2×32×32×4=8,192 bytes. It contributes 2×32³=65,536 conventional FLOPs.
Stage-load intensity is 65,536/8,192=8 FLOP/byte at this specifically defined boundary. This excludes output traffic and cross-block rereads.
Two allocated stages consume 16,384 bytes, or 16 KiB, before other shared allocations. Whether that schedule fits and improves throughput depends on the device, register use, waits, and actual memory behavior.
The stage has eight FLOPs per counted input-load byte and two-stage storage is 16 KiB. Neither number establishes perfect overlap or kernel occupancy.
The living notebook
Explore the model
Interactive lab · roofline
More reuse raises only a conditional bound
Reset, set I=4 and B=100, predict 400 GFLOP/s, then Play or Step. Separately Reset, set I=8 and B=100, predict 800 GFLOP/s, then Play or Step. The fixed synthetic 1,000-GFLOP/s model does not allocate tiles, synchronize threads, or model occupancy.
- Attainable ceiling
- 640 GFLOP/s
- Ridge intensity
- 12.5 FLOP/byte
- Ceiling regime
- Memory-bound ceiling
Synthetic roofline ceiling min(1000, B·I), in GFLOP/s, with B in decimal GB/s and I in FLOP/byte. Playback traces the roof without changing the selected kernel. This upper-bound model is not a measured throughput prediction, hardware comparison or price model. 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 reuse improvement must survive synchronization and resource costs before becoming measured speedup.
Pause & reason
Check your understanding
Your turn
Try it yourself
A rectangular input stage has T_M=16,T_N=32,T_K=8 with two-byte A and B elements. Find one-stage bytes and three-stage bytes, excluding padding.
Take a moment to make a prediction or work it out on paper. Open a hint when you need one.
Give me a hint
Count the A and B rectangles separately before multiplying by the number of allocated stages.
Compare with the solution
A occupies 16×8×2=256 bytes and B occupies 8×32×2=512 bytes, for 768 bytes per stage. Three stages require 2,304 bytes before padding and other storage. This storage count alone does not determine resident block count because registers, threads, and hardware allocation granularity also matter.
Keep the idea
Take it with you
- Block-local reuse and external device-memory traffic are different boundaries.
- Consumers must see completed loads and finish before a stage is overwritten.
- Extra staging trades potential overlap for shared-memory and register capacity.
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.