Ist Rust schneller als C?
(steveklabnik.com)- Der Leistungsvergleich zwischen Rust und C ist eine komplexe Frage, deren Antwort davon abhängt, wie die Annahme „alle Bedingungen sind gleich“ definiert wird
- Bei Inline-Assembly können beide Sprachen denselben Assembly-Code erzeugen, daher gibt es keinen Geschwindigkeitsunterschied durch die Sprache selbst
- Beim Speicherlayout von Structs kann Rust durch Neuanordnung von Feldern eine kleinere Größe erreichen, mit dem Attribut
#[repr(C)]ist aber auch ein identisches Layout wie in C möglich - Durch Unterschiede bei Laufzeitprüfungen und dem Verhalten von Entwicklern können sich in realen Projekten Code-Struktur und Performance unterscheiden
- Schlussendlich gibt es keinen Leistungsunterschied aufgrund inhärenter Sprachgrenzen; das Ergebnis hängt von Projekt- und Entwicklerfaktoren ab
Problemstellung und die Unschärfe der Prämissen
- Ausgangspunkt ist die auf Reddit gestellte Frage: „Kann Rust unter gleichen Bedingungen schneller als C sein?“
- Die Formulierung „alle Bedingungen sind gleich“ ist beim Vergleich von Sprachen selbst ein sehr schwer zu definierendes Konzept
- Leistungsvergliche hängen nicht nur von Sprachunterschieden ab, sondern auch von Code-Form, Entwicklungsentscheidungen und Compiler-Optimierungen
Vergleich von Inline-Assembly
- Rust unterstützt Inline-Assembly auf Sprachebene, C dagegen über Compiler-Erweiterungen
- In beiden Sprachen lässt sich dasselbe Beispiel mit dem Befehl
rdtscschreiben - Der von
rustc 1.87.0undclang 20.1.0erzeugte Assembly-Code ist vollständig identisch
- In beiden Sprachen lässt sich dasselbe Beispiel mit dem Befehl
- Dieses Beispiel zeigt keinen Performance-Unterschied der Sprachen, belegt aber, dass Rust Low-Level-Kontrolle auf demselben Niveau wie C bieten kann
Unterschiede im Struct-Layout
- Rust-Structs können die Speichernutzung durch Neuanordnung von Feldern optimieren
- Im Beispiel ist das Rust-Struct 16 Byte groß, das entsprechende C-Struct 24 Byte
- In C muss die Reihenfolge der Felder manuell geändert werden, um auf dieselbe Größe zu kommen
- Mit dem Attribut
#[repr(C)]lässt sich in Rust dasselbe Speicherlayout wie in C erzwingen
Soziale und entwicklerbezogene Faktoren
- Dank der Sicherheitsprüfungen von Rust gibt es Fälle, in denen Entwickler aggressivere Optimierungen versuchen können
- Im Stylo-Projekt von Mozilla scheiterten zwei Parallelisierungsversuche in C++, während die Umsetzung in Rust erfolgreich war
- Selbst im selben Projekt können sich je nach Sprache und Erfahrung der Entwickler Performance und Stabilität des resultierenden Codes unterscheiden
- Da sich das Ergebnis „derselben Aufgabe“ je nach Kenntnisstand von Einsteigern und Experten sowie je nach Sprachbeherrschung unterscheidet, sind einfache Vergleiche schwierig
Compile-Time- und Laufzeitprüfungen
- Viele Sicherheitsprüfungen in Rust werden zur Compile-Time durchgeführt, einige bleiben jedoch Laufzeitprüfungen
- Beim Zugriff auf
array[0]führt Rust beispielsweise eine Bounds-Check-Prüfung durch, C nicht - Mit
get_unchecked()kann man in Rust dasselbe Verhalten wie in C erhalten
- Beim Zugriff auf
- Wenn der Compiler Sicherheit nachweisen kann, können beide Sprachen Prüfungen durch Optimierungen entfernen
- Diese Unterschiede beeinflussen die Art, wie Code geschrieben wird, und können dadurch letztlich Performance-Unterschiede verursachen
Fazit
- Selbst wenn man annimmt, dass C „die schnellste Sprache“ ist, gibt es keinen Grund, warum Rust nicht dasselbe Leistungsniveau erreichen könnte
- Nicht inhärente Sprachgrenzen, sondern Projekteigenschaften, Entwicklerfähigkeiten, Zeitbeschränkungen und andere externe Variablen entscheiden über Leistungsunterschiede
- Die Frage „Ist Rust schneller als C?“ ist daher eher eine Frage des Engineering-Kontexts als des Sprachvergleichs
9 Kommentare
Im Embedded-Bereich wird sogar unter Berücksichtigung der Hardware-Cache-Line-Größe programmiert. Es dürfte letztlich darauf ankommen, wie weit ein Programmierer auf Sprachebene extreme Optimierungen treiben kann und wie gut Standardbibliothek und Compiler performen. Da beide ohnehin Low-Level-Unterstützung bieten, scheint der Unterschied bei einem geringen Overhead wohl minimal zu sein. Deshalb ist das meiner Meinung nach keine besonders sinnvolle Debatte. Wenn eine extreme Optimierung nötig ist, braucht es am Ende ohnehin menschliches Eingreifen. Compiler sind nämlich nicht so perfekt, wie man vielleicht denkt.
Ich denke, Rust wird eher ein Ersatz für C++ als für C sein. C ist praktisch die einzige (vielleicht letzte) Sprache, bei der man den vom Compiler erzeugten Code abschätzen kann …
Zig ist auch nicht schlecht ... T_T
Beim Schreiben ist es irgendwie zu einem KI-Zusammenfassungsstil geworden ;_;
Haben Sie das nicht absichtlich gemacht, hehe?
Das hängt von den Fähigkeiten des Compilers ab.
Wenn man denselben Code assembliert, wird man es sehen.
Die ffmpeg-Leute scheinen wohl zu denken, dass Rust nicht unbedingt schneller als C ist, haha https://www.memorysafety.org/blog/rav1d-perf-bounty/
Hacker-News-Kommentare
Kurz gesagt: Die Maximalgeschwindigkeit ist fast gleich, aber bei echtem Code gibt es große Unterschiede
Vor allem ist Multithreading ein großer Faktor. In Rust müssen alle globalen Variablen thread-sicher sein, egal ob Threads verwendet werden oder nicht, und der Borrow Checker beschränkt Speicherzugriffe darauf, entweder geteilt oder verändernd zu sein
Dadurch ist das Schreiben von Multithreading-Code in Rust fast schon der Standard. In C ist schon das Erzeugen von Threads belastend, etwa wegen Plattformkompatibilität oder Debugging-Risiken
Threads in C zu bauen ist nicht schwer, aber umständlicher als Rusts
std::thread::spawn(move || { ... });Mehr als die Speichersicherheit beeinflusst das Nebenläufigkeitsmodell der Sprache. Go lässt sich auch ohne Speichersicherheit mit
go f()leicht parallelisierenPersönlich habe ich in Go häufiger Heisenbugs gesehen
#pragma omp foreinfach parallelisierenDank Rusts Traits lassen sich schnellere und flexiblere Abstraktionen bauen
In C kann man das mit Makros oder Funktionszeigern nachahmen, aber in Rust kann der Aufrufer zwischen dynamischem Dispatch und statischem Dispatch wählen
In Embedded-Umgebungen zerstören Funktionszeiger den Cache und kosten Leistung, während Rust-Traits Inlining-Optimierungen erlauben und daher deutlich effizienter sind
Egal ob Rust oder C, am Ende arbeitet man auf Byte-Ebene, und inzwischen sind auch Werkzeuge für Binary Patching besser geworden und leicht nutzbar
Box<dyn Trait>in der Funktionssignatur verwendetMit
impl Traitbleibt dem Aufrufer die WahlPersönlich sehe ich Rust, C und C++ fast als dieselbe Familie niedrigstufiger Sprachen, daher halte ich die Leistungsunterschiede für gering
Rusts strenge Aliasing-Regeln sind vorteilhaft für Optimierungen, und UB (Undefined Behavior) in C/C++ existiert zugunsten der Performance
Außerdem sind Generics in Rust und C++ viel mächtiger als in C; etwa ist template-basiertes Sortieren statt
qsort()leichter inline zu optimierenIch halte solche Geschwindigkeitsdebatten zwischen Sprachen meist für sinnlos
Mehr als die Sprache selbst bestimmt die Compiler-Implementierung die Leistung
Rust, C und C++ sind alle niedrigstufige Sprachen, aber wichtig ist, was mit „schnell“ gemeint ist
Geht es um die Höchstgeschwindigkeit von Code, den Experten optimiert haben, oder um die Wahrscheinlichkeit, dass durchschnittliche Entwickler innerhalb des Budgets schnellen Code schreiben? Das ist ein Unterschied
Mit manueller Optimierung verschwinden die Unterschiede zwischen den Sprachen jedoch fast vollständig
Rust hat allerdings einen kleinen Vorteil darin, dass es eine Sprache ist, in der sich schneller Code leichter schreiben lässt
Ich dachte immer, Rusts Vorteile seien Multithreading und Stack-Allokation
Dank des Ownership-Modells kann man mehr auf den Stack legen als in C/C++, was malloc/free-Overhead reduziert
Bei solchen Themen wird oft emotional gestritten, deshalb wollte ich eher Unterschiede in der Denkweise als konkrete Zahlen behandeln
Wenn man über die „Geschwindigkeit“ einer Sprache spricht, sollte man zwei Dinge betrachten
Rust und C sind schneller als Python oder JS, weil es fast keine Laufzeitprüfungen gibt
Rust kann allerdings Aliasing-Informationen besser weitergeben und bietet dadurch mehr Optimierungsspielraum
Im Debug-Modus ist es so langsam wie Ruby, im Release-Modus erreicht es jedoch Geschwindigkeiten auf C-Niveau
Gegenüber C haben C++ oder Rust mehr Compile-Time-Features, sodass sich schneller Code leichter schreiben lässt
Zum Beispiel ist dieser Code in C fast unmöglich
In C braucht man dafür externe Werkzeuge wie re2c
Assembler ist kein Teil des C-Standards, deshalb lässt es sich schwer direkt mit Rust vergleichen; Rust hat eher einen Charakter, der dem GCC-Projekt nähersteht
Ob eine Sprache „schnell“ ist, hängt letztlich von Implementierung und Kontext ab
Mehr als die Geschwindigkeit der Sprache selbst zählt die Kombination aus Compiler und Hardware
Im Durchschnitt weiß ich nicht, welche Sprache am schnellsten ist, aber die Streuung dürfte bei C++ am größten sein.