Written in Python, Part 1: The Slowest Language Won
Python runs orders of magnitude slower than C. Every frontier model is trained by it. The usual explanation reverses the causation. The interpreter was never on the critical path.
A series on how one language shaped artificial intelligence, and how intelligence is now reshaping it.
A Hobby Project That Wasn’t
The office was closed for the week around Christmas. Amsterdam in December 1989 gets about eight hours of grey daylight, and the Centrum Wiskunde en Informatica, the Dutch national research institute for mathematics and computer science, had shut down for the holidays. Guido van Rossum had a home computer and an empty week. Writing about it six years afterward, he described the project he went looking for that Christmas as a hobby, something to fill the days while the office was shut.
What he produced over those weeks was an interpreter. Not a compiler, not a specification, not a paper. A program that reads a line of text, works out what it means, does the corresponding thing, and moves to the next line. That choice is worth pausing on, because it is the least ambitious option available to someone designing a language and it fixes the character of everything built on top. An interpreter is slow by construction. It decides what to do at the moment of doing it, every time, and paying that cost repeatedly is the price of never having to know in advance what the program will contain. He was not trying to build something fast. He was trying to build something he could use by Thursday.
This is the founding story, and it is told constantly, and it is misleading in a way that matters for everything that follows.
The misleading part is the word hobby. It implies a blank page. What actually happened that week was that a man who had spent three and a half years building a programming language that failed sat down to build another one, carrying with him a precise and expensive understanding of why the first one had died. The Christmas story compresses a long professional defeat into a weekend of inspiration. Strip the compression away and the interesting question appears immediately: what does someone build when they already know exactly what killed their last language?
The answer to that question is the reason a language that executes arithmetic roughly seven orders of magnitude slower than a modern accelerator now sits at the top of every serious machine learning system on earth.
The standard explanation for this arrangement runs: Python is slow, but it has a great ecosystem, and the ecosystem outweighed the slowness. Every clause in that sentence is true. The causal arrow is backwards. Python did not win the machine learning stack despite being slow. It won a position in the stack where being slow costs nothing, and it was designed, from that first week onward, to occupy exactly such a position without ever admitting that was the plan.
What Killed ABC
To see why, you have to look at the language that failed.
ABC began in 1975 at the Mathematical Centre in Amsterdam, the institute that would later be renamed CWI, as a project by Leo Geurts and Lambert Meertens to build a replacement for BASIC. Meertens was van Rossum’s mentor there, and he is worth a moment on his own, because the usual telling reduces him to a line in someone else’s biography.
Seven years before ABC started, Meertens submitted a string quartet to the IFIP Congress in Edinburgh. He had not composed it in the ordinary sense. He had written a grammar, the first non-context-free affix grammar, and generated the piece from it. The jury gave him a special prize. The score was published the same year as a Mathematical Centre report, which is a sentence that tells you most of what you need to know about the intellectual culture of the place. He would go on to edit the revised ALGOL 68 report and, for six years in the 1970s, to chair the Dutch Pacifist Socialist Party.
A man who generates a string quartet from a grammar is not going to accept that programming belongs to engineers. Meertens and Geurts published a programming course in the bulletin of the Computer Arts Society, and Meertens taught programming to non-specialists, artists among them, and ran into something anyone who has taught a technical subject will recognize. The things that were obvious to a scientist were not obvious to anyone else, and the obstacle was not intelligence. It was that the languages of the period asked their users to think about the machine before they could think about the problem. Computer time cost more than programmer time, so languages had been designed to be efficient for the machine and merely survivable for the person. The ABC team proposed inverting that.
Van Rossum joined the effort in 1982, the same year as Steven Pemberton, after graduating from the University of Amsterdam, and worked on it for three and a half years as an implementer rather than as its architect. He has been consistent, across decades of interviews, about how much of Python came out of that period, and about being indebted to what he learned and to the people he learned it from.
ABC took readability as its primary design constraint rather than as a courtesy extended after the important decisions were made. That is a stronger commitment than it sounds. Nearly every language claims to value clarity. What distinguishes ABC is that when clarity conflicted with something else, clarity won, and the something else was usually performance or machine proximity. The language had exactly five basic types, number, text, compound, list, and table, chosen to match how a non-specialist thinks about data rather than how memory is laid out. It required no variable declarations. It indicated statement nesting by indentation, following what Peter Landin had named the off-side rule two decades earlier, and this is the single most recognizable thing about Python today and was not van Rossum’s invention.
It got a great many things right. It also died, and the causes are worth separating, because the tempting story is not the accurate one.
The tempting story is that ABC died of distribution. There is real evidence for it. This was the late 1980s, before the web, and getting a language into someone’s hands meant physically moving a copy to them. There was no repository to point at, no single command that fetched an interpreter, no social mechanism by which a curious person could try a language on a Tuesday afternoon because a colleague mentioned it. In a pre-web world, distribution was the hurdle nobody in Amsterdam could clear.
But the record documents a second cause more precisely, and it is the one that matters here. ABC was built as a monolithic system. It could not be extended, could not be adapted as requirements changed, and, most consequentially, could not reach the file system or the operating system underneath it. It was a language for teaching and prototyping that had no way to become a language for doing.
The two causes are related, of course. A language you cannot extend is a language nobody has a reason to pass along. But they are different failures and they taught different lessons, and van Rossum emerged from ABC carrying both. The first was that readability was worth designing for. The second, less often quoted, was that a language nobody can obtain or extend is a language that does not exist.
Both lessons are visible in the thing he built over that Christmas week. One of them is visible in Python’s syntax, which is the part everyone talks about. The other is visible in Python’s architecture, which is the part that actually determined its future.
Where the Time Actually Goes
Skip forward three decades and consider a question that sounds like it should embarrass the language.
A training run for a frontier model costs somewhere in the range of tens to hundreds of millions of dollars in compute. The code orchestrating that run is Python. How much of that money is being burned by the interpreter?
Horace He works on the PyTorch team at Meta and is responsible for torch.compile and FlexAttention, which places him among the small number of people whose job is to make sure expensive hardware is not idling. In 2022 he wrote an essay called Making Deep Learning Go Brrrr From First Principles, which has since become the standard reference for reasoning about this class of question.
It opens with a complaint rather than a framework, and the complaint is the reason the framework exists. He describes what practitioners actually do when a model runs slowly, which is to reach for a grab bag of things that worked before or that somebody mentioned on Twitter. Use in-place operations. Set the gradients to None. Install one particular patch version of PyTorch and not the one after it. He is not sneering at this. He is pointing out that people optimize by guessing because nobody has told them what to measure, and that the guessing is expensive in a field where a wasted week of GPU time is a real number on a real invoice.
His answer is to insist that any deep learning workload is spending its time in one of exactly three places. Compute, meaning actual floating point arithmetic on the accelerator. Memory bandwidth, meaning the movement of tensors between the accelerator’s storage and its compute units. And overhead, meaning everything else.
Python lands in the third bucket, and He is blunt about it. Time spent in the Python interpreter? Overhead. So does the framework’s own dispatch logic, and so does the latency of launching a kernel as distinct from executing one.
The magnitudes involved deserve stating precisely, because the imprecise version of this comparison circulates widely and is wrong in an instructive way. An NVIDIA A100, the accelerator that carried most large model training between 2020 and 2023, is rated at 312 teraflops of half-precision throughput on its tensor cores, dense, without structured sparsity. Benchmarking Python locally, He measured roughly 32 million additions per second. Put those side by side and the ratio is not a factor of a hundred, the number people casually reach for when comparing Python to C. In the time Python performs one floating point operation, an A100 can perform close to ten million.
Nor is the interpreter the only tax. The framework adds its own. Running the same small-scale experiment through PyTorch rather than raw Python, He measured a few hundred thousand operations per second, worse than the interpreter alone, because a tensor addition in PyTorch is not one thing. Python has to look up which method the addition dispatches to. PyTorch then has to determine the tensor’s data type, its device, and whether gradients need tracking, all to decide which kernel is the right kernel. Only then does it launch. On a flamegraph of PyTorch performing a single addition, the arithmetic is one narrow box in a wide field of deciding what to do. Every one of those lookups exists so the next line can do something different from the last. The overhead is the cost of not having to commit in advance.
The correct response to these numbers is not that Python is unusable for machine learning. It is to ask how any system built this way could possibly work at all.
The answer is the single most important structural fact in this series, and it has nothing to do with how fast Python runs. Frameworks like PyTorch dispatch work to the accelerator asynchronously. When your Python code issues an operation, the framework queues a kernel and returns immediately, without waiting for the arithmetic to finish. The interpreter then proceeds to the next line and queues the next kernel. As long as the interpreter can stay ahead of the accelerator, filling the queue faster than the hardware drains it, every microsecond the interpreter spends is spent in parallel with real work and costs nothing. The overhead does not get optimized away. It gets hidden.
This is what it means to say Python occupies the orchestration layer. It is not a metaphor about ecosystems or communities. It is a claim about a queue. The language sits above the work rather than inside it, and the only performance requirement placed on anything sitting above the work is that it must not fall behind.
Which yields the condition under which the arrangement breaks, stated by He with more precision than the marketing version of this story ever manages: if the operations being dispatched are too small, the interpreter cannot run far enough ahead, and a very expensive machine sits waiting for instructions. Slowness at the top of the stack is free until the units of work at the bottom get small. Then it is not free at all.
Hold onto that condition. It is where the fifth instalment of this series lives, and the sixth.
The Escape Hatch Was in the Blueprint
None of this would have been available to Python if the language had been designed as a closed system, and the striking thing about the historical record is how early the opposite decision was made.
Van Rossum was not writing Python in a vacuum that Christmas. He had moved on from ABC to work on Amoeba, a distributed operating system, alongside Sape Mullender at CWI. That work was done in C, and by his own account in an oral history recorded for the Computer History Museum, the frustration that pushed him toward a new language was specific and practical: he believed he would be considerably more productive if he could write in something like ABC instead of C, provided that something could still reach the system underneath it.
He said the same thing more plainly in 1996, describing what he had set out to write that Christmas: an interpreter for a language descended from ABC, aimed at the people who lived in Unix and C. Not aimed at students. Not aimed at artists. Aimed at the population that already had system access and wanted something more humane to hold it with.
That target audience is the whole architecture. ABC could not reach the system. It was a beautiful room with no doors. Python was designed from the outset to combine the productivity of ABC with access to C, borrowing modularity ideas from Modula-2 and the improvisational feel of Unix shell scripting. The C extension interface was not bolted on later under pressure from users who needed speed. It was in the original design brief, because the original design brief was written by someone who had just spent three and a half years trapped inside a language that had no way out.
He was not theorizing about system access from a distance either. In 1986, while at CWI, van Rossum wrote a glob routine that was contributed to BSD Unix. This was a person who lived at the boundary between a high level language and the operating system, and who built his own language with that boundary already load bearing.
The consequence took thirty years to become obvious. Every performance-critical thing that Python now drives, the linear algebra kernels, the tensor libraries, the CUDA calls, the compiled graph executors, reaches the hardware through a door that was cut before anyone involved had heard of a neural network that worked. The two-language structure of modern machine learning, Python on top and compiled code underneath, is often described as an awkward compromise the field backed into. It is closer to the truth to say the field discovered a shape that had been sitting in the language since 1989, waiting for a workload that needed it.
There is a quieter point underneath the historical one. A language that is fast has an incentive to keep work inside itself. A language that is slow, and knows it, has an incentive to build excellent doors. Python’s slowness was not neutral with respect to its architecture. It pushed the design toward delegation, and delegation is precisely the skill the machine learning stack turned out to require.
Compare the alternative posture. A language that believes it can be the executor treats every call out to foreign code as a failure of its own runtime, something to be minimized and eventually absorbed, and its foreign function interface tends to be awkward because making it pleasant would be admitting defeat. Python never had that pride available. Reaching into C was the plan, and the interface was maintained as a first-class part of the language by a community that assumed most serious numerical work would happen elsewhere.
Thirty years of that assumption compounds. By the time deep learning arrived, the field did not have to invent the pattern of wrapping compiled code and making it feel native. It inherited a mature version, along with the people who knew how to maintain it.
Where Slowness Actually Bit
An argument that only collects confirming evidence is not an argument. So consider the case where all of this failed, because it is more informative than any of the successes.
Reinforcement learning has an inner loop that supervised learning does not. An agent takes an action, an environment computes the consequence, the agent observes and acts again, and this cycle repeats millions of times. For most of the 2010s that environment ran on the CPU, stepped from Python, while the policy network ran on the accelerator. Every step meant crossing between the two.
The arithmetic of that arrangement is brutal. A single CPU core simulating a physics environment manages something on the order of one to five thousand steps per second. A policy network on a modern GPU can consume upward of half a million. The accelerator spends the overwhelming majority of its life waiting, which is the failure mode He identified in its purest form: the units of work are small, the interpreter and the transfer cost cannot be hidden behind them, and an extraordinarily expensive machine is reduced to an ornament.
The fix, when it came, was not to make the loop faster. It was to remove the loop from the interpreter’s control path entirely. Isaac Gym, published in 2021 by Viktor Makoviychuk and colleagues at NVIDIA, put the physics engine, the observation computation, and the reward computation all in GPU memory, exposing the resulting buffers directly as tensors so that nothing had to travel back to the CPU between steps. Brax, released the same year by a team at Google including C. Daniel Freeman, did the equivalent in JAX. The reported speedups run to two and three orders of magnitude on continuous control tasks, with locomotion behaviours that had taken hours arriving in minutes on a single accelerator.
Read the interface those systems expose and the point sharpens. Isaac Gym’s headline contribution is a Python API. The user still writes Python. What changed is that the Python no longer sits between the steps. It sets the simulation up, hands over a description of what should happen, and gets out of the way for thousands of steps at a time.
Now, the disciplined version of what this shows, because there is a sloppy version and it is tempting.
The sloppy version says Python’s slowness caused reinforcement learning to move onto the GPU. That overstates the case. The dominant cost in the old arrangement was the traffic between CPU and accelerator, not interpreter execution as such, and anyone who has profiled these systems will say so.
The defensible version is more interesting anyway. Python’s position at the top of the stack imposes a structural rule on everything built beneath it: whatever sits in the inner loop must be pushed below the interpreter, or it will dominate. For most of deep learning, that rule was easy to satisfy, because the inner loop was a matrix multiplication and matrix multiplications were the first thing anyone pushed down. Reinforcement learning was the field where the rule could not be satisfied for the better part of a decade, because the thing in the inner loop was a physics simulation with branching logic and per-step state, and there was nowhere to push it. RL paid for that in research velocity, and the payment shows up in the historical record as a field that scaled later and more awkwardly than its neighbours.
The language did not dictate the research. It priced it. Some directions were cheap and some were expensive, and researchers, being people with finite time and finite compute budgets, went where things were cheap.
It is worth being concrete about who paid. When the JaxMARL team moved multi-agent environments onto the accelerator, they described the old arrangement as one that limited scalability to the compute typically available in academia, and reported wall-clock improvements of roughly fourteen times for a single training pipeline and up to twelve thousand five hundred times when many runs are vectorized together. Numbers of that size are not efficiency gains. They are the difference between an experiment a graduate student can run and an experiment a graduate student cannot. A language’s position in the stack decided which questions were affordable to ask, and it decided it differently for a lab with a cluster than for one without.
Some people simply paid the bill. Joseph Suarez built PufferLib by writing his environments entirely in C, more than twenty thousand lines of it, to get millions of steps per second out of hardware that Python could not keep fed. It works. It is also, as a later paper noted with some understatement, a C implementation in a field where everyone writes Python, which means every environment added to it costs a kind of labour most of the field cannot supply. That is what the price looks like when someone pays it in full rather than routing around it.
That pricing mechanism is the subject this series is really about, and it will get a full instalment of its own.
The Team That Was Cancelled
There is a coda to the story of the man who started it, and it is the sort of coda that would seem heavy handed if someone had invented it.
Van Rossum retired from Dropbox in 2019. He was not good at retirement. In November 2020 he announced he was coming out of it to join Microsoft, and his own explanation for the decision has the flatness of someone who does not think it requires much justification: he got bored sitting at home while retired. Given freedom to choose a project, he chose to make Python faster.
The vehicle was a plan drafted in 2020 by the core developer Mark Shannon, proposing to speed up CPython by a factor of five across four releases. Van Rossum’s read on it was that the plan was sound and impossible for one volunteer, so his instinct was to see whether Microsoft would fund a team around Shannon. Microsoft agreed. Six engineers, working in the open, no long-lived private forks, no surprise six-thousand-line pull requests.
They delivered real gains. Python 3.11 came in around twenty-five percent faster on average, which is a substantial result for a mature interpreter with a stable C extension ABI to protect. The later releases delivered less, and the five-times target receded.
In May 2025 Microsoft cancelled the project. Most of the team was laid off. The notices went out while the team was travelling to Pittsburgh for the Python Language Summit at PyCon, so several of them learned they no longer had jobs while in transit to the annual gathering of the people whose language they had spent four years accelerating. Mike Droettboom, one of the core developers on the team, wrote publicly that Microsoft’s support for the project had been cancelled and that his heart went out to the majority of the team who had been let go. Among those laid off were Mark Shannon, who had authored the plan, along with core developers Eric Snow and Irit Katriel.
You can read this as a story about corporate priorities in a year of layoffs, and that reading is available and probably correct as far as it goes. But there is a second reading that belongs to this series.
It helps to know that when He listed the remedies for each of his three performance regimes, the entry under overhead-bound included, among the serious suggestions, simply not using Python. The joke works because everyone knows the option is unavailable. Nobody is going to rewrite the stack. The overhead is not a problem to be solved so much as a fact to be managed, and the field manages it by keeping the units of work large enough that the interpreter stays out of the way.
The most consequential software system humanity has yet built runs on Python. The best funded organized attempt to make Python fast was cancelled, and the machine learning field did not notice. Not because the field is callous, though the silence is worth sitting with, but because a twenty-five percent improvement in interpreter throughput moves nothing at all when the interpreter is already running ahead of the queue. The work that mattered was happening in kernels, in compilers, in memory layouts, in the parts of the stack that Python delegates to and never touches. Faster CPython was a serious project aimed at a bottleneck that, for this particular workload, had stopped existing.
Which returns us to a week in Amsterdam and a man who had just watched a good language die because nobody could get it.
He did not set out to build the control layer for machine intelligence. He set out to build something readable, something extensible, something a person could actually obtain and use, and he built it in a form that never pretended to be the thing doing the work. That refusal to compete on execution speed, which reads for most of Python’s history as a limitation, turns out to be the property that made it fit. The slowest language won because it was the only one that never needed to be fast.
It did need one other thing, though, and Python did not have it in 1991. Handing work down to compiled code is only useful if there is a shared way to describe the work being handed down. Someone had to invent the object that everything below the interpreter could agree on.
That object is an array, and the next instalment belongs to the people who built it.
Written in Python. How a language built for human readability became the control layer of machine intelligence.


