A project called microgpt-mlpl takes Andrej Karpathy's famous microgpt.py — a roughly 200-line Python implementation that trains a small GPT on 32,033 names and invents new ones — and reimplements it across five different languages to compare how the choice of programming language shapes both code length and execution speed.
The benchmark setup
Microgpt.py implements a one-layer, four-head GPT with scalar autograd, Adam optimization, and a sampler, all over lists of floats. It contains no external dependencies. The project's Rust port replicates the same algorithm and model shape using a tape-based scalar autograd system with no external crates.
Three separate implementations target sw-MLPL, an array language with built-in automatic differentiation. One faithful port traces microgpt.py line by line with hand-written layers and, with the --rs-parity flag, replays the Rust port's random number stream to produce byte-identical output. A second idiomatic port rebuilds the model using MLPL's Model DSL, composing embeddings, causal attention, residual connections, and Adam in a declarative style. A third compact version reduces the entire pipeline to the smallest possible implementation: the corpus as a single token stream processed in 16-token windows, one DSL chain, and KV-cached sampling.
Performance results
All implementations were tested on an Apple M1 Max. The Rust port runs in 0.589 seconds. The idiomatic MLPL version completes in 0.468 seconds, roughly 21% faster than the compiled Rust binary. The faithful MLPL port takes 0.718 seconds, about 22% slower than Rust but still dramatically faster than the original Python, which requires 64.4 seconds.
The code line counts tell a striking story. The Python original runs 149 lines, the Rust port 390 lines, and the MLPL idiomatic version just 48 lines. The compact MLPL implementation fits the entire trained model into 32 lines.
The key insight: array primitives
The project's central finding is that the idiomatic MLPL interpreter beats compiled Rust because a DSL layer compiles into a single native array operation, while the Rust port retains microgpt.py's per-scalar tape-based autograd. Array-level primitives eliminate the overhead of tracking individual floating-point operations.
This is visible in the model definition itself. Where microgpt.py devotes an entire class to autograd and loops over scalars, the idiomatic MLPL version expresses the full model body as one expression and training as a single call per step, with the optimizer, loss function, and parameter updates all visible in a compact block.
Cross-language parity
One of the more technically notable results is that exact cross-language parity is achievable. The Rust port uses a SplitMix64 random number generator, and the project reimplemented it in pure MLPL using 16-bit limbs. The faithful port with --rs-parity then produces output that is byte-for-byte identical to the Rust version, confirming that the random streams and numerical behavior match exactly across the two language implementations.
Literate programs
Each implementation is distributed as an Org document processed through ob-mlpl. Every code block's output is real rather than mocked. Each section states the underlying mathematics, and model functions carry their equations as annotations. The program blocks tangle into a runnable script whose output is checked against a variant-specific baseline, so the prose cannot drift from the actual code behavior.
The three literate sections guide the reader through progressively deeper understanding: the compact version shows the shape of an MLPL language model in 32 lines, the idiomatic version explains how the Model DSL composes and what its defaults change, and the faithful version spells out every equation — confirming that masked attention equals the KV-cache loop and that gradients equal finite differences.
What the comparison reveals about language design
The project draws several conclusions about how language design affects implementation. Readability and speed trade off on a case-by-case basis: hand-written equations versus DSL layers, a mask built inside the loss versus passed in separately, per-step reads versus pre-encoding — each choice is measured and documented. The interpreter itself shapes the code; every data read copies globals and large arrays, so the corpus must be pre-encoded before training begins.
For developers evaluating approaches to implementing neural networks in different language paradigms, the microgpt-mlpl project offers a concrete, measured comparison that isolates language effects by holding the algorithm and model architecture constant across all implementations.