1
FOR_ITER ; next value from the range
Interpreter dispatch — indirect jump through the ~200-case opcode switch, an often-mispredicted branch (~15 cycles). Then call the iterator's __next__ and box a fresh int object for i.
~25 machine instr
2
LOAD_FAST sum ; push a pointer
Dispatch again, then read a pointer out of the frame's local array and push it on the value stack. You loaded an address, not a number.
~10 machine instr
3
LOAD_FAST i ; push a pointer
Same ceremony a second time. Two dispatches so far, and not one arithmetic operation has happened yet.
~10 machine instr
4
BINARY_OP += ; the "add"
Type-check both operands (follow two type pointers, look up the numeric slot) →
unbox two heap PyObjects (dereference into the digit array — pointer-chasing, so likely
cache misses, Part 3) → do the one real add →
allocate and box a brand-new int object for the result.
~50–70 machine instr
5
STORE_FAST sum ; rebind the name
Write the new pointer into the frame, then update reference counts: incref the new object, decref the old one, and free it if it reached zero.
~12 machine instr
6
JUMP_BACKWARD ; loop again
Back to the top of the eval loop — and pay all of the above again, 100 million times over.
~6 machine instr