Comment

Comment by jsnell

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.
parent jsnell 9 years ago

Discussion

Live from the official Hacker News API

A modern reader for Hacker News · SvelteKit + Skeleton