1 Punkte von GN⁺ 2025-03-19 | 1 Kommentare | Auf WhatsApp teilen
  • Wenn man das von Ubuntu bereitgestellte Quellpaket von jq selbst baut, kann sich die Leistung um bis zu 90 % verbessern
  • Durch Verbesserungen bei Compiler, Optimierungs-Flags und Speicher-Allocatoren lässt sich die Performance maximieren

Setup

  • jq wird zur Verarbeitung von GeoJSON-Dateien im JSON-Format verwendet
    • Es wird eine Abfrage ausgeführt, die aus der 500 MB großen Parcel-Map des Alameda County Assessor für alle Parcels ab einem bestimmten Wert den Stadtnamen ausgibt
  • Auf einem Ryzen-9-9950X-System dauert dies mit gecachten Dateien etwa 5 Sekunden, daher wurde versucht, das zu verbessern

Schritt 1: Paket neu bauen

  • Quellcode von jq von Launchpad herunterladen und ohne zusätzliche Flags neu bauen
  • Ergebnis: 2–4 % Leistungsverbesserung
  • Benchmark-Ergebnisse
    • Gebautes jq: durchschnittlich 4,517 Sekunden
    • Standardpaket von Ubuntu: durchschnittlich 4,641 Sekunden
    • Leistungsverbesserung: 1,03-fach schneller

Schritt 2: Clang und erweiterte Optimierungs-Flags verwenden

  • Mit Clang-18 kompilieren und Optimierungsstufe sowie LTO verwenden
  • Verwendete Haupt-Flags:
    • -O3 → höhere Optimierungsstufe
    • -flto → Link-Time Optimization aktivieren
    • -DNDEBUG → Debug-Code ausschließen
  • Benchmark-Ergebnisse
    • Gebautes jq: durchschnittlich 3,853 Sekunden
    • Standardpaket von Ubuntu: durchschnittlich 4,631 Sekunden
    • Leistungsverbesserung: 1,20-fach schneller

Schritt 3: TCMalloc hinzufügen

  • Statt des Standard-malloc von GNU libc wird TCMalloc verwendet
  • Mit zusätzlichem -L/usr/lib/x86_64-linux-gnu -ltcmalloc_minimal bauen
  • Benchmark-Ergebnisse
    • Gebautes jq: durchschnittlich 3,253 Sekunden
    • Standardpaket von Ubuntu: durchschnittlich 4,611 Sekunden
    • Leistungsverbesserung: 1,42-fach schneller

Schritt 4: Dynamisches Preload von TCMalloc anwenden

  • Im Ubuntu-Standardpaket wird TCMalloc per dynamischem Preload verwendet
  • Benchmark-Ergebnisse
    • Standard-jq: durchschnittlich 4,601 Sekunden
    • jq mit TCMalloc: durchschnittlich 4,082 Sekunden
    • Leistungsverbesserung: 1,13-fach schneller

Schritt 5: Dynamisches Preload anderer Allocatoren testen

  • Test von jemalloc und mimalloc, zwei weiteren von Ubuntu bereitgestellten Speicher-Allocatoren
  • mimalloc liefert die beste Leistung
  • Benchmark-Ergebnisse
    • Standard-jq: durchschnittlich 4,123 Sekunden
    • jq mit TCMalloc: durchschnittlich 4,130 Sekunden
    • jq mit Jemalloc: durchschnittlich 3,510 Sekunden
    • jq mit Mimalloc: durchschnittlich 3,154 Sekunden → 1,31-fache Leistungsverbesserung

Schritt 6: Direkt mit mimalloc bauen

  • mimalloc wird nicht per dynamischem Preload, sondern statisch gelinkt
  • Maximale Leistung
  • Benchmark-Ergebnisse
    • Gebautes jq: durchschnittlich 2,428 Sekunden
    • Standardpaket von Ubuntu: durchschnittlich 4,606 Sekunden
    • Leistungsverbesserung: 1,90-fach schneller

🚀 Endergebnis

  • Das selbst gebaute jq ist 90 % schneller als das Ubuntu-Paket
  • Leistung bei der Verarbeitung von 13.000 JSON-Dateien mit 2,2 GB:
    • Gebautes jq: 0,755 Sekunden
    • Standard-jq: 1,424 Sekunden
    • Leistungsverbesserung: etwa 2-fach

1 Kommentare

 
GN⁺ 2025-03-19
Hacker-News-Kommentare
  • Der Titel „Ubuntu-Pakete neu bauen und durch Wechsel des Speicher-Allocators 90 % schneller machen“ wirkt wie Clickbait

    • Es geht nur um ein einziges Paket, und ein Teil der Leistungssteigerung wurde nicht durch Neukompilierung erreicht
    • Ich habe schon einmal jemalloc per Preload verwendet, um die malloc-Implementierung auszutauschen, und damit positive Ergebnisse bei der Stabilisierung der Speichernutzung erzielt
    • Das hat ein Speicherleck-Problem gelöst; wahrscheinlicher war es jedoch ein Problem der Speicherfragmentierung und kein Problem der Anwendung selbst
  • Engineering ist die Kunst des Kompromisses

    • Der Artikel erklärt, dass der Großteil der Leistungssteigerung durch die Spezialisierung des Speicher-Allocators erzielt wurde
    • Bei Multithread-Projekten ist die Wahl des Allocators wichtig; was in einem Projekt zu mehr Geschwindigkeit führt, kann in einem anderen Abstürze verursachen
    • Auch die Reallokationsstrategie muss berücksichtigt werden, und man muss zwischen langfristiger Stabilität und kurzfristiger Geschwindigkeit wählen
    • Bei der Entwicklung eines Videoeditors habe ich mit verschiedenen Allocators experimentiert und festgestellt, dass der glibc-Allocator langfristige Stabilität bietet
  • Gentoo Linux ist ein Betriebssystem, das darauf ausgelegt ist, für den jeweiligen Einsatzzweck des Nutzers optimiert zu werden

    • Nach der anfänglichen Einrichtung ist es einfach zu benutzen, und ich erinnere mich daran, im Gentoo-Linux-Channel viele Freunde gefunden zu haben
    • Frühe ChromeOS-Versionen waren im Grunde angepasste Gentoo-Linux-Installationen
  • Wenn man Pakete wie jq manuell installiert, kann man Sicherheitsupdates verpassen

    • Beispielsweise gab es Sicherheitsupdates für onigurama, und wenn so etwas erneut passiert, könnte man verwundbar sein
    • Es gab Fälle, in denen mehrere Sicherheitslücken wie CVE-2017-9224 behoben wurden
  • Die Verwendung eines inoffiziellen malloc kann seltsame Bugs verursachen

    • Sobald man über die von den Entwicklern verwendeten Flags hinausgeht, steigt die Wahrscheinlichkeit von Problemen
  • Wenn ich lese, dass man mit einer einfachen Änderung einen großen Geschwindigkeitsschub erzielen kann, möchte ich den Entwicklern von jq davon erzählen

    • Der Artikel scheint diese Option nicht in Betracht gezogen zu haben, und auch in den Kommentaren wird sie nicht erwähnt
  • Es kann sinnvoll sein, Pakete aus dem Quellcode zu kompilieren oder offizielle Binärdateien herunterzuladen

    • Es war schwierig, Updates für manuell installierte und aus dem Quellcode kompilierte Pakete zu verfolgen, deshalb habe ich ein Tool entwickelt, um dieses Problem zu lösen
  • Die Rust-Funktion cargo install ist nützlich, weil sie Optimierungen für eine bestimmte Plattform ermöglicht

    • jaq und yq sind Optionen, die ich häufig verwende, um bei der Nutzung von jq Leistungsverbesserungen zu erzielen
  • Nach dem Wechsel des Speicher-Allocators kann man durch Neubauen von Ubuntu-Paketen 90 % mehr Geschwindigkeit erreichen

    • Wahrscheinlich funktioniert das auch unter Debian und RedHat
    • Zuerst dachte ich, es sei ein Artikel darüber, Ubuntu in Linux From Scratch zu verwandeln