Lilith.
⌕
Editorial illustration: Optimized C cut parse_number() to 19 ns. Rust only won the first round
Lilith illustration · editorial remix

Simon Willison used Claude Opus 5.5 to build four versions of a SQLite parse_number() function that turns text such as -$1,234.56 into a number. The useful part of the experiment is that he did not stop at the first surprise and instead traced where it came from.

Rust beat straightforward C until C stopped calling a general parser

The test covered two Rust implementations and two C implementations. Straightforward C processed the mixed dataset in 67.3 ns per row, while Rust took 40.5 to 45.9 ns. On decimal values, the gap widened to 106.3 ns versus 46.5 to 54.5 ns.

The cause was not the language as a whole. The C version handed decimal values to the general purpose strtod() function, while Rust used a faster path in its parser. Adding roughly 30 lines for common values brought optimized C down to 19.2 ns on mixed data, 15.8 ns on integers and 17.5 ns on decimals.

One small function exposed both abstraction costs and manual labor

The C extension was 14,408 B. The Rust variants occupied 298,464 B and 320,864 B because they carried the standard library and supporting code. Optimized C also required 104 lines of code, manual buffer handling, overflow checks and care around floating point calculations.

The Rust versions were shorter and safer to write. That is the practical choice for developers: a tiny hot function can buy lower latency and a smaller binary with C, but the bill arrives as more intricate code and more room for mistakes. The right language depends on whether every nanosecond matters more than the time of the people who will maintain the extension.

This is a narrow microbenchmark, not a universal language contest

The measurements ran on one Linux x86-64 machine with 2 vCPUs and 2 million rows. Each figure is the fastest of 15 runs after subtracting a baseline table scan. That isolates function cost well, but it does not show how the implementations behave with another C library, on another architecture or inside an application doing more work around the database.

The research report was also produced with an LLM. Its credibility comes from published code, 40 hand written tests and 8 million fuzz comparisons with no detected mismatch, not from the origin of the prose. Reproduction on another machine still matters more than a confident conclusion drawn from one table.

Reproduction will show whether the advantage survives another machine

The most useful next signal would be the same benchmark on macOS, ARM and different standard library implementations. Removing the Vec allocation from both Rust variants would also help separate the language cost from the library wrapper cost.

Production imports add another test: malformed value frequency, string length and how often the slow strtod() fallback runs. That is where we will learn whether 19.2 ns survives contact with data that was not designed for a benchmark.

Lilith's verdict

Rust crossed the line ahead of the first C implementation, then optimized C pulled away over 2 million rows. For a function this small, the flag on the language matters less than the parser actually being measured.