6 Punkte von GN⁺ 3 시간 전 | 1 Kommentare | Auf WhatsApp teilen
  • libzstd-rs-sys ist nach zlib und bzip2 das dritte Kompressionsprojekt der Trifecta Foundation und die erste Rust-basierte Veröffentlichung von zstd
  • Zstd ist ein auf moderne CPUs abgestimmtes Kompressionsformat, das schneller als gzip ist und eine höhere Kompressionsrate bietet; es dürfte gzip im Web-Traffic schrittweise ersetzen
  • Das bisherige Rust-zstd-Crate kompiliert C-Code aus dem Quelltext und benötigt daher eine C-Toolchain sowie Target-Support, was die Einrichtung für Windows und WebAssembly erschweren kann
  • Die Rust-Implementierung kann als Drop-in-kompatible C-Bibliothek kompiliert werden; Testsuite, Fuzz-Tests und Miri werden genutzt, um eine Alternative zur C-Referenzimplementierung zu verifizieren
  • Die Standard-Dekomprimierung ist einige Prozent langsamer als in C, doch der Leistungsverlust von etwa 3 % ist der Preis für Speichersicherheit; mit einem experimentellen Flag lässt sich C-Niveau erreichen

Erste Veröffentlichung und Bedeutung der Rust-Implementierung

  • Die Trifecta Tech Foundation hat mit libzstd-rs-sys die erste Veröffentlichung ihres dritten Kompressionsprojekts angekündigt, das nach zlib und bzip2 nun zstd behandelt
  • Zstd ist ein Kompressionsformat, das mit Blick auf moderne CPUs entwickelt wurde und deutlich schneller als gzip ist sowie eine bessere Kompressionsrate erreichen kann
  • zstd ist bereits weit verbreitet und dürfte gzip im Web-Traffic schrittweise ersetzen
  • In Rust lässt sich zstd bereits über das zstd-Crate verwenden, doch das bisherige Crate kompiliert C-Code aus dem Quelltext und benötigt deshalb eine C-Toolchain sowie Target-Support für das jeweilige Ziel
  • Da die Einrichtung einer C-Toolchain für Windows oder WebAssembly schwierig sein kann, bietet eine reine Rust-Implementierung Rust-Entwicklern eine bessere Erfahrung bei der Nutzung von Abhängigkeiten
  • libzstd-rs-sys kann wie die Arbeiten an zlib und bzip2 als Drop-in-kompatible C-Bibliothek kompiliert werden und zielt darauf ab, eine Alternative zur C-Referenzimplementierung zu sein
  • Die C-Referenzimplementierung wird von Meta gepflegt und erfordert bei Beiträgen eine Vereinbarung zwischen Meta und den Mitwirkenden; eine unabhängige, leistungsfähige und kompatible Implementierung kann daher das Open-Source-Ökosystem stärken

Verifizierung, Leistung und verbleibende Arbeiten

  • Die anfängliche Referenzimplementierung wurde mit c2rust konvertiert; anschließend wurden Aufräumarbeiten an der Dekomprimierung und am Dictionary-Builder abgeschlossen
  • Der Rust-Code wird zu einer statischen C-Bibliothek kompiliert und danach mit der Testsuite der Referenzimplementierung verifiziert
  • Fuzz-Tests und Miri werden ebenfalls genutzt, um die Korrektheit der Implementierung zu überprüfen
  • Eine Prerelease ist unter libzstd-rs-sys v0.0.1-prerelease.2 verfügbar
  • Preis der Speichersicherheit

    • Die Standardleistung bei der Dekomprimierung ist einige Prozent langsamer als die C-Referenzimplementierung
    • Jede Änderung, die in main gemergt wird, wird in der Benchmark-Suite gemessen
    • Mit aktiviertem Feature-Flag unsafe-performance-experimental wird C-Leistung erreicht
    • Dieses Flag deaktiviert an vier Stellen Bounds-Checks, an denen Eingabedaten für die Indizierung von Datenstrukturen verwendet werden
    • Für die meisten Nutzer dürfte ein Leistungsverlust von etwa 3 % ein akzeptabler Preis für die verbesserte Speichersicherheit sein
    • Wer die letzte Leistungsreserve benötigt, kann das Risiko in Kauf nehmen und dieses Flag aktivieren; das Verhalten an diesen vier Stellen entspricht dann dem von C ohne Bounds-Checks
  • Implementierung der Komprimierung und Integration ins Ökosystem

    • Für den Komprimierungsteil wird noch Finanzierung gesucht
    • Da es Code-Sharing zwischen Komprimierung und Dekomprimierung gibt, wurde bereits ein Teil des Komprimierungscodes geprüft, doch der Großteil der Aufräumarbeiten steht noch aus
    • Benchmarks zur Vermeidung von Regressionen bei der Komprimierungsleistung wurden eingerichtet, und mit der Testsuite der Referenzimplementierung wird geprüft, ob korrekte Ergebnisse erzeugt werden
    • Die verbleibenden Arbeiten sind unter Milestone 4: Encoder implementation zusammengefasst
    • Es gibt einen zstd-Fork, der libzstd-rs-sys anstelle der C-Bibliothek verwendet; künftig soll dies gern Upstream übernommen werden
    • Bei den am häufigsten genutzten APIs ist die Integration vergleichsweise einfach
    • Beim Feature experimental gibt es eine Unstimmigkeit: zstd-safe verwendet ein Enum, für FFI-Sicherheit müsste jedoch ein struct verwendet werden
  • Förderung

1 Kommentare

 
GN⁺ 3 시간 전
Lobste.rs-Kommentare
  • Das sind wirklich erfreuliche Neuigkeiten. Vor ein paar Tagen musste ich wegen einer Abhängigkeit libc-dev dazuholen, um zstd zu bauen, und ich habe mich gefragt, ob das schon einmal jemand ernsthaft in Rust neu implementiert hat.
    Hoffentlich wird das in der Community breit angenommen.

  • Ich arbeite an einem Rust-basierten Projekt auf Grundlage von WireGuard und ziehe dafür mehrere Rust-Kryptobibliotheken heran. Speichersicherheit ist zwar ein klarer Vorteil, aber anders als alte C-Bibliotheken wurden sie nicht alle sicherheitsauditiert.
    Am Ende frage ich mich, ob es den Preis wirklich wert ist, solche Algorithmen in Rust neu zu schreiben.

    • Es ist absolut den Aufwand wert. Kryptografie wird viel häufiger wegen Implementierungsfehlern umgangen als dadurch, dass der Algorithmus selbst gebrochen wird.
      Damit ein System sicher ist, muss der „langweilige“ nicht-kryptografische Code für Parsing, Protokollzustand und Buffer-Management korrekt funktionieren. Wenn ein Angreifer ein magisches Paket schicken kann, das beliebige Codeausführung ermöglicht, beschäftigt er sich nicht mit hochentwickelter Kryptoanalyse oder Timing-Seitenkanälen, die eine Entschlüsselung von Tausenden Jahren auf Jahrzehnte verkürzen würden.
    • Wenn die Performance-Anforderungen des Projekts nicht extrem eng sind und man ein wenig FFI in Kauf nehmen kann, kann man aus Rust auch den Go-Kryptostack verwenden.
      Beide Seiten sind speichersicher, und man hat den Vorteil, dass es nur eine schmale unsafe-FFI-Grenze gibt. Go-Kryptobibliotheken sind derzeit reifer als das, was Rust beziehungsweise genauer das crates.io-Ökosystem bietet.
  • Ich frage mich, ob jemand eine dokumentierte Erklärung dafür kennt, wann man die Suffixe -sys und -rs-sys verwenden sollte. Mein Bauchgefühl war, dass -sys für Crates gedacht ist, die nicht in Rust geschriebene Systembibliotheken kapseln.
    In diesem Rahmen ergibt das Suffix -rs-sys aber wenig Sinn, also lag ich mit meinem Bauchgefühl wohl falsch. Kennt jemand eine maßgebliche Quelle?

    • Dieser Paketname ist so ziemlich einer der schlechtesten, die man sich vorstellen kann. Drei der vier Bestandteile des Namens sind falsch oder unerwünscht.
      *-sys: eine alte und weit verbreitete Konvention, erklärt unter https://doc.rust-lang.org/cargo/reference/…. Auf diese Crate passt sie aber überhaupt nicht.
      lib*: Dass es sich um eine Bibliothek handelt, wissen wir bereits; in Rust ist dieses Präfix nicht üblich, und auch im obigen Link wird halb indirekt darauf hingewiesen, es zusammen mit *-sys zu vermeiden. Bei libzstd-sys könnte man erwarten, dass es gegen liblibzstd linkt. Nebenbei habe ich auch unabhängig von Rust schon echte doppelte lib-Namen gesehen.
      *-rs: Wie unter https://rust-lang.github.io/api-guidelines/naming.html gesagt wird: „Jede Crate ist Rust! Man muss die Benutzer nicht ständig daran erinnern.“
    • -sys-Crates legen normalerweise eine sehr rohe C-artige Schnittstelle offen, enthalten intern viel unsafe Code und bauen oder linken meist gegen externe Bibliotheken.
      Den Namen -rs-sys habe ich weniger konsistent verwendet gesehen. Er scheint für Bibliotheken verwendet zu werden, die externen Code bauen, der innerhalb einer Rust-Crate neu verpackt ist, etwa für unvollständige Rust-Implementierungen, in denen noch C-Anteile stecken, oder für Rust-Unterstützungscode zu sys-Crates.
    • Es gibt schon eine gewisse Logik.
      libzstd ist der ursprüngliche Name. C-Bibliotheken neigen dazu, lib im Namen zu tragen, und statt ihn an Rust/Cargo-Konventionen anzupassen, wurde er zur Referenz beibehalten.
      -rs kennzeichnet die Rust-Neuschreibung zur Unterscheidung von der C-Implementierung von Facebook. Das ist ein allgemeines Suffix, das in vielen Rust-Projekten üblich ist, ähnlich wie Python-Bibliotheken Namen wie pysomething verwenden.
      -sys deshalb, weil diese Implementierung ein Drop-in-Ersatz ist, der eine unsafe-C-API offenlegt. Aus Sicht von Cargo-Nutzern ist es keine Rust-Bibliothek. Es gibt keine Rust-Schnittstelle; man ruft sie wie C-Code mit C-Funktionen auf.
      Es ist also nicht -rs-sys, sondern die -sys-Version von libzstd-rs.
  • Warum sollte man das statt ruzstd wählen? Wäre es nicht besser, in die bestehende Crate zu investieren?

    • In ruzstd ist die Kompression noch nicht vollständig implementiert.
      Ein 1:1-Port ermöglicht es, mit einer relativ geradlinigen Code-Transformation ähnliche Geschwindigkeit und Funktionsparität zu erreichen, statt auf einer anderen Codebasis erst wieder herausfinden zu müssen, wie man einen ebenso schnellen und vollständigen Kompressor baut.
  • „Die C-Referenzimplementierung wird von Meta gepflegt, und um beizutragen, muss man mit Meta eine Contributor Agreement unterzeichnen.“
    Ich habe kürzlich etwas Interessantes erfahren: Auch die von Facebook gepflegte zstd-Referenzimplementierung wird mit LLMs programmiert und ist zudem eine Abhängigkeit von openssl; deshalb begrüße ich es uneingeschränkt, wenn es mehr Alternativen gibt.

  • „In der Standardkonfiguration ist die Dekompressionsleistung unserer Implementierung ein paar Prozent langsamer als die C-Referenzimplementierung.“
    Das ist alles, was man über dieses Projekt wissen muss.

    • Könntest du ein wenig genauer erklären, wie dieser Satz bei dir angekommen ist?
      Direkt nach dem zitierten Satz folgt nämlich dieser Abschnitt:
      „Wenn man allerdings das Feature-Flag unsafe-performance-experimental aktiviert, erreicht man die gleiche Performance wie in C. Daher halten wir diesen Performanceverlust für vertretbar. Dieses Flag deaktiviert 4 Bounds-Checks an vier Stellen, an denen die Eingabedaten für die Indizierung von Datenstrukturen verwendet werden. Für die meisten Nutzer sind etwa 3 % Performanceverlust wahrscheinlich ein akzeptabler Preis für mehr Speichersicherheit. Wer wirklich auch das letzte bisschen Performance braucht, kann dieses Flag auf eigene Verantwortung aktivieren. Das Verhalten an diesen vier Stellen entspricht dann dem von C ohne Bounds-Checks, und es scheint in vielen Produktionssystemen problemlos zu funktionieren.“