← Software Engineering & Cloud Studio
Concept Explainer · Software Engineering

Compiled vs. Interpreted Languages

Why "Python is slower than C" is really a question of whentranslation happens — not simply which language is "better."

Every program a CPU runs has to end up as native machine instructions eventually — there's no way around that. The entire difference between a compiled language and an interpreted one is whenthat translation from source code to machine instructions happens, and how much of it happens ahead of time versus while the program is actually running. That one timing decision — not some vague notion of language "quality" — is what produces the performance gap people notice between something like C and something like Python.

The Setup

Translation has to happen — the only question is when

A compiler translates source code entirely into native machine code (or a form very close to it) in one pass, before the program is ever run. That compilation step happens once, produces a standalone executable file, and once it's done, running the program means the CPU executes those already-translated instructions directly — no translation happens during execution at all. An interpreter, by contrast, translates and executes source code roughly line by line (or translates to an intermediate bytecode first, then interprets that bytecode) at runtime — every single time the program runs, that translation work happens all over again, layered throughout execution rather than finished in advance.

Compiled: all translation happens ahead of time

Fast execution
COMPILE TIME — happens once, before the program ever runssource code(e.g. main.c)compilertranslates all of itnative executable(pure machine code)RUN ▶RUNTIMECPUexecutes machine code directly100% real work, 0% translationAll the translation work is finished before "RUN." Nothing to the right of that line ever re-translates anything.
When translation happens
Once, before running
A single, ahead-of-time compile step produces a standalone executable.
Runtime translation overhead
None
The CPU runs native instructions directly — the tradeoff is a separate compile step, and a platform-specific executable.

Interpreted: translation happens during execution, every run

Repeated overhead
NO SEPARATE COMPILE STEPsource code(e.g. main.py)RUN ▶RUNTIME — interpreter translates as it goes, line by linetranslateruntranslateruntranslateruntranslateruntranslation overhead recurs throughout the entire timeline — not finished before "RUN"this whole cycle repeats from the top every single time the program is run
When translation happens
During execution, every run
No compile step needed first — but the interpreter re-translates the same source every single time.
Runtime translation overhead
Ongoing, layered throughout
The tradeoff: faster iteration and the same source code runs on any platform with the right interpreter.

The reality: a spectrum, not a strict binary

Hybrid: bytecode + JIT
all translation ahead of time (C, Rust, Go)all translation during execution (shell scripts)you are here: Python, Java, JavaScriptsource code(.py / .java / .js)bytecode compilerahead of time, onceVM: interprets bytecode / JIT-compiles hot pathsduring execution, every run

Python, Java, and JavaScript all compile source code to an intermediate bytecode ahead of time — but that bytecode still gets interpreted (or, for frequently-executed "hot" code paths, further compiled just-in-time (JIT)to native machine code) by a virtual machine while the program runs. That's a real ahead-of-time step and a real during-execution step, which is exactly why "compiled vs. interpreted" is better understood as a spectrum of how much translation happens ahead of time vs. during execution, not a strict either/or category.

Why this works

Python isn't slower than C because it's a "worse" language. It's slower because more of its translation happens while the program is running.

C's toolchain does all of its translation work once, ahead of time, and hands the CPU pure native machine instructions with zero runtime translation cost. Python's toolchain compiles to bytecode ahead of time too, but that bytecode still has to be interpreted — and, in modern implementations, selectively JIT-compiled — while the program runs, every single time it runs. That interpretation and JIT-compilation work is real CPU time spent on something C's executable never has to do at all. The performance gap is a direct, structural consequence of when translation happens, not an arbitrary judgment about which language is engineered better.

Common misconception
"Compiled languages are always simply 'better' or 'faster' than interpreted languages, since compilation is inherently the superior approach."

Incomplete, and the "inherently superior" framing is the part that's wrong. The real, structural difference is whentranslation happens — ahead of time versus during execution — and that timing does produce a real, predictable execution-speed tradeoff in compiled languages' favor. But interpreted and hybrid languages offer genuine, countervailing engineering advantages: no separate compile step before you can run or test a change, the same source code running unmodified on any platform with the right interpreter installed, and much faster development iteration. Those advantages are exactly why interpreted and hybrid languages dominate scripting, data science, web backends, and rapid prototyping despite the raw speed tradeoff. And as the third panel above shows, most "interpreted" languages people reach for today — Python, Java, JavaScript — actually use a hybrid bytecode-plus-JIT approach that blurs the strict compiled/interpreted binary considerably. It's a spectrum of engineering tradeoffs, not a contest with one universally correct winner.

Related Concept Explainers
Memory Leaks vs. Garbage Collection
Read it →
Stack vs. Heap
Read it →

Compiled vs. Interpreted Languages — Concept Explainer

Explains the real, structural difference between compiled languages (source code translated entirely to native machine code ahead of time, before the program runs) and interpreted languages (source code translated during execution, every time the program runs) — and why modern languages like Python, Java, and JavaScript actually use a hybrid bytecode-plus-JIT approach that sits in between the two extremes.

What a Compiler Actually Does

A compiler translates source code entirely into native machine code (or an intermediate form very close to it) in one pass, completely finished before the program is ever run. That produces a standalone, platform-specific executable file. When the program runs, the CPU executes those already-translated native instructions directly, with zero translation overhead happening during execution. The tradeoffs: you need a separate compile step before you can run the program at all, and the resulting executable is tied to the CPU architecture and operating system it was compiled for — it generally won't run on a different platform without being recompiled.

What an Interpreter Actually Does

An interpreter translates and executes source code roughly line by line (or translates to bytecode first, then interprets that bytecode) at runtime — during execution, rather than ahead of time. That means the translation work happens all over again every single time the program runs, which is real, recurring overhead an equivalent compiled program never pays. In exchange, interpreted languages need no separate compile step before running — you can change a line and rerun immediately — and the same unmodified source code can generally run on any platform that has the right interpreter installed, without recompiling anything.

Why Modern Languages Blur the Line

Python, Java, and JavaScript are the languages people most often call "interpreted," but none of them are purely interpreted in the strict sense. All three first compile source code to an intermediate bytecode ahead of time, and then a virtual machine interprets that bytecode at runtime — often further compiling frequently-executed ("hot") code paths just-in-time (JIT) into native machine code during execution, to claw back some performance. That makes "compiled vs. interpreted" a spectrum describing how much translation happens ahead of time vs. during execution, rather than a strict either/or category. Python's relative slowness next to C isn't a verdict on Python as a language — it's a direct, structural consequence of C finishing all of its translation ahead of time, while Python still does substantial translation work (bytecode interpretation, with partial JIT help in some implementations) during every run.

Frequently asked questions

Why is Python slower than C if they're both just "programming languages"?

Because they translate source code to machine instructions at different times. C's compiler does all of that translation once, ahead of time, producing a native executable the CPU runs directly with no further translation. Python compiles to bytecode ahead of time, but that bytecode is still interpreted (with some JIT optimization in implementations like PyPy) during every execution — real, recurring translation work C's compiled executable never has to do. The gap is structural, not a reflection of Python being "worse."

Does JIT compilation make Python or Java as fast as C?

Usually not quite, though JIT closes a meaningful part of the gap for hot, frequently-executed code paths. A JIT compiler has to do its analysis and native-code generation while the program is running, competing for the same CPU time the program itself needs, and it typically can't apply as many aggressive, whole-program optimizations as an ahead-of-time compiler with unlimited time to analyze the entire codebase before anything runs. For CPU-bound, hot-path-heavy workloads, JIT-compiled languages (like Java, or JavaScript's V8 engine) can get impressively close to compiled-language speed; for typical Python code, the gap to C usually remains larger.

If compiled languages are generally faster, why does anyone build in an interpreted language?

Because raw execution speed isn't the only thing that matters. Interpreted and hybrid languages skip the separate compile step entirely, so you can change a line of code and immediately rerun it — a major win for development speed and iteration. The same source code also generally runs unmodified on any platform with the right interpreter installed, without needing platform-specific recompilation. For most application code, where developer time and portability matter more than shaving milliseconds off a hot loop, that tradeoff clearly favors interpreted or hybrid languages.

Is Java compiled or interpreted?

Both, in the sense that matters here — it's a hybrid. The javac compiler translates Java source code into bytecode ahead of time, once. That bytecode is then interpreted by the Java Virtual Machine (JVM) at runtime, with the JVM's JIT compiler further translating frequently-executed bytecode into native machine code during execution. So Java has a real ahead-of-time compilation step and a real during-execution translation step — it doesn't fit cleanly into either the pure-compiled or pure-interpreted category.

What does "JIT" actually mean?

"Just-in-time" compilation: instead of translating all code to native machine code ahead of time (like a traditional compiler) or interpreting it fresh every time (like a pure interpreter), a JIT compiler watches the program run, identifies code paths that execute often enough to be worth the investment, and compiles just those hot paths to native machine code during execution — trading some upfront analysis time for faster execution on the code that runs most.

🎓

Try our Software Engineering & Cloud Studio

More calculators, simulators, and guides for this discipline.

Related tools & guides

Memory Leaks vs. Garbage Collection — Concept ExplainerStack vs. Heap — Concept ExplainerSynchronous vs. Asynchronous Programming — Concept ExplainerSoftware Engineering & Cloud System Architecture