Compilation, JIT and type specialization
▶ Script: 1_compilation_and_types.jl · Notebook: Interpreted vs compiled · 🐍 Python: 1_compilation_and_types_python.ipynb
⬅ Previous: 0 — Julia for Python users · Next: 2 — Multiple dispatch vs OOP ➡


numba @njit, numpy, Julia mysum, Julia sumWhat happens on the very first call?






| variant | 1st-call spike | plateau |
|---|---|---|
| pure Python | 0 | ~50 ms |
numba @njit |
~270 ms | ~0.4 ms |
| numpy | ≈ 0 | ~0.2 ms |
Julia mysum |
~7 ms | ~0.4 ms |
Julia sum |
~20 ms | ~0.1 ms |
Spike then fast (numba, Julia) · no spike (Python slow, numpy fast). Why?
numpy): compiled before → fast, no warmupThe spike is the JIT compiling. Paid once.
numpy.sum: the loop is compiled C.
for over a numpy array: slow againDiscard the 1st call; measure the steady state.
▶ Script: cell “0. Getting started”, then “2. Measuring properly — compare the two timings”
$v plugs the value in: time mysum, not the global lookup (why: with dispatch)$ before globals in @btime▶ Script: cell “Read the distribution: why is the minimum the number to quote?”
x + y, several machine codesNo single “x + y” instruction: it depends on the types.
x, y |
machine code for x + y |
|---|---|
Int, Int |
add (integer) |
Float64, Float64 |
vaddsd (float) |
Float64, Int |
vcvtsi2sd + vaddsd (convert + add) |
Julia: 1st call compiles a version for the concrete types, then reuses it. No type checks left → ≈ C.
Try
f(3.0)andf(3). What changes?
▶ Script: cells “3. Type specialization — step 1” to “Step 3, with an Int”
f, two compilationssum beats mysum10⁵ floats (fits in cache):
| time / call | |
|---|---|
mysum |
44 µs |
Base sum |
7.5 µs |
~6× apart.
@code_nativetells why.
▶ Script: cells “4. Bonus (optional)” to “The kernel
sumcalls”
mysum: what to look forsum: what to look for@code_native sum(vc) is mostly a call; the work is in Base.mapreduce_impl:
Bet first: why can’t the compiler do this for our loop?
Warning
Float + is not associative: (a+b)+c ≠ a+(b+c). The compiler must keep our order → scalar.
sum allows reordering → vectorized.
@simd: permission to reorderfunction mysum_simd(x)
s = zero(eltype(x))
@inbounds @simd for i in eachindex(x) # ← @simd
s += x[i]
end
return s
end@inbounds: no bounds checks · @simd: may reorder +
| variant | time / call |
|---|---|
mysum (scalar) |
44 µs |
mysum_simd (vaddpd) |
~4 µs |
Base sum |
7.5 µs |
Not magic: the algorithm (reordering → SIMD), not the language.
estimate_pi(10_000_000)?@code_warntype = @code_typed, uncertain types in colorn? Why ideal for parallelism?▶ Script: cells “5. Monte-Carlo π” to “Q3”
warmup_jit/@simd: sum_vs_size/reflection.jl🎯 Concrete types → specialized code (≈ C) ·
@btime f($x)
⬅ Previous: 0 — Julia for Python users · Next: 2 — Multiple dispatch vs OOP ➡