Lilith.
⌕
Redaktionelle Illustration: Optimiertes C drückte parse_number() auf 19 ns. Rust gewann nur die erste Runde
Illustration von Lilith · redaktioneller Remix

Simon Willison ließ Claude Opus 5.5 vier Varianten einer SQLite Funktion namens parse_number() erstellen, die Text wie -$1,234.56 in eine Zahl umwandelt. Wertvoll ist das Experiment vor allem deshalb, weil er beim ersten überraschenden Ergebnis nicht stehen blieb, sondern dessen Ursache untersuchte.

Rust schlug einfaches C, bis C den allgemeinen Parser umging

Der Test umfasste zwei Implementierungen in Rust und zwei in C. Einfaches C verarbeitete die gemischten Daten in 67,3 ns pro Zeile, während Rust 40,5 bis 45,9 ns benötigte. Bei Dezimalzahlen wuchs der Abstand auf 106,3 ns gegenüber 46,5 bis 54,5 ns.

Die Ursache lag nicht in der Sprache als Ganzes. Die C Variante übergab Dezimalzahlen an die allgemeine Funktion strtod(), während Rust einen schnelleren Pfad seines Parsers verwendete. Rund 30 zusätzliche Zeilen für häufige Werte brachten das optimierte C auf 19,2 ns bei gemischten Daten, 15,8 ns bei Ganzzahlen und 17,5 ns bei Dezimalzahlen.

Eine kleine Funktion legte Abstraktionskosten und Handarbeit offen

Die C Erweiterung war 14 408 B groß. Die Rust Varianten belegten 298 464 B und 320 864 B, weil sie die Standardbibliothek und weiteren Hilfscode mitbrachten. Optimiertes C erforderte zugleich 104 Codezeilen, manuelle Pufferverwaltung, Überlaufprüfungen und Sorgfalt bei Gleitkommaberechnungen.

Die Rust Versionen waren kürzer und sicherer zu schreiben. Für Entwickler ist die Abwägung konkret: Eine kleine, häufig aufgerufene Funktion kann mit C geringere Latenz und eine kleinere Binärdatei erreichen, bezahlt dafür aber mit komplizierterem Code und mehr Fehlermöglichkeiten. Entscheidend ist, ob jede Nanosekunde oder die Zeit der späteren Maintainer schwerer wiegt.

Der Test ist eng begrenzt und kein allgemeines Sprachduell

Gemessen wurde auf einer einzigen Linux x86-64 Maschine mit 2 vCPUs und 2 Millionen Zeilen. Jeder Wert ist das beste Ergebnis aus 15 Läufen nach Abzug eines einfachen Tabellenscans. Das isoliert die Funktionskosten gut, zeigt jedoch nicht das Verhalten mit einer anderen C Bibliothek, auf einer anderen Architektur oder in einer Anwendung, die rund um die Datenbank weitere Arbeit erledigt.

Auch der Forschungsbericht entstand mit Unterstützung eines LLM. Glaubwürdig machen ihn der veröffentlichte Code, 40 handgeschriebene Tests und 8 Millionen Fuzz Vergleiche ohne festgestellte Abweichung, nicht die Herkunft des Textes. Eine Reproduktion auf einem anderen Rechner bleibt aussagekräftiger als ein selbstsicheres Fazit aus einer Tabelle.

Reproduktion zeigt, ob der Vorsprung einen Rechnerwechsel übersteht

Das nützlichste nächste Signal wäre derselbe Benchmark auf macOS, ARM und mit anderen Implementierungen der Standardbibliothek. Zusätzlich sollte die Vec Allokation aus beiden Rust Varianten entfernt werden, um die Kosten der Sprache von denen des Bibliothekswrappers zu trennen.

Reale Importdaten liefern eine weitere Probe: Häufigkeit fehlerhafter Werte, Länge der Zeichenketten und Nutzung des langsamen Rückfalls auf strtod(). Dort zeigt sich, ob 19,2 ns den Kontakt mit Daten überstehen, die nicht für einen Benchmark gebaut wurden.

Liliths Urteil

Rust kam vor der ersten C Implementierung ins Ziel, doch optimiertes C zog über 2 Millionen Zeilen davon. Bei einer so kleinen Funktion entscheidet der gemessene Parser, nicht die Flagge der Sprache.