The same computation c = a + b over an array of length N, run two ways. A Python for-loop runs N interpreted steps, each reading boxed objects, adding, re-boxing, and storing; the step counter climbs with N. The whole-array expression a + b is one Python-level call whose loop runs in compiled C over the packed buffer, so its call count stays 1 no matter how large N gets. The speed comes from the flat single-dtype layout, not magic.

Vectorize: send the loop to C

Same answer, c = a + b, two ways. A Python loop does the work one element at a time, in the interpreter. Writing it as a whole-array expression hands the loop to compiled C over the packed buffer.

N 16
Python loop for i in range(N): c[i] = a[i] + b[i]
each step pays interpreter overhead, per element
PYTHON STEPS0 / 16
PER STEPread·read·add·box·store
NumPy c = a + b
one array op → C loop over packed memory (often SIMD)
PYTHON CALLS1
THE C LOOPruns all N, no per-element Python

relative work — Python-level operations

Python
16 steps
NumPy
1 call

// schematic, not a benchmark — counts Python-level ops, not milliseconds

This is the payoff of the packed line: one dtype + contiguous memory is exactly what lets a tight machine loop add every element with no Python object per step. Vectorizing doesn't skip the work — it moves the loop to where the work is cheap.