The following experiments suggest that the uops are deallocated at some point before the load completes. While this is not a complete answer to your question, it might provide some interesting insights.
On Skylake, there is a 33-entry reservation station for loads (see https://stackoverflow.com/a/58575898/10461973). This should also be the case for the Coffee Lake i7-8700K, which is used for the following experiments.
We assume that R14
contains a valid memory address.
clflush [R14]
clflush [R14+512]
mfence
# start measuring cycles
mov RAX, [R14]
mov RAX, [R14]
...
mov RAX, [R14]
mov RBX, [R14+512]
# stop measuring cycles
mov RAX, [R14]
is unrolled 35 times. A load from memory takes at least about 280 cycles on this system. If the load uops stayed in the 33-entry reservation station until completion, the last load could only start after more than 280 cycles and would need another ~280cycles. However, the total measured time for this experiment is only about 340 cycles. This indicates that the load uops leave the RS at some time before completion.
In contrast, the following experiments shows a case where most uops are forced to stay in the reservation until the first load completes:
mov RAX, R14
mov [RAX], RAX
clflush [R14]
clflush [R14+512]
mfence
# start measuring cycles
mov RAX, [RAX]
mov RAX, [RAX]
...
mov RAX, [RAX]
mov RBX, [R14+512]
# stop measuring cycles
The first 35 loads now have dependencies on each other. The measured time for this experiment is about 600 cycles.
The experiments were performed with all but one core disabled, and with the CPU governor set to performance (cpupower frequency-set --governor performance
).
Here are the nanoBench commands I used:
./nanoBench.sh -unroll 1 -basic -asm_init "clflush [R14]; clflush [R14+512]; mfence" -asm "mov RAX, [R14]; mov RAX, [R14]; mov RAX, [R14]; mov RAX, [R14]; mov RAX, [R14]; mov RAX, [R14]; mov RAX, [R14]; mov RAX, [R14]; mov RAX, [R14]; mov RAX, [R14]; mov RAX, [R14]; mov RAX, [R14]; mov RAX, [R14]; mov RAX, [R14]; mov RAX, [R14]; mov RAX, [R14]; mov RAX, [R14]; mov RAX, [R14]; mov RAX, [R14]; mov RAX, [R14]; mov RAX, [R14]; mov RAX, [R14]; mov RAX, [R14]; mov RAX, [R14]; mov RAX, [R14]; mov RAX, [R14]; mov RAX, [R14]; mov RAX, [R14]; mov RAX, [R14]; mov RAX, [R14]; mov RAX, [R14]; mov RAX, [R14]; mov RAX, [R14]; mov RAX, [R14]; mov RAX, [R14]; mov RBX, [R14+512]"
./nanoBench.sh -unroll 1 -basic -asm_init "mov RAX, R14; mov [RAX], RAX; clflush [R14]; clflush [R14+512]; mfence" -asm "mov RAX, [RAX]; mov RAX, [RAX]; mov RAX, [RAX]; mov RAX, [RAX]; mov RAX, [RAX]; mov RAX, [RAX]; mov RAX, [RAX]; mov RAX, [RAX]; mov RAX, [RAX]; mov RAX, [RAX]; mov RAX, [RAX]; mov RAX, [RAX]; mov RAX, [RAX]; mov RAX, [RAX]; mov RAX, [RAX]; mov RAX, [RAX]; mov RAX, [RAX]; mov RAX, [RAX]; mov RAX, [RAX]; mov RAX, [RAX]; mov RAX, [RAX]; mov RAX, [RAX]; mov RAX, [RAX]; mov RAX, [RAX]; mov RAX, [RAX]; mov RAX, [RAX]; mov RAX, [RAX]; mov RAX, [RAX]; mov RAX, [RAX]; mov RAX, [RAX]; mov RAX, [RAX]; mov RAX, [RAX]; mov RAX, [RAX]; mov RAX, [RAX]; mov RAX, [RAX]; mov RBX, [R14+512]"
movl (rax), edx; leal (rdx), ecx; leal (rdx), edi; leal (rdx), esi
... On same ICL with 4 ports forlea
would all 3 of thelea
above be replayable? What if its more uops thatRAT
bandwidth? 4) If the uops are not replayed in a loop is there an idea for when they will get redispatched? Is it only if there is no contention for the port (hopefully) or can it actually add extra bottlenecks? 5) Will replay always be on the same port the instruction was dispatched too? – Noahlea
in your example) would replay, but also uops that would dispatch a cycle later due to port conflicts and dependencies would often replay, and sometimes more than that. I couldn't come up with an exact bright line "horizon" in cycles from the load result where stuff \ – BeeOnRope