2 Punkte von GN⁺ 2025-09-12 | Noch keine Kommentare. | Auf WhatsApp teilen
  • Die Paketinstallation von Bun arbeitet im Vergleich zu bestehenden Paketmanagern extrem schnell
  • Der Schlüssel zur schnellen Installation liegt in einem systemprogrammatischen Ansatz und der Minimierung von Systemaufrufen
  • Leistungssteigerungen werden durch detaillierte Strategien wie native Implementierung auf Basis von Zig, die Nutzung von Binärcaches und OS-spezifische Optimierungen erzielt
  • Auch bei der Entpackung von Tarballs und dem Kopieren von Dateien kommen Hochleistungsverfahren zum Einsatz, die Hardware-Eigenschaften ausnutzen
  • Durch Optimierung von Datenstrukturen wie Abhängigkeitsgraphen und Lockfiles werden CPU-Cache-Effizienz und Speicherzugänglichkeit verbessert

Warum Bun Install schnell ist

  • bun install von Bun bietet im Durchschnitt eine 7-fach schnellere Paketinstallation als npm, 4-fach schneller als pnpm und 17-fach schneller als yarn
  • Das liegt nicht nur an Benchmarks, sondern daran, dass das Problem der Paketinstallation aus der Perspektive der Systemprogrammierung statt aus der von JavaScript angegangen wurde
  • Auf mehreren Ebenen werden Leistungsoptimierungen konsequent umgesetzt, darunter die Minimierung von Systemaufrufen, binäres Caching von Manifesten, optimierte Tarball-Extraktion und natives Dateikopieren des Betriebssystems

Grenzen von Node.js und der Architektur von Paketmanagern

  • Seit der Veröffentlichung von Node.js im Jahr 2009 hat sich das asynchrone IO-Modell auf Basis von Event Loop und Threadpool auch auf Paketmanager übertragen
  • Damals war diese Strategie mit asynchronem IO und hoher Frequenz an Systemaufrufen aufgrund der Hardwaregrenzen (langsame Festplatten, langsame Netzwerke) sinnvoll
  • In modernen Systemen sind jedoch NVMe-SSDs, schnelle Netzwerke und leistungsstarke CPUs verbreitet, und der eigentliche Flaschenhals ist nicht IO, sondern der Overhead von Systemaufrufen

Die Kosten von Systemaufrufen und Moduswechseln

  • Wenn ein Programm eine Aufgabe wie das Lesen einer Datei anfordert, muss es von User Mode in den Kernel Mode wechseln, was teure CPU-Zyklen verbraucht (1000–1500 cycles)
  • Paketinstallationen erfordern grundsätzlich zehntausende bis hunderttausende Systemaufrufe, sodass allein die Kosten dieser Umschaltungen bereits mehrere Sekunden CPU-Zeit verbrauchen können
  • Bei der Installation von React und seinen Abhängigkeiten verwendet npm zum Beispiel rund 1 Million, yarn 4 Millionen, pnpm 500.000 und bun 160.000 Systemaufrufe

Unterschiede zwischen bestehenden Paketmanagern und dem Ansatz von Bun

  • npm, pnpm und yarn basieren alle auf Node.js, wodurch JavaScript durch mehrere Abstraktionsschichten ausgeführt werden muss (libuv, Event Loop, Threadpool, Vermittlung von Systemaufrufen)
  • Dabei summieren sich Argumentkonvertierung, Workerpool-Warteschlangen, Verzweigung von Event-Loop-Aufgaben und futex-Systemaufrufe zur Locksynchronisation, sodass am Ende die Verwaltung der Systemaufrufe sogar langsamer wird als das IO selbst
  • Paketmanager, die mit Node.js gebaut sind, können aufgrund dieser strukturellen Grenzen kaum eine tatsächlich native Leistung erreichen

Bun: eine native Installations-Engine in Zig

  • Bun ruft in der Sprache Zig Systemaufrufe direkt auf und lässt damit sowohl die JavaScript-Engine als auch sämtliche Abstraktionsschichten aus
  • Beim Lesen einer Datei wird zum Beispiel direkt aus Zig-Code der Systemaufruf openat() ausgeführt und die Daten sofort zurückgegeben
  • Dadurch kann das Lesen von zehntausenden Dateien ohne separaten Threadpool, Event Loop oder Datenkonvertierung extrem schnell ablaufen
  • In Benchmarks kann Bun 146.057 package.json-Dateien pro Sekunde lesen; Node.js ist mit rund 60.000 mehr als doppelt so langsam

Optimierung von Abhängigkeitsverwaltung und DNS

  • Beim Ausführen von bun install stößt Bun parallel zur Analyse der Abhängigkeiten asynchron DNS-Prefetching an
  • Auf macOS wird dafür zum Beispiel Apples inoffizielle asynchrone DNS-API (getaddrinfo_async_start()) verwendet, was die gleichzeitige Ausführung von Netzwerkaufgaben ohne blockierende Threads ermöglicht
  • Bestehende Paketmanager basieren auf dem libuv-Threadpool, wodurch intern faktisch blockierender Code läuft und Ressourcen verschwendet werden

Binäres Caching von Paketmanifesten

  • npm und andere cachen Manifeste als JSON, Bun hingegen parst sie einmal und speichert das Ergebnis anschließend als Binärformat (.npm-Datei)
  • Dadurch werden String-Duplikate und Parsing-Overhead minimiert; im Arbeitsspeicher ist der direkte Zugriff über Offset-Berechnungen möglich (keine neuen Objekte, kein erneutes Parsing, keine Garbage Collection nötig)
  • Mit ETag- und If-None-Match-Headern werden nur Änderungen geprüft, sodass die Aktualität ohne unnötiges Parsen von Daten verifiziert werden kann
  • In Benchmarks ist eine Installation aus dem Bun-Cache sogar schneller als eine frische Installation mit npm

Performance bei der Verarbeitung von Tarballs

  • Herkömmliche Paketmanager empfangen Tarballs als Stream, wodurch bei unzureichendem Pufferspeicher fortlaufend Reallokationen, Kopien und Größenanpassungen auftreten
  • Bun empfängt zuerst den gesamten Tarball und entpackt ihn dann; über die letzten 4 Bytes von gzip wird die unkomprimierte Größe vorab bestimmt, sodass nur ein einziges Mal Speicher allokiert werden muss
  • Mit libdeflate und ähnlichen Bibliotheken wird schnelles Entpacken ermöglicht und sowohl unnötige Kopien als auch Größenanpassungen vollständig vermieden

Abhängigkeitsgraphen und Optimierung von Datenstrukturen

  • Bestehende Paketmanager erzeugen Abhängigkeitsbäume auf Basis von JavaScript-Objekten und Pointern, was zu zufälliger Verteilung im Speicher und häufigen CPU-Cache-Misses führt (Problem des Pointer Chasing)
  • Bun verwendet das Muster Structure of Arrays (SoA) und speichert alle Pakete, Strings und Abhängigkeiten in großen zusammenhängenden Speicherblöcken
    • Durch den Zugriff auf Basis von Offsets und Längen kann die CPU mehrere Pakete gleichzeitig in Cache-Line-Einheiten lesen (cachefreundliche Struktur)
    • Auch das Lockfile wird statt als JSON/YAML passend zum SoA-Muster so gespeichert, dass String-Deduplizierung und sequenzieller Speicherzugriff leicht möglich sind
  • Das Binärformat des Lockfiles (bun.lockb) wurde ebenfalls experimentell eingeführt, wegen der schlechteren Zusammenarbeit über Git jedoch durch ein besser lesbares Plain-Format ersetzt

OS-spezifische Optimierung beim Kopieren von Dateien

macOS

  • Verwendung von clonefile: Ein Verzeichnis wird als Ganzes per Copy-on-Write mit einem einzigen Systemaufruf dupliziert
  • Dadurch wird die doppelte Nutzung von Speicherplatz auf dem Datenträger minimiert und die Installationsgeschwindigkeit maximiert
  • Falls clonefile fehlschlägt, erfolgt ein stufenweiser Fallback über per-directory cloning zu copyfile

Linux

  • Zuerst wird ein Hardlink versucht: Es wird keine neue Datei erzeugt, sondern nur eine neue Referenz auf die bestehende Datei angelegt (ohne Verschiebung von Datenträgerdaten)
  • Wenn Hardlinks nicht möglich sind, wird auf Btrfs/XFS per ioctl_ficlone Copy-on-Write verwendet
  • Danach folgen als Fallback copy_file_range, sendfile und zuletzt normales copyfile

Gesamtfazit

  • Bun überschreitet durch Minimierung von Systemaufrufen, binäre Strukturen, OS-Optimierung und verbesserte Datenstrukturen die traditionellen Leistungsgrenzen von Paketmanagern
  • Dadurch werden neben extrem schnellen Installationen auch Speicher- und CPU-Effizienz verbessert
  • Im Vergleich zu bestehenden Node.js-basierten Managern kann es ohne Austausch der Laufzeit direkt in Projekten eingesetzt werden (Kompatibilität bleibt erhalten)
  • In realen großen Codebasen verkürzt es Installationsvorgänge, die früher mehrere Minuten dauerten, auf wenige Millisekunden bis einige Sekunden
  • Als gelungenes Beispiel für maßgeschneiderte Optimierung auf System-, Hardware- und OS-Ebene hat es hohen Forschungs- und Referenzwert

Noch keine Kommentare.

Noch keine Kommentare.