Compilers vs Interpreters
A compiler translates your entire program to machine code before it runs. An interpreter executes your code line by line. Most modern languages blur this line.
Why It Matters
Knowing how your language executes helps you understand its performance characteristics, error behavior, and debugging tools. It also helps you pick the right language for a job.
Comparison
| Property | Compiler | Interpreter |
|---|---|---|
| When errors appear | Before running (compile time) | While running (runtime) |
| Execution speed | Fast (native machine code) | Slower (overhead per line) |
| Startup time | Slow (must compile first) | Fast (run immediately) |
| Output | Binary executable | No separate file |
| Examples | C, Go, Rust | Python, Ruby, PHP |
The Spectrum
Most languages don’t fit neatly into one category:
Pure Compilation Pure Interpretation
←————————————————————————————————————————→
C Go Rust Java C# Python Bash
| | | |
Compiled Compiled Compiled to Compiled to
to native to native bytecode, bytecode,
code code JIT-compiled interpreted
at runtime by CPython VM
JIT Compilation
Just-In-Time compilation is a hybrid: the program starts interpreted, and the runtime compiles hot paths to native code on the fly.
- Java — JVM uses JIT (HotSpot)
- JavaScript — V8 engine uses JIT
- Python — PyPy uses JIT (CPython does not)
Code Example
# C: compile first, then run
gcc -o hello hello.c # produces binary
./hello # runs native code
# Go: compile and run in one step
go run main.go # compiles to temp binary, runs it
# Python: interpreted (bytecode compiled transparently)
python3 script.py # compiles to .pyc bytecode, then interprets
# You can see Python's compilation step:
python3 -c "import py_compile; py_compile.compile('script.py')"
# Creates __pycache__/script.cpython-312.pyc# Python compiles to bytecode, not machine code
import dis
def greet(name):
return f"Hello, {name}!"
dis.dis(greet)
# Shows bytecode instructions that the CPython VM interpretsAhead-of-Time vs Just-in-Time
| Strategy | Pros | Cons |
|---|---|---|
| AOT (C, Go) | Predictable performance, no runtime overhead | Slower dev cycle |
| JIT (Java, JS) | Can optimize based on runtime data | Warm-up time, more memory |
| Interpreted (Python) | Fastest development cycle | Slowest execution |
Related
- How Computers Execute Code — what happens after compilation
- Type Systems — static types enable better compile-time optimization
- Big O Notation — algorithmic complexity matters more than language speed