Python isn't just glue, it's an implicit JIT ecosystem

Most recent update: 22nd November 2024 - 17:54:42 - 9304 characters

Writing more Rust recently led me to a revelation about Python. Rust was vital to my original task, but only a few simplifications away, the shorter Python version leapt to almost as fast. I'd stumbled from a cold path to a hot path...

Looking closer, I began to see these hot paths everywhere in Python's ecosystem - well-worn trails connecting optimized nodes, paved over time by countless developers before me.

What struck me wasn't just finding a single optimized path, it was how everything worked so well together, across components not originally intended to be so tightly coupled. This pattern felt familiar. Pervasive even. This felt far more than just Python as a glue language.

It's not fast. It's not magical. And yes, it always has a breaking point.

But that's exactly what makes it special.

You can:

  • write it in your sleep
  • import any_magic as you need (batteries included or a global pip install)
  • conjure up some Frankensteinian FortRust++ library written in MMIX assembly from the dawn of the machine age
  • execute your code line by line until it (predictably) breaks, leaving you in an interpreter to introspect the ruins

And here's the thing: that's actually a remarkably good process for early development!

The combination is what is most profoundly special, captured in the broader picture of how the Python ecosystem evolves and what directs that evolution.

When you write Python code you're not just writing glue, you are an explorer in Python's implicit just-in-time compilation ecosystem.

Every time a new found Python code path becomes hot enough, the ecosystem responds by forging a new component from the barest of metals that is then glued into place.

This glue isn't static. It evolves much as a desire path does, based on the patterns of usage across the ecosystem.

Python's role isn't just connecting components - it's discovering which components need to exist.

The Python performance paradox

Python is slow. But maybe, just maybe, we might want it that way?

When a Python code path becomes slow enough to matter, something counter-intuitive happens: the ecosystem doesn't optimize the Python, it glues in something else.

Python is slow to run, but fast to experiment with. You act as a scout, finding paths worth paving. The paths favor expressivity, ease of use, and simplicity over performance. You're not going to win the performance battle so you don't even try to fight it. The more heavily used the trail, the more infrastructure arrives to support it.

This is an emergent optimization strategy that works better than any planning could hope to.

Python, by itself, is the antithesis of premature optimization. It's all about getting something running and only later deciding to make it work faster (if that even matters).

The Python API trends towards Pareto optimal, typically covering 80% of the use cases while exposing 20% of the underlying component's capabilities. Instead of reinventing wheels in Fastâ„¢ languages, we sticky tape in the best optimized solutions where we find needed.

If we need to go further than Python's glue allows we know we've already derisked the underlying component and can drop down a layer of abstraction if needed. Python doesn't matter if it's written in FortRust++ as long as the Python API bends itself towards simple and easy to use.

Python continues bouncing along, optimizing for end user capabilities (ease of use, composition, simplicity, ...) rather than underlying magic. Python doesn't get jealous of other languages in trying to steal that hard won library for itself.

The implicit JIT ecosystem

Before diving deeper, let's unpack what we mean by an "implicit JIT ecosystem".

In traditional Just-In-Time (JIT) compilation, a program optimizes its hot paths - frequently executed code sections - during runtime. This means we don't spend a large amount of time optimizing code paths that aren't important up front (premature optimization) and could even find better optimizations based on how the code is actually used rather than how we assume it's going to be used.

The Python ecosystem does something remarkably similar, but at a community scale.

Instead of optimizing at runtime within a single program, Python's ecosystem optimizes across the collective usage patterns of the entire community. When Python developers consistently hit performance bottlenecks along certain paths, the ecosystem responds.

While part of this optimization process is simply usage driven (i.e. the most paths "traced" in the largest and most active ecosystem) this alone doesn't explain all of Python's success.

There are characteristics of Python that amplify this dynamic, such as readability, forgiving nature, and even its own performance constraints ("slow Python"), that all act in concert to create an environment that discovers and optimizes these paths in an accessible, composable, and (eventually) efficient way.

The implicit JIT ecosystem isn't unique to Python but Python's characteristics amplify and shape the pace and breadth of that evolution.

Even if the implicit JIT ecosystem is entirely generic to any glue language it's a dynamic worth consideration.

The implicit JIT ecosystem made explicit

While Python might be the most successful implicit JIT ecosystem, it isn't alone. We've seen this pattern play out repeatedly: start with a "slow" but expressive language, discover the hot paths through real usage, then optimize those specific paths as proven necessary.

As startups grew into tech giants, their engineering teams burned in explicit hot paths:

But those explicit JIT ecosystems rarely match those that Python's implicit JIT ecosystem evolved. The ecosystem isn't just the individual, it's the collective. Instead we have a singular sequoia tree sticking out above the canopy of a dwarfed forest.

Python as the fastest slow language

The machine learning and data science world in Python provides perhaps the clearest example of this implicit JIT evolution.

Slow Python code that becomes important is a structural flaw that the ecosystem works to correct.

Python's need for numerical processing became numpy, pandas, scikit-learn, and a hundred other base tools. Being written in C and Cython was enough to spark the flame, but we're still seeing further gains - like Polars exploring where Rust might take the pandas codebase.

The evolution of ML frameworks shows this pattern at its most dramatic. PyTorch didn't emerge from Lua's Torch because Python was faster - it emerged because Python's ecosystem was fully populated and the ease of use built further momentum in adoption. Libraries had to surface in Python or risk irrelevance.

Then a small Python library called autograd asked "What if we could differentiate native Python and Numpy code?". This inspired JAX. Mirroring the point, JAX originally stood for "Just After (Python's) eXecution", grew "JAX is Autograd and XLA", and now ... JAX is JAX. Yet it still supports much the same Python API as when numpy first started.

import jax.numpy as jnp

for W, b in params:
  outputs = jnp.dot(inputs, W) + b
  inputs = jnp.tanh(outputs)

Somehow we now have Python, the "slow" language, orchestrating most of the world's FLOPS.

The glue language is also the LLM language

Python was optimized to be concise, forgiving, incremental, and (relatively) simple for humans - which is the exact same need for LLMs.

For most tasks you likely don't want anything beyond three lines (import, setup, execution) if you can help it. Python is optimally placed for that:

  • no overhead to play,
  • a full ecosystem of interoperable components to thread together,
  • and a set of APIs built to be forgivingly simple to a wayward programmer.

The Python interpreter itself becomes a powerful environment for self play, suggesting Python's role as a glue language might be more central to the age of AI than giving you a hot path to run the matrix multiplications.

The implicit JIT ecosystem gets a true JIT

After decades of being the catalyst for an implicit JIT ecosystem, Python is finally experimenting with its own JIT in 3.13.

It's a fitting evolution. That slowness we once cursed might well have been a necessary catalyst for the ecosystem to build hot paths that Python alone would never have been capable of.

Python's performance constraints didn't just create an ecosystem of optimized components, they allowed us to see exactly what paths needed to be optimized, no more and no less. Sometimes the best path forward is by going both fast and slow.