Skip to content
Tech News
← Back to articles

What happens when a GPU writes memory

read original get Programming Massively Parallel Processors (Hwu, Kirk, Hajj) → more articles
Why This Matters

This is a deep technical walkthrough of how a store instruction (STG.E) travels through an RTX 4090's memory hierarchy \u2014 register file reads, the load/store unit, coalescer, L1, TLB, crossbar, L2 slice and eventually DRAM on eviction. It matters because much of this behavior isn't publicly documented, so the author fills gaps with original microbenchmarks, giving GPU kernel developers a clearer mental model of write costs and throughput limits.

Key Takeaways
Worth a Look

Programming Massively Parallel Processors (Hwu, Kirk, Hajj) — If tracing an STG.E through L1, the crossbar, L2 slices and DRAM sounds like fun, this book is the canonical deep dive into CUDA's memory hierarchy and how kernels like a vector add actually map onto GPU hardware. It pairs perfectly with the article's instruction-level walkthrough, giving you the model behind coalescing, caches, and write behavior.

See Programming Massively Parallel Processors (Hwu, Kirk, Hajj) on Amazon → Affiliate link — we may earn a commission on purchases, at no extra cost to you. Product picked by AI based on this article; it is not a tested recommendation.

In our previous post we followed one LDG.E down through all the hardware units on an RTX 4090 — through its L1, translation, through the crossbar to its L2 slice, and thence to DRAM. The request retrieved its result, and then came back up through its waystations, returning its result to its warp, which then continued to execute its part in the kernel.

The part it was playing was in a kernel that performed a vector add. The same kernel, once it has loaded elements of both vectors, adds them together, and then stores the result.

/*00c0*/ IMAD.WIDE R6 , R6 , R7 , c [0x0][0x170] ; // &c[i] /*00d0*/ FADD R9 , R4 , R3 ; // a[i] + b[i] /*00e0*/ STG.E [R6.64] , R9 ; // c[i] = ... /*00f0*/ EXIT ;

STG.E is the instruction that’s responsible for writing the calculated sum back to global memory. In this post we’re going to follow STG.E through the same waystations, figuring out what happens at each step. As before, this information is not all publicly available; where it isn’t, we’ll run new experiments.

Setting the scene: the LDG.E has returned to the warp, the FADD has added together the contents of R4 and R3 into R9 , and now, the contents of that register must be stored. The warp has become eligible within its subpartition, and its lanes start to execute STG.E .

warp 6 cyc coalescer L1 copy kept TLB crossbar L2 ack 140 ns controller DRAM on eviction SM die board

STG.E [R6.64], R9 is a global store of the 32 bits in register R9 to the 64-bit address in R6 and R7 . Where in LDG.E , we read two rows of the register file, in STG.E , we must read three: the two making up the address to which we’re going to store the data, and the data itself.

The instruction then issues to the load/store unit. The LSU sends on the opcode (‘store to these addresses’), the 32-bit mask of active lanes, and the 32 computed addresses.

How often can the SM issue stores One warp can push a new STG.E instruction through register/LSU/coalescer/L1 about every 6.1 cycles (2.3 ns at 2.6 GHz), regardless of how many lanes it issues for. The exit from the SM can sustain 32 bytes stored (or loaded) per cycle, so if all the warps are issuing, they’ll bottleneck here1.

The next stop is the coalescer. Its job is to take 32 four-byte accesses and turn them into the smallest achievable number of 32-byte sectors. Our kernel writes 128 contiguous bytes, so that’s four sectors, or one line2.

... continue reading