Every October, Miguel Grinberg runs his informal performance benchmark against the newest Python 3.15 release, and this year’s results are a split decision: the standard interpreter is barely faster than 3.14, but the experimental JIT compiler has its best showing yet.
Grinberg tested the 3.15.0rc3 release candidate, since the official release was still days away. The benchmark uses two deliberately simple programs. The first computes Fibonacci numbers recursively, a workload that stresses function calls. The second bubble-sorts 10,000 random numbers, pure loop work. Each runs single-threaded and across four threads, against CPython 3.10 through 3.15, plus PyPy 3.12, Node.js 26.3 and Rust 1.97, with separate runs for the JIT and free-threaded interpreter builds introduced in 3.13.
The standard interpreter: a rounding-error year
On the recursive Fibonacci test, single-threaded, 3.15 completes in 6.94 seconds versus 7.17 for 3.14. That is a 1.03x improvement, which is to say: nothing you will notice. The bubble sort test tells the same story, 1.97 seconds against 2.06, again about 1.04x. With four threads the picture is no better, and the Fibonacci run actually regressed slightly, 32.5 seconds against 3.14’s 31.8.
For context on where Python sits, PyPy 3.12 runs the Fibonacci test 5.5 times faster than 3.15, Node.js about 5 times faster, and Rust 77 times faster. On the sort, Node is 30x faster and Rust 50x. These gaps have barely moved in years, which is the quiet point of including them.
Looking back across versions, only two releases in the 3.10 to 3.15 span produced real jumps: 3.11 with its 1.7x leap and 3.14 with 1.23x. Everything else held steady or inched forward. 3.15 is not that kind of release.
The JIT wakes up
The interesting result is the JIT build. In 3.15, the JIT runs the Fibonacci test 1.20x faster than the standard interpreter of the same version, and the sort 1.28x faster. That is the first year the JIT build has beaten the regular interpreter on these tests rather than trailing it. Against the 3.14 JIT, the 3.15 JIT improved 1.24x on Fibonacci and 1.53x on the sort.
Grinberg’s caveat is important though: the JIT is still an experimental feature and he will not use it in production until that changes. So this is a trend worth tracking, not a deployment decision.
Free-threading holds its ground
The free-threaded build, which drops the global interpreter lock, shows the pattern you would expect. Single-threaded it matches the regular interpreter, within a percent or two. Under four threads it is where the real win lives: the free-threaded 3.15 runs the threaded Fibonacci test about 4.5 times faster than the standard build, and the threaded sort about 1.74 times faster. Those ratios held roughly steady from 3.14, meaning the GIL-free build is no longer paying a meaningful single-threaded tax.
That matters for anyone with CPU-bound, thread-parallel Python work. The free-threaded interpreter now looks like a credible option for that specific class of workload, without the performance penalty that made it a curiosity in 3.13.
Should you upgrade?
Grinberg’s own verdict: 3.15 is at best a minor improvement over 3.14 in raw performance, and he sees no reason to rush production upgrades. He plans to use 3.15 as his daily interpreter and may move some projects for the features rather than the speed, calling out JavaScript-style unpacking in comprehensions and lazy imports.
That is a sensible posture for most teams. If you are on 3.13 or earlier, the accumulated gains through 3.14 still make an upgrade worthwhile, and 3.15 comes along for free. If you are on 3.14, there is no performance case yet; upgrade when you want the language features. And if you have genuinely thread-parallel CPU workloads, the free-threaded build deserves a benchmark against your actual code, because the historical downside is mostly gone.
The benchmark has limits worth naming, and Grinberg names them himself. Two small programs cannot represent real workloads: no I/O, no third-party libraries, no garbage collection pressure from long-lived objects. Code that spends its time in NumPy or pandas will see completely different ratios, because those hot paths are compiled C and barely notice which interpreter calls them. The value here is the trend line between interpreter versions under controlled conditions, not absolute numbers for your application.
What would make the upgrade worth it has nothing to do with speed. Lazy imports cut startup time for CLI tools and serverless functions, which is a real cost win for cold starts. The comprehension unpacking syntax closes an annoying gap with JavaScript. Small releases like this one are where the ecosystem quietly consolidates before a bigger jump, which is roughly what happened between 3.11 and 3.14.
The one thing to watch for next cycle is whether the JIT loses its experimental status. If 3.16 ships it as a default-on feature with these gains intact, that would be the first fundamental execution-model change in CPython since the specializing interpreter landed, and the performance conversation changes shape entirely.