Benchmarking von C versus Rust für SQLite-Erweiterungen zur Zahlenanalyse
Simon Willison verglich C- und Rust-Implementierungen einer SQLite parse_number()-Erweiterung und stellte fest, dass optimiertes C 16–19 ns pro Zeile erreichte.
Automatisch aus dem englischen Original übersetzt.
Der Entwickler Simon Willison veröffentlichte am 8. November 2026 einen technischen Benchmark, der die Leistung und Komplexität beim Erstellen einer benutzerdefinierten SQLite-Erweiterung in C im Vergleich zu Rust gegenüberstellte. Die Studie konzentrierte sich auf eine parse_number()-Funktion, die dazu dient, unordentliche String-Daten in numerische Werte umzuwandeln. Dabei zeigte sich, dass eine hochgradig optimierte C-Implementierung in der Rohgeschwindigkeit die Rust-Pendants übertreffen konnte, während sie gleichzeitig einen deutlich kleineren Binary-Footprint beibehielt.
Was passiert ist
Willison startete das Experiment während eines Hundespaziergangs, neugierig auf die praktischen Unterschiede bei der Entwicklung einer SQLite-Erweiterung in C oder Rust. Er nutzte einen KI-Assistenten, um erste Prototypen für beide Sprachen zu generieren, mit dem Ziel, eine Funktion zu erstellen, die Währungssymbole, Kommas und Prozentzeichen aus Strings wie -$1,234.56 oder '12.5%' entfernt. Das Ziel war es, einen gültigen numerischen Wert für Summierung und Sortierung zurückzugeben oder NULL, wenn die Eingabe nicht analysiert werden konnte. Der erste Vergleich zeigte, dass die Rust-Erweiterungen zwar größer in der Dateigröße waren, aber überraschend schneller als die naive C-Implementierung, die auf die strtod-Funktion der Standardbibliothek vertraute.
Um die Leistungslücke zu schließen, wies Willison die KI an, eine benutzerdefinierte C-Analysefunktion mit einem kompakten Fast Path zu schreiben, der die langsameren Aufrufe der Standardbibliothek für die Dezimalverarbeitung umgeht. Diese optimierte C-Version schloss die Lücke und übertraf schließlich die Rust-Implementierungen in der Geschwindigkeit. Die endgültigen Ergebnisse zeigten, dass der neue C-Code gemischte Daten in 19 Nanosekunden pro Zeile verarbeitete, im Vergleich zu 41–46 Nanosekunden für die Rust-Versionen. Das Experiment endete damit, dass die KI einen vollständigen Forschungsbericht erstellte, der für Willisons öffentliches Repository formatiert war und Code, Tests sowie Leistungsmetriken dokumentierte.
Wichtige Details
- Die optimierte C-Erweiterung erzielte eine Verarbeitungszeit von 16–19 Nanosekunden pro Zeile, was etwa doppelt so schnell war wie die Rust-Versionen.
- Die Dateigröße unterschied sich erheblich: Die C-Erweiterung war 14 KB groß, während die Rust-Versionen zwischen 298 KB und 321 KB lagen.
- Ursprünglicher einfacher C-Code war bei Dezimalzahlen langsamer aufgrund der Abhängigkeit von
strtod, aber ein benutzerdefinierter Fast Path löste diesen Engpass. - Alle vier Implementierungen bestanden 8 Millionen Fuzz-Tests, was konsistentes Verhalten und Genauigkeit bei Edge Cases sicherstellt.
- Die Rust-Implementierung unter Verwendung von
rusqlitebenötigte 46 ns für gemischte Daten, währendsqlite-loadable-rs41 ns benötigte. - Die finale C-Implementierung verarbeitete Ganzzahlen in 16 ns und Dezimalzahlen in 18 ns und übertraf damit Rists 32–36 ns bzw. 47–55 ns.
Hintergrund
SQLite ist eine weit verbreitete eingebettete Datenbank-Engine, die Entwicklern ermöglicht, ihre Funktionalität durch benutzerdefinierte Funktionen zu erweitern, die in kompilierten Sprachen wie C oder Rust geschrieben sind. Diese Erweiterungen laufen direkt innerhalb des Datenbankprozesses, was sie ideal für Hochleistungs-Datentransformationsaufgaben wie die Bereinigung importierter Datensätze macht. Bei der Wahl einer Sprache für diese Erweiterungen müssen Entwickler oft die Leichtigkeit der Entwicklung und Speichersicherheit gegen Roh-Ausführungsgeschwindigkeit und Binary-Größe abwägen. C bietet minimalen Overhead und winzige Binaries, erfordert jedoch manuelles Speichermanagement, während Rust starke Sicherheitsgarantien bietet, aber auf Kosten größerer Binaries und möglicherweise anderer Laufzeitcharakteristiken.
Die parse_number()-Funktion adressierte ein häufiges Problem im Data Engineering: die Konvertierung von menschenlesbaren Strings in maschinenlesbare Zahlen. Standard-SQL-Funktionen wie replace() können Zeichen entfernen, sind aber oft langsam, wenn sie zeilenweise auf große Datensätze angewendet werden. Benutzerdefinierte Erweiterungen umgehen einige dieser Overheads, indem sie näher an der Hardware operieren. Allerdings erfordert das Schreiben effizienter Parser in C sorgfältige Optimierung, um die Fallstricke von Allzweck-Konvertierungsfunktionen zu vermeiden, die spezifische Formatierungsbesonderheiten möglicherweise nicht effizient handhaben.
Warum es wichtig ist
Für Teams, die selbst gehostete Datenbanken verwalten oder datenintensive Anwendungen entwickeln, hebt dieser Benchmark die greifbaren Trade-offs zwischen Entwicklungskomfort und betrieblicher Effizienz hervor. Während Rust eine sicherere Entwicklungserfahrung und robuste Tooling bietet, war der Leistungsabzug in diesem spezifischen Micro-Benchmark signifikant. Für High-Volume-Datenimport-Pipelines, wo jede Nanosekunde zählt, kann die Fähigkeit, maximale Leistung aus C herauszuholen, Infrastrukturkosten senken und den Durchsatz verbessern. Dies bringt jedoch die Verantwortung mit sich, Speichersicherheit und potenzielle Bugs manuell zu verwalten.
Die Studie unterstreicht auch die wachsende Rolle von KI-Assistenten in der Low-Level-Systemprogrammierung. Durch die Nutzung von KI zur Generierung und Optimierung komplexen C-Codes können Entwickler Leistungsniveaus erreichen, die andernfalls tiefgreifende Expertise in der Systemprogrammierung erfordern würden. Dies demokratisiert den Zugang zu Hochleistungs-Datenbankerweiterungen und ermöglicht kleineren Teams, benutzerdefinierte Optimierungen ohne Einstellung spezialisierter C-Ingenieure umzusetzen. Es deutet darauf hin, dass die Einstiegshürde für das Schreiben effizienter Datenbank-Plugins sinkt, vorausgesetzt, man weiß, wie man die Tools effektiv steuert.
Was Sie tun können
- Bewerten Sie Ihre aktuellen Datenbereinigungs-Pipelines, um Engpässe zu identifizieren, bei denen benutzerdefinierte SQLite-Erweiterungen langsamere SQL-Funktionen ersetzen könnten.
- Erwägen Sie die Verwendung von C für leistungs-kritische Erweiterungen, wenn Ihr Team über die Expertise verfügt, Speichersicherheit und Teststrenge zu managen.
- Nutzen Sie Rust für Erweiterungen, bei denen Entwicklungsgeschwindigkeit, Speichersicherheit und Wartbarkeit höhere Prioritäten haben als Roh-Leistung auf Nanosekundenebene.
- Implementieren Sie umfassende Fuzz-Tests für jede benutzerdefinierte Analyse-Logik, um sicherzustellen, dass unerwartete oder fehlerhafte Eingaben ordnungsgemäß behandelt werden.
- Benchmarken Sie Ihre spezifischen Use Cases, anstatt sich auf allgemeine Annahmen zu verlassen, da die Leistung je nach Datenverteilung und Hardware variieren kann.
- Nutzen Sie KI-Tools zum Prototyping und Optimieren von Low-Level-Code, überprüfen Sie aber immer die generierten Ausgaben durch strenge Tests und Code Reviews.



