Working on the most recently retired instruction would be odd, since it'd lead to things like the cost of cache misses being assigned to instructions that have nothing to do with memory access.
But other than that, I've never been able to build a mental model for exactly how the accounting works. For example take an inner loop with no memory accesses, like this:
18: add $0x1,%rax
cmp %rax,%rdx
jne 18
On a Skylake this will execute once per cycle (IPC of ~3), so we're clearly spending equal amounts of time on each instruction. But perf will assign basically all of the cost to the jump.
0.28 │18:┌─→add $0x1,%rax
0.27 │ │ cmp %rax,%rdx
99.45 │ └──jne 18
So it could be consistent with some kind of "first in-flight" or "last in-flight" model. But change the loop to be 5 instructions long (and with an IPC of ~5, so still executing the whole loop once per cycle), and suddenly the PC gets smeared across two instructions:
2.12 │20:┌─→add %rsi,%rax
0.94 │ │ add $0x1,%rcx
45.36 │ │ add %rax,%rdx
0.10 │ │ cmp %rcx,%rsi
51.48 │ └──jne 20
Take an almost equivalent loop, just swapping two instructions. It's still 5 instructions and an IPC of ~5. And...
80.79 │20: add $0x1,%rcx
1.29 │ add %rax,%rdx
0.89 │ add %rsi,%rax
0.00 │ cmp %rcx,%rsi
17.02 │ ↑ jne 20
Multiple runs of an individual test case will be pretty consistent on a specific machine; so it's not that the assignment is just random. It's deterministic, but the exact mechanism is very opaque.
But other than that, I've never been able to build a mental model for exactly how the accounting works. For example take an inner loop with no memory accesses, like this:
On a Skylake this will execute once per cycle (IPC of ~3), so we're clearly spending equal amounts of time on each instruction. But perf will assign basically all of the cost to the jump. So it could be consistent with some kind of "first in-flight" or "last in-flight" model. But change the loop to be 5 instructions long (and with an IPC of ~5, so still executing the whole loop once per cycle), and suddenly the PC gets smeared across two instructions: Take an almost equivalent loop, just swapping two instructions. It's still 5 instructions and an IPC of ~5. And... Multiple runs of an individual test case will be pretty consistent on a specific machine; so it's not that the assignment is just random. It's deterministic, but the exact mechanism is very opaque.