How Fast Is Crow?
Crow runs one program two ways: interpreted, the moment you start it, or compiled to a native binary through Rust. This article measures both against Rust, Go and CPython on eight programs from the Computer Language Benchmarks Game, on one machine, with every port's output checked byte for byte.
The numbers
Each cell is the median of five runs, three for the interpreter, as wall time for the whole process from launch to exit. The bracket is the range across the runs as a share of the median. A dash means the program was not run that way: at fifty million steps, n-body would keep CPython and the interpreter busy for minutes.
| Program, size | Crow compiled | Rust | Go | CPython | Crow interpreted |
|---|---|---|---|---|---|
| fannkuch-redux, n = 10 | 0.13 s [3%] | 0.13 s [1%] | 0.12 s [1%] | 2.97 s [0%] | 1.31 s [1%] |
| n-body, 50 million steps | 4.20 s [0%] | 1.53 s [0%] | 1.48 s [0%] | — | — |
| n-body, 1 million steps | 0.086 s [1%] | 0.032 s [1%] | 0.031 s [2%] | 2.35 s [2%] | 2.13 s [2%] |
| spectral norm, n = 5,500 | 0.081 s [3%] | 0.076 s [7%] | 0.11 s [4%] | 8.84 s [3%] | 6.57 s [1%] |
| k-nucleotide, 5 million | 0.054 s [4%] | 0.028 s [5%] | 0.13 s [2%] | 4.27 s [6%] | 0.26 s [1%] |
| reverse complement, 5 million | 0.11 s [6%] | 0.061 s [4%] | 0.072 s [5%] | 0.24 s [2%] | 0.55 s [0%] |
| pidigits, 5,000 digits | 0.31 s [1%] | 0.81 s [1%] | 0.26 s [1%] | 0.99 s [1%] | 0.34 s [1%] |
| binary trees, depth 18 | 0.094 s [15%] | 0.76 s [9%] | 0.64 s [3%] | 0.64 s [3%] | 2.26 s [0%] |
| binary trees, depth 18, the Rust port linked with mimalloc | 0.15 s [1%] |
Lower is faster. The Rust and Go ports that take a thread count ran with sixteen, as did Crow's parallel for
;
the CPython ports of spectral norm and binary trees ran a pool of sixteen processes, and the other CPython ports one thread, as the Benchmarks Game's do.
Compiled Crow against Rust and Go
On fannkuch-redux and spectral norm, compiled Crow runs with Rust and Go: fannkuch-redux takes 0.13 seconds against Rust's 0.13 and Go's 0.12, and spectral norm takes 0.081 seconds against Rust's 0.076 and Go's 0.11.
On pidigits, Crow's built-in whole numbers, which grow to any size, take 0.31 seconds: close to Go's math/big
at 0.26 and well ahead of Rust's num-bigint at 0.81.
n-body and the two byte-heavy programs are where the generated code is not yet as tight. Compiled Crow takes 2.7 times Rust's time on n-body, twice on k-nucleotide and 1.7 times on reverse complement.
Binary trees is as much about allocators as about languages. Every port makes one node at a time; Crow's programs allocate through mimalloc, and the Rust and Go ports through their default allocators. Linked against mimalloc, the Rust port's time falls from 0.76 to 0.15 seconds, and Crow's is 0.094.
The interpreter
crow run -i
starts at once, with no build step. It runs each function as a typed instruction form, made on the function's first call, that keeps ints and floats unboxed in registers.
It is faster than CPython on five of the seven programs CPython ran: 2.3 times on fannkuch-redux, 1.1 times on n-body, 1.3 times on spectral norm, 16 times on k-nucleotide and 2.9 times on pidigits. On the other two it is slower, taking 2.3 times CPython's time on reverse complement and 3.5 times on binary trees, where sixteen Python processes beat sixteen interpreter threads.
Compiled, the same programs run from 1.1 times faster than interpreted, on pidigits, to 81 times faster on spectral norm. pidigits spends its time in big-number arithmetic, which the interpreter and the compiled program run alike.
How it was measured
- The machine. An Apple M4 Max with sixteen cores and 64 GB, running macOS 26.5.
- The versions. Crow 0.7.0 with the changes since, rustc 1.93.1, Go 1.23.0 and CPython 3.12.12.
- The builds.
Compiled Crow is the binary
crow buildmakes, at optimization level 3 with link-time optimization; the interpreter iscrow run -i. The Rust ports were built withrustc -C opt-level=3 -C target-cpu=nativeand the Go ports withgo build. - What a timing includes. Start-up, always, and compilation, never: every binary was built beforehand and run once before the timed runs, since macOS checks a new executable at its first launch. The interpreter's time includes reading and lowering the program.
- Correctness. Every port's output was compared byte for byte on every run, and all of them agreed.
- One caveat on pidigits. The Rust port uses the pure-Rust num-bigint crate, where the Benchmarks Game's fastest Rust entries call GMP.
Five runs is a small sample, which is why the raw timings are published with the programs.
Two more comparisons, from earlier
These were measured with earlier development versions of Crow and were not re-run for this article.
Data frames, ten million rows
Crow's frame operations are Rust in its runtime, polars's are Rust called from Python, and pandas's are C and Cython called from Python, so this compares kernels as much as languages. One CSV of ten million rows and 659 MiB, each operation timed alone from inside the program, the best of three, measured on October 6, 2026:
| Operation | Crow | polars 2.0 | pandas 3.0 |
|---|---|---|---|
| load | 0.41 s | 1.04 s | 4.67 s |
| filter | 0.07 s | 0.05 s | 0.24 s |
| group | 0.04 s | 0.03 s | 0.34 s |
| sort | 0.35 s | 0.53 s | 2.04 s |
| join | 0.13 s | 0.22 s | 0.49 s |
| window | 0.07 s | 0.16 s | 0.31 s |
| unique | 0.01 s | 0.05 s | 0.14 s |
| save | 0.40 s | 0.12 s | 4.96 s |
| peak memory | 4.7 GB | 5.2 GB | 3.5 GB |
Crow was faster than pandas on every operation and than polars on five of eight. It was slower than polars on saving a CSV, by three times, and by a third on filter and group.
A web server under load
A TechEmpower-style load at 256 connections against Crow's compiled server and a Rust server on axum serving the same routes, with the same Postgres behind both, measured on October 10, 2026. The numbers say what Crow's web layer costs over the Rust server it is built on.
| Test, 256 connections | Crow compiled | Rust, axum |
|---|---|---|
| plaintext | 1.21 million requests per second | 1.19 million |
| single database query | 78,000 | 83,000 |
| twenty queries | 8,200 | 8,300 |
| twenty updates | 3,850 | 3,900 |
| fortunes | 76,000 | 83,000 |
Crow's server matched Rust on plaintext and reached 92 to 99 percent of its throughput with a database behind the handler.
Run it yourself
The programs in all four languages, the harness and the raw timings are published beside this article:
- README, with the build lines and what each file is
- bench5.py, the harness: five runs per program and language, three for the interpreter, the median and range, and the byte-for-byte check of every output
- results5.json, every timing of this run
crow build fannkuch.co # and the other .co programs
rustc -C opt-level=3 -C target-cpu=native -o fannkuch_rs fannkuch.rs
go build -o fannkuch_go fannkuch.go
./fasta_go 5000000 1 > fasta.txt # the input of k-nucleotide and reverse complement
python bench5.py
Crow, its book and its install line are at github.com/agncrow/crow.
Every timing in the first table comes from the run whose raw results are published beside this article; the frame and web tables give the dates they were measured.
