advanced24 min studyLesson 473 in Engineering

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

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.

Fstage=2T3,Qstage=2sT2,Istage=TsF_{\rm stage}=2T^3,\qquad Q_{\rm stage}=2sT^2,\qquad I_{\rm stage}=\frac{T}{s}
T is a positive square tile width and s is equal bytes per staged input element. Q_stage counts one cooperative load of each A/B stage element only, excluding output and repeated traffic. I_stage is FLOP per stage-load byte, not automatically measured HBM intensity.↔ Scroll wide equations horizontally.

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.

Sstages=d (sATMTK+sBTKTN)S_{\rm stages}=d\,(s_A T_M T_K+s_B T_K T_N)
d is the positive number of simultaneously allocated staging buffers, T terms are tile dimensions, and s terms are bytes per staged element. S_stages is ideal shared-storage bytes before padding, metadata, and other allocations; it is not the complete occupancy calculation.↔ Scroll wide equations horizontally.

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.

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.

  1. One stage loads two 32×32 tiles, totaling 2×32×32×4=8,192 bytes. It contributes 2×32³=65,536 conventional FLOPs.

  2. Stage-load intensity is 65,536/8,192=8 FLOP/byte at this specifically defined boundary. This excludes output traffic and cross-block rereads.

  3. 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 result

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.

Explore the model

Interactive lab

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.

More reuse raises only a conditional boundSynthetic 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. Current readings: Attainable ceiling: 640 GFLOP/s; Ridge intensity: 12.5 FLOP/byte; Ceiling regime: Memory-bound ceiling. All parameters are adjustable with the labeled controls below.Hypothetical roofline upper bound: 640 GFLOP per second at operational intensity 8 FLOP per byte and bandwidth 80 gigabytes per second.0.1110641101001000Operational intensity · FLOP/byte · log scaleCeiling · GFLOP/s · log scalePeak: 1000
Attainable ceiling / kernelBandwidth roof / traceCompute ceiling
Attainable ceiling
640 GFLOP/s
Ridge intensity
12.5 FLOP/byte
Ceiling regime
Memory-bound ceiling
t = 0 s

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.

Check your understanding

Why can a thread with no valid output coordinate still be required in a tiled block?

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.

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.