In this lesson, you will learn how the CPU ensures memory consistency while aggressively speeding up execution by allowing loads to bypass stores.
The Memory Bottleneck
In an out-of-order execution pipeline, we want to run as many instructions as possible in parallel. However, memory operations create a unique problem. If a LOAD instruction appears after a STORE in the program order, can the LOAD execute before the STORE finishes?
If the two instructions refer to the same memory address, the LOAD must wait to receive the updated data from the STORE. If they refer to different addresses, they are independent, and waiting is a waste of precious clock cycles. Memory Disambiguation is the process of determining whether two memory accesses conflict.
The Load-Store Queue (LSQ)
To manage this, the CPU uses a structure called the Load-Store Queue. This acts as a specialized buffer that tracks all pending memory operations:
- Store Queue (SQ): Stores keep their calculated addresses and data here until they are ready to "commit" (write to cache).
- Load Queue (LQ): Loads track their addresses and check against the SQ.
When a LOAD is ready to execute, it queries the STORE queue. If the LOAD address does not match any address currently in the STORE queue, the hardware determines there is no dependency, and the LOAD is allowed to bypass the store and proceed to memory immediately.
An Illustrative Example
Consider this code sequence:
STORE R1, [R2] ; Store value in R1 to address in R2
LOAD R3, [R4] ; Load value from address in R4 into R3
If the CPU computes that [R2] is address 0x1000 and [R4] is 0x2000, the LSQ identifies that these addresses do not overlap. The LOAD can execute while the STORE is still waiting for its data to be calculated or for the memory bus to become available. If the LOAD had to wait for the STORE to finish simply because it appeared later in the code, we would lose the performance benefits of our out-of-order execution pipeline.
Key Takeaway: The Load-Store Queue enables performance gains by dynamically checking for memory dependencies, allowing independent loads to bypass pending stores rather than waiting for them to complete.