Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.Sign up
Tracing JITs and modern CPUs: Part 2 #6
Here is a simple exercise to connect the theory and practice of tracing JITs and modern Intel microarchitectures. I write a small example program, see how LuaJIT compiles it to a trace, and then see how a Haswell CPU executes it. This follows on from #5 and #3 respectively.
The program is trivially simple: it uses the Snabb Switch
Here is the code:
local counter = require("core.counter") local n = 1e9 local c = counter.open("test") for i = 1,n do counter.add(c, 1) end
I run this using snsh (snabb shell, a LuaJIT frontend) with JIT trace dumping enabled:
There we see that LuaJIT has compiled the loop body down to five instructions:
This seems pretty nice actually: according to the semantics of Lua the call to
So that is what the tracing JIT does!
Now what does the Haswell CPU do with this?
First the theory: we can refer to the excellent AnandTech article to see how each Haswell CPU core works:
The CPU takes in a large number of x86 instructions, JITs them all into internal Haswell micro-instructions, figures out their interdependencies, and schedules them for parallel execution across eight independent execution units. (This is a sophisticated piece of technology.)
To connect this with practice we will use the
I test with a Xeon E5-2620 v3 and this command:
So what does this mean?
This is the level of visibility that I want to have into the programs I am working on. I am quite satisfied with this example. Now what I want to do is make it easy for Snabb Switch hackers to get this level of visibility into the practical code that they are working on.
Fun fact: the Mill processor is capable of executing over 30 instructions per cycle, each cycle, in general purpose workloads. Sure, it's not a shipping product just yet, but an interesting architecture for the future indeed.