The mental model most programmers carry around looks something like this: C++ is fast, Python is slow, and if you care about performance you write compiled code. This model was never perfectly accurate, and by now it’s actively misleading.
Interpreted languages have gotten dramatically faster. Compiled languages have taken on enough abstraction overhead that their raw-speed advantages are smaller than advertised. And in the vast majority of real applications, neither fact matters much, because your bottleneck isn’t the language runtime anyway.
The interpreted side has been closing the gap for decades
Python is the standard punching bag here, so let’s use it. CPython, the reference interpreter, is genuinely slow at CPU-bound tasks. But Python hasn’t stood still. PyPy, a JIT-compiled Python implementation, routinely executes numeric code five to ten times faster than CPython, sometimes more. Numba applies JIT compilation to specific Python functions and can approach C speeds for the right numerical workloads. And Python increasingly acts as a coordination layer on top of libraries like NumPy and PyTorch, where the actual computation runs in highly optimized C or CUDA. The Python code that trains a large neural network isn’t slow. The Python interpreter barely touches the hot path.
JavaScript’s trajectory is even more dramatic. V8, Google’s JavaScript engine, introduced hidden classes and inline caching around 2008 and transformed what was a scripting language for form validation into something that can run real-time graphics, audio processing, and server-side workloads at speeds that would have seemed impossible two decades ago. The V8 team spent years chasing C++ performance from an interpreted language, and while they never fully caught it, they got close enough to matter.
The compiled side carries costs people undercount
Compiled doesn’t mean zero overhead. It means the overhead is paid at different times and in different ways.
Modern C++ with heavy template metaprogramming can produce machine code that’s genuinely difficult to reason about. Abstractions in Rust or C++ that look thin in source can generate surprising amounts of machine code once the compiler unrolls them. Memory allocation patterns in C++ can produce cache misses that devastate performance in ways that a JIT-compiled language, which can profile and reorganize at runtime, might actually handle better.
There’s also the human cost. A C++ codebase is slower to write, harder to maintain, and more expensive to staff than an equivalent Python or JavaScript codebase. If you choose C++ to get a 30% performance advantage but your team ships features at half the rate, the math doesn’t obviously favor C++.
The bottleneck is almost never the language runtime
This is the argument that actually settles the debate for most working engineers. As one article on this site explains well, your program spends most of its time waiting. Waiting for the database. Waiting for the network. Waiting for disk I/O. Waiting for an external API.
When a Python web service handles a request, the typical profiling result shows that somewhere between 80 and 95 percent of wall-clock time is spent waiting on I/O. The Python interpreter is idle for most of that. Rewriting the service in Go or C++ doesn’t change the database query time. It changes the thin sliver of time spent in application logic, which was already not your problem.
The cases where language runtime speed genuinely matters are specific: numerical simulation, real-time signal processing, game engines, compilers, cryptographic primitives, video encoding. These exist and they matter. But they’re not the median production service.
The counterargument
The serious version of the counterargument isn’t “C is faster than Python” (it often is). It’s that at scale, constant factors matter. A service processing a billion requests per day that wastes 5 milliseconds of CPU per request in interpreter overhead is burning real money. Companies like Cloudflare and Discord have published accounts of rewriting hot paths from interpreted or garbage-collected languages to Rust and seeing meaningful cost reductions.
That’s real. The question is whether you’re operating at that scale and whether you’ve actually measured that the language runtime is your bottleneck before spending six months on a rewrite. Most teams that reach for a compiled language for performance reasons haven’t done that measurement. They’re optimizing speculatively, which is the most expensive kind.
The right answer isn’t “interpreted languages are always fast enough.” It’s “know where your actual bottleneck is before you let language choice drive architectural decisions.”
What to take away
The hierarchy is real but compressed. C++ is still faster than Python for CPU-bound work. It’s just not ten times faster across the board anymore, and the workloads where it matters are narrower than the conventional wisdom suggests.
Start with the language that lets your team move fastest and maintain code most confidently. Profile before you optimize. When you find a genuine CPU-bound hot path, you have options short of a full rewrite: JIT libraries, native extensions, or isolated services. A Python service with a C extension in one critical function is a completely legitimate architecture.
The performance conversation in software has always been more nuanced than the compiled-versus-interpreted framing implies. The engineers who get performance right are the ones who measure first and reach for the sledgehammer only when the data justifies it.