2026-11-08 · ← News
Optimiertes C drückte parse_number() auf 19 ns. Rust gewann nur die erste Runde
Simon Willison verglich vier SQLite Erweiterungen zum Parsen von Zahlenketten. Rust schlug die einfache C Variante, doch rund 30 Zeilen Optimierung brachten C auf 19,2 ns pro Zeile und zeigten, dass ein Sprachbenchmark oft vor allem die Wahl des Parsers misst.
Das Bild konnte nicht geladen werden.
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.