1 Punkte von GN⁺ 2025-03-19 | 1 Kommentare | Auf WhatsApp teilen
  • Wird derselbe jq-Quellcode neu gebaut und der Allocator ausgetauscht, sinkt die Verarbeitungszeit für 500 MB GeoJSON von 4,606 Sekunden auf 2,428 Sekunden; damit ist der Build 1,90-mal schneller als das Ubuntu-Binary
  • Der Benchmark wurde auf einem Ryzen 9 9950X mit der Parcel Map des Alameda County Assessor durchgeführt; extrahiert wurde SitusCity für die Bedingung TotalNetValue < 193000
  • Schon ein einfacher Rebuild brachte 2–4 % Verbesserung; die Kombination aus clang-18, -O3, -flto und -DNDEBUG erreichte gegenüber dem Ubuntu-Paket 1,20-fache Performance
  • Im Profil zeigte sich, dass Kosten für Speicherallokationen stark ins Gewicht fallen; verglichen wurden TCMalloc, jemalloc und mimalloc, wobei mimalloc in LD_PRELOAD-Experimenten am schnellsten war
  • Der finale mit mimalloc gelinkte Build erzielte auch in einem separaten Fall mit 2,2 GB JSON 0,755 Sekunden gegenüber 1,424 Sekunden; je nach Workload können sich also große Unterschiede zum Standard-Build der Distribution ergeben

Referenz-Workload und Messmethode

  • Testobjekt ist das JSON-Verarbeitungstool jq; als Eingabedaten dient eine 500-MB-GeoJSON-Datei mit der Parcel Map des Alameda County Assessor
  • Die ausgeführte Query gibt aus der Parcel-Liste SitusCity für Einträge aus, die TotalNetValue < 193000 erfüllen
    • .features[] | select(.properties.TotalNetValue < 193000) | .properties.SitusCity
  • Das standardmäßige Ubuntu-/usr/bin/jq benötigte bei gecachter Datei etwa 5 Sekunden; die detaillierten Benchmarks wurden wiederholt mit hyperfine gemessen
  • Um Schwankungen während der Ausführung zu reduzieren, wurde der Prozess mit taskset -c 2 auf logische CPU 2 fixiert
    • Diese Einstellung soll Einflüsse durch System-Interrupts auf CPU 0 und CPU-Migration vermeiden

Einfacher Rebuild desselben Quellcodes

  • Der von Ubuntu verwendete jq-Quellcode wurde heruntergeladen und ohne zusätzliche Flags per configure und Build neu gebaut
  • Allein dieser einfache Rebuild war etwa 2–4 % schneller als das Ubuntu-Binary-Paket
    • Rebuild-Binary: durchschnittlich 4,517 Sekunden
    • Ubuntu-/usr/bin/jq: durchschnittlich 4,641 Sekunden
    • Daraus ergibt sich etwa 1,03-fache Performance

Einsatz von clang und Optimierungs-Flags

  • Im nächsten Schritt wurden clang-18, ein höheres Optimierungsniveau, LTO sowie Flags im Zusammenhang mit Debugging und Profiling gemeinsam eingesetzt
  • Die wichtigsten performancewirksamen Flags waren -O3, -flto und -DNDEBUG
    • -O3 nutzt ein höheres Optimierungsniveau als -O2
    • -flto aktiviert Link-Time Optimization
    • -DNDEBUG reduziert die im Profil deutlich sichtbaren Assertion-Kosten
  • Ein Beispiel für die verwendete configure-Konfiguration sieht wie folgt aus
    • CC=clang-18
    • LDFLAGS="-flto -g -Wl,--emit-relocs -Wl,-z,now -Wl,--gc-sections -fuse-ld=lld"
    • CFLAGS="-flto -DNDEBUG -fno-omit-frame-pointer -gmlt -march=native -O3 -mno-omit-leaf-frame-pointer -ffunction-sections -fdata-sections"
  • Dieser Build war 1,20-mal schneller als das Ubuntu-Binary
    • Optimierter Rebuild: durchschnittlich 3,853 Sekunden
    • Ubuntu-/usr/bin/jq: durchschnittlich 4,631 Sekunden

Experimente mit Austausch des Allocators

  • jq ist ein komplexes C-Programm, und im Profil zeigte sich Speicherallokation als größter Kostenfaktor
  • Zunächst wurde mit dem als Ubuntu-Paket bereitgestellten TCMalloc gelinkt und neu gebaut
    • Zu LDFLAGS wurde -L/usr/lib/x86_64-linux-gnu -ltcmalloc_minimal hinzugefügt
    • Rebuild-Binary: durchschnittlich 3,253 Sekunden
    • Ubuntu-/usr/bin/jq: durchschnittlich 4,611 Sekunden
    • Das Ergebnis ist 1,42-mal schneller als das Ubuntu-Binary
  • Auch beim unveränderten Ubuntu-Binary lässt sich durch reinen Austausch des Allocators per LD_PRELOAD eine gewisse Verbesserung erzielen
    • Standard: durchschnittlich 4,601 Sekunden
    • TCMalloc per Preload: durchschnittlich 4,082 Sekunden
    • Gegenüber dem Standard 1,13-mal schneller

Dynamisches Preloading und THP-Konfiguration

  • Die von Ubuntu bereitgestellten jemalloc, mimalloc und TCMalloc wurden per LD_PRELOAD verglichen
  • Dieser Vergleich wurde mit den folgenden gesetzten Umgebungsvariablen durchgeführt
    • MIMALLOC_LARGE_OS_PAGES=1
    • MALLOC_CONF="thp:always,metadata_thp:always"
    • GLIBC_TUNABLES=glibc.malloc.hugetlb=1
  • Im Ergebnis war mimalloc am schnellsten
    • Standard-glibc: durchschnittlich 4,123 Sekunden
    • TCMalloc per Preload: durchschnittlich 4,130 Sekunden
    • jemalloc per Preload: durchschnittlich 3,510 Sekunden
    • mimalloc per Preload: durchschnittlich 3,154 Sekunden
  • Aktiviertes THP bringt sowohl dem glibc-Allocator als auch jemalloc und mimalloc Vorteile
  • THP + mimalloc ist 31 % schneller als THP + glibc und 48 % schneller als die glibc-Standardeinstellung

Endergebnis des mit mimalloc gelinkten Builds

  • Da dynamisches Preloading aus Performance-Sicht nicht als ideal betrachtet wurde, wurde jq im letzten Schritt mit mimalloc gelinkt und neu gebaut
  • Der finale Build war 1,90-mal schneller als das Ubuntu-Binary-Paket
    • mimalloc-Rebuild: durchschnittlich 2,428 Sekunden
    • Ubuntu-/usr/bin/jq: durchschnittlich 4,606 Sekunden
    • Jeder Benchmark basiert auf 10 Durchläufen
  • Derselbe Build wurde auch in einer anderen Anwendung verwendet
    • Verarbeitet wurden 2,2 GB JSON in 13.000 Dateien
    • Für die Parallelisierung kam rush zum Einsatz
    • jq als mimalloc-Rebuild: 0,755 Sekunden
    • jq aus dem Ubuntu-Paket: 1,424 Sekunden
  • Auch in diesem separaten Fall liegt die Beschleunigung nahezu bei

1 Kommentare

 
GN⁺ 2025-03-19
Hacker-News-Meinungen
  • Clickbait wie „Ein Ubuntu-Paket neu bauen und durch Austausch des Memory Allocators 90 % schneller machen“ ist so schlimm, dass man am liebsten einen Schlag über TCP/IP hinweg austeilen würde. Es war genau ein Paket, und ein Teil der Verbesserungen lag nicht einmal am Neukompilieren.
    Trotzdem habe ich einmal jemalloc per LD_PRELOAD in ein Programm eingeschleust, um die malloc-Implementierung auszutauschen, und die Ergebnisse waren ziemlich gut. Ich habe die Performance nicht gemessen, aber der Speicherverbrauch der Anwendung stabilisierte sich, und ein Problem, das wie ein Memory Leak aussah, verschwand ebenfalls. Tatsächlich war es wahrscheinlich weniger ein Problem der App selbst als vielmehr Speicherfragmentierung im Standard-malloc.

    • Ich habe mir den glibc-Memory-Allocator angesehen, und es lag nicht an Speicherfragmentierung, sondern an threadlokalen Caches, die nie an den Kernel zurückgegeben werden. Selbst wenn free() aufgerufen wird, wird Speicher nach außen hin außer in Ausnahmefällen nicht wirklich freigegeben.
      Je mehr Threads und CPU-Kerne es gibt, desto schlimmer wird dieses Problem. Eine einfache Lösung ist, die „magische“ Umgebungsvariable MALLOC_ARENA_MAX=2 zu setzen und damit die Anzahl der Caches zu begrenzen. Eine andere Möglichkeit ist, dass die Anwendung regelmäßig malloc_trim() aufruft, um die Caches zu leeren; dafür muss allerdings der Quellcode geändert werden.
      https://www.joyfulbikeshedding.com/blog/2019-03-14-what-caus...
    • Stimmt, ich hätte es auch fast kurz geglaubt. Aber es ist auch leicht, Ubuntu die Schuld für den Fehler zuzuschieben. Persönlich finde ich, dass Ubuntu beim Zusammenstellen von Paketen ziemlich gute Arbeit leistet und tatsächlich auch mit aktivierten Stack-Protection-Optionen kompiliert.
      Umgekehrt bin ich froh, dass nicht mit dem potenziell fehleranfälligen -O3 kompiliert wird. Für einige performancekritische Teile kann das gut sein, aber ich möchte nicht, dass das gesamte System mit -O3 kompiliert wird.
    • Dass man solche Verbesserungen global allein anhand der Beschreibung in einem einzigen Artikel erzielen kann, ist ausgeschlossen; das wirkt eindeutig übertrieben. 90 % schneller ist eine Zahl aus einem Mikrobenchmark.
    • Ich frage mich, wie viele vorpaketierte Binär-Distributionen mit den sichersten Optionen für Betriebssystem und Hardware gebaut werden und dadurch nicht die bestmögliche Performance erreichen. Ehrlich gesagt vermute ich: die meisten.
      Vor langer Zeit habe ich angefangen, Mozilla und meinen Linux-Kernel nach meinen Vorlieben zu bauen, und normalerweise brachte das ordentliche Performance-Gewinne. Der ganze Zweck der Linux-Distribution Gentoo besteht beispielsweise darin, Performance-Gewinne dadurch zu erzielen, dass alles aus dem Quellcode optimiert kompiliert wird.
    • Der Titel ist Clickbait, aber es ist gut, App-Entwickler dazu zu ermutigen, ihre Software neu zu bauen. Besonders dann, wenn bei einigen verbreiteten Utilities wie jq, grep, ffmpeg oder ocrmypdf die CPU der Flaschenhals ist; solche allgemeinen Unix-Utilities werden oft als universelle Build-Ziele erstellt, nicht für eine bestimmte Anwendung.
  • Engineering bedeutet Kompromisse. Die meisten Gewinne im Artikel stammen daraus, den Speicher-Allokator zu spezialisieren. Man muss bedenken, dass manche Projekte multithreaded sind: Ein Thread alloziert, ein anderer Thread schreibt Daten, und ein dritter Thread gibt sie wieder frei.
    Der Allokator muss damit umgehen können; daher kann eine Beschleunigung in einem Projekt in einem anderen zu Abstürzen führen. Auch die Strategie für Re-Allokationen ist ein Problem. Manche Programme allozieren vorab und rühren malloc danach nie wieder an, während andere ständig freigeben und erneut Speicher anfordern. Entscheidend ist auch, wie gut Fragmentierung gehandhabt wird und ob die Laufzeit 10 Sekunden oder 10 Jahre beträgt. Manchmal ist die Wahl des Allokators der Unterschied zwischen langfristiger Stabilität und kurzfristiger Geschwindigkeit.
    Als ich einen Video-Editor baute, der beim Testen von 4K-Video Frames cached, habe ich mehrere Allokatoren ausprobiert. 32 MB pro Frame bei 60 fps sind fast 2 GB pro Sekunde und Spur. Man stößt sofort an die Grenzen des Allokators und merkt zumindest, dass der Standard-Allokator von glibc langfristig am stabilsten ist. In kurzen Benchmarks ist er aber der langsamste.

    • Mimalloc ist wie JEMalloc / TCMalloc ein universeller Allokator. glibc ist als ziemlich schlechter Allokator bekannt, und MIMalloc oder ein aktuelles TCMalloc – also nicht die in Ubuntu standardmäßig mitgelieferte Version – sind glibc deutlich voraus.
      Natürlich kann das Ausmaß der Beschleunigung variieren, aber Benchmarks zeigen im Allgemeinen Verbesserungen über die Breite. Ob das für eine bestimmte Anwendung relevant ist, ist eine ganz andere Frage. Was Abstürze angeht: Das sind alles universelle, multithreaded Allokatoren, sie verhalten sich also nicht grundlegend anders als glibc; Bugs kann es genauso gut auch in glibc geben.
    • Ich arbeite ebenfalls mit großen 8K-Video-Frames [1]. Wenn du die Frames selbst meinst, sind 60 Allokationen pro Sekunde gar nichts. glibc ist aus genau einem Grund langsam: Jede Allokation liegt über DEFAULT_MMAP_THRESHOLD_MAX, und auf 64-Bit-Plattformen sind das 32 MiB, sodass man glibc nicht dazu bringen kann, sie zu cachen, wie im mallopt-Handbuch dokumentiert.
      Jedes Mal wird per mmap direkt beim Kernel Speicher angefordert und per munmap zurückgegeben. Diese System Calls sind etwas langsam, und in meinem Fall sind die Kosten für Page Faults auf jeder Speicherseite beim ersten Zugriff zu hoch, um die Performance-Ziele zu erreichen. Die Lösung ist wirklich simpel: Nur für Video-Frames eine eigene Free List über einem universellen Allokator oder über mmap verwenden. Da Allokationen exakt derselben Größe sehr stabil wiederholt werden, funktioniert das gut.
      [1] Im UYVY-Format etwas weniger als 64 MiB, im I420-Format etwas weniger als 48 MiB.
    • Ich finde diesen Kommentar etwas schwer zu verstehen. Ich kenne mich mit C oder C-Compilern nicht besonders gut aus, aber ich habe den gesamten Gist gelesen, viel daraus gelernt und fand ihn wertvoll.
      Wenn ich nun diesen übergeordneten Kommentar lese, mache ich mir aber Sorgen, ob ich den Artikel völlig missverstanden habe. Vom Ton her klingt es so, als dürfe man das, was im Gist beschrieben wird, auf keinen Fall tun und als sei es ein schrecklicher Vorschlag, der all diese Komplexität übersieht. Kann jemand helfen zu verstehen, ob der ursprüngliche Gist ein guter Text ist, ob er berechtigte Punkte hat oder ob er völlig wertlos ist? Bis ich diesen Kommentar gesehen habe, hielt ich ihn für wertvoll, aber mir ist klar geworden, dass ich nicht klug genug bin, das zuverlässig zu unterscheiden.
    • Genau deshalb ist es eine schlechte Idee, einen einzigen Allokator für alles auf der Welt zu verwenden. Es ist furchtbar, wenn alle – Single-Thread-Anwendungen und sogar Multi-Thread-Anwendungen mit strikt kontrolliertem Ressourcenmanagement – die Kosten für Thread-Sicherheit zahlen müssen.
    • Ein bekanntes Leid. Wenn man die passende Sprache für ein Projekt verwendet, sagen die Leute aus allen möglichen Gründen, man hätte eine andere Sprache verwenden sollen.
      Wenn man den passenden Kompressionsalgorithmus für die Daten verwendet, erklären einem die Leute, warum man dumm sei und einen anderen Algorithmus hätte nehmen sollen. Kürzlich musste ich einen bestimmten langen JSON-String komprimieren, um ihn in Dynamo zu speichern, und nachdem ich alle populären Algorithmen gründlich getestet hatte, lag Brotli weit vorn. Das hielt trotzdem nicht jeden Vorbeikommenden davon ab, mir zu sagen, zlib sei besser. Manchmal ist das ziemlich ermüdend.
  • Gentoo Linux ist im Grunde eine Distribution, die genau für solche Leute gemacht wurde: damit man den eigenen Linux-Rechner für die eigenen Zwecke optimieren kann
    Nach der Ersteinrichtung ist sie ziemlich simpel und leicht zu benutzen. Ich erinnere mich, dass ich im Gentoo-Linux-Kanal auf Matrix viele Freunde gefunden habe; das war eine schöne Zeit
    https://www.gentoo.org/
    Interessanterweise war das frühe ChromeOS im Grunde eine angepasste Gentoo-Linux-Installation. Ich weiß nicht, ob es intern immer noch Gentoo Linux nutzt

    • Stimmt, aber es ist erwähnenswert, dass Optimierung hier nicht unbedingt Performance bedeutet
      Ich nutze Gentoo seit 20 Jahren, aber nie wegen der Performance. Gentoo ist großartig, wenn man weiß, wie sich das System verhalten soll, und es hilft einem, dorthin zu kommen
    • Das HN-artige „install gentoo“-Meme sehe ich zum ersten Mal. Definitiv raffinierter
      Das Ziel von Gentoo ist es, ein Betriebssystem zu haben, das alle Programme aus dem Quellcode baut, statt vorgefertigte Binärpakete zu verwenden. Das ermöglicht fortgeschrittene Geschwindigkeitssteigerungen und Anpassungen, bedeutet aber auch, dass selbst die grundlegendsten Komponenten wie der Kernel aus dem Quellcode kompiliert werden müssen. In der Linux-Community gilt es wegen seines aufwendigen Installationsprozesses als sehr komplexes Betriebssystem. Eine Standardinstallation von Gentoo bootet direkt in eine Eingabeaufforderung; die Nutzer müssen die Festplatte selbst partitionieren, ein Paket namens „Stage 3 tarball“ herunterladen und entpacken, Pakete manuell installieren und daraus das System aufbauen. Neue oder unerfahrene Nutzer wissen oft nicht, was sie tun sollen, wenn sie in ein Installationsprogramm gelangen und keine grafische Oberfläche sehen. Mitglieder von /g/ übertreiben oft den Wert von Gentoo, um neue Nutzer dazu zu bringen, eine Installation zu versuchen
    • Ich habe Gentoo seit 2003 durchgehend genutzt und bin erst ganz kürzlich, Ende 2024, zu Void Linux gewechselt, nachdem ich es ausprobiert hatte. Bei Void ist es zwar kein erklärtes Ziel und keine Architektur-Funktion, dass Endnutzer aus dem Quellcode bauen können, aber die Chancen stehen ziemlich gut, dass man es tatsächlich zum Laufen bekommt
      Es kann ein- oder zweimal haken, aber wenn man insgesamt genug Linux-Erfahrung hat, ist es wahrscheinlich, dass man in die Build-Rezepte hineingeht, sie repariert, sie nach Bedarf zum Laufen bringt und die Änderungen upstream beiträgt. Das ist das Ergebnis eines hartnäckigen Fokus auf Minimalismus und darauf, Overengineering jeder Art zu vermeiden. Genau das habe ich während meiner ganzen Gentoo-Zeit vermisst. Bei Gentoo habe ich ständig an USE-Flags und Package-Masks herumgedreht, auf eine Weise, die anderen Nutzern kaum half. Das Build-System war so komplex, dass es über die Jahre hinweg zu schwierig war, es wirklich zu beherrschen, Probleme auf Root-Cause-Ebene zu beheben und upstream beizutragen. Void kann auch eine ideale Basis sein, wenn man nicht das gesamte System aus dem Quellcode bauen will, aber Pakete aus Distributions-Binaries und selbst aus dem Quellcode gebauten Paketen mischen möchte
    • Ich habe Gentoo eine Zeit lang genutzt, aber wegen der Versuchung, endlos an allem herumzuschrauben, habe ich mir am Ende immer wieder das System kaputtgemacht. Nicht Gentoos Schuld, sondern meine
      Danach bin ich zu ArchLinux gewechselt, und für mich war das größtenteils in Ordnung. Wenn man einen ziemlich standardmäßigen Prozessor nutzt, glaube ich nicht, dass Gentoo einen so großen Vorteil bringt
    • Soweit ich weiß, wird Gentoo-basiertes ChromeOS gerade durch Android ersetzt
  • Damit fällt man nicht nur bei jq, sondern auch bei Sicherheitsupdates für onigurama, die Regex-Parsing-Abhängigkeit, heraus. Früher gab es ein Sicherheitsupdate für onigurama, und wenn so etwas wieder passiert, könnte man verwundbar sein. jq wird häufig zum Parsen nicht vertrauenswürdiger JSON-Daten verwendet
    Es ging um „Sicherheitsupdate: Behebung mehrerer ungültiger Pointer-Dereferenzierungen, Out-of-bounds-Write-Speicherbeschädigungen und Stack-Buffer-Overflows“; betroffen waren CVE-2017-9224, CVE-2017-9226, CVE-2017-9227, CVE-2017-9228 und CVE-2017-9229

    • Wenn man einen Userspace-Paketmanager wie Gentoo Prefix verwendet, kann man diesen Custom-Build installieren und trotzdem weiter Sicherheitsupdates erhalten
    • Trotzdem steckt darin doch so etwas wie der Keim einer Idee für ein Paketverwaltungssystem, das je nach Plattform intelligent entscheidet, ob gebaut werden soll? Die Performance scheint ziemlich groß zu sein, um sie liegen zu lassen
    • Im Allgemeinen stimmt das, aber in diesem Fall ist es falsch. Der im Gist beschriebene Build linkt onigurama weiterhin dynamisch. onigurama steckt in einem anderen Paket namens libonig5 und wird ganz normal aktualisiert werden
    • Ich frage mich, wie allgemein anwendbar solche CVEs wirklich sind. Es fühlt sich an, als würde man sagen, dass man die Sicherheit einer Tresortür verpasst, wenn man zu Hause eine Zimmertür benutzt. Das ist nicht falsch, aber es gibt Gründe, warum nicht jede Tür in einer Bank eine Tresortür ist
      Ich will den Wert des CVE-Systems nicht kleinreden, aber es lässt sich schwer bestreiten, dass sich die tatsächlichen Auswirkungen der einzelnen Findings stark unterscheiden
    • Absolut. Und wenn man den Allocator austauscht und Compiler-Flags ändert, kann man zufällig auch gegen Angriffe immun werden, die von einem bestimmten Speicherlayout abhängen
  • Es ist eine Weile her, dass ich mich mit so etwas beschäftigt habe, aber meiner Erinnerung nach kauft man sich, sobald man über die Flags hinausgeht, die die Upstream-Entwickler verwenden, seltsame Bugs und enorme Gleichgültigkeit ein, wenn Bugs auftreten. Ich meine hier nicht Distributions-Paketierer, sondern Upstream-Entwickler
    Einen anderen malloc als den aus libc habe ich nie verwendet, aber ich vermute, dass dasselbe Prinzip gilt

    • Zwei gegensätzliche Dinge sind gleichzeitig wahr. Wenn eine einzelne Person nicht im Geringsten abweichen will, ist sie auf demselben Weg wie die meisten anderen und hat kurzfristig die höchsten Erfolgschancen
      Aber wenn alle das tun, entsteht eine Monokultur, und Monokulturen sind fragil und schlecht. Code wird erst dadurch ein wenig robuster, dass er in anderen Kontexten gebaut wird, also auf anderen Plattformen, mit anderen Compilern, Optionen, Bibliotheken usw. Ein Bug, den die meisten nie berührt haben, weil ihre Plattform oder ihre Build-Flags zufällig direkt an der Falle vorbeiführen, ist trotzdem ein Bug, und es ist besser für den Code, ihn zu finden und zu beheben. Als Einzelne profitieren wir alle davon, wenn Code insgesamt robuster statt fragiler wird
    • Ich baue mein emacs seit Langem selbst und bin bisher noch auf keine seltsamen Bugs gestoßen. Ich dachte, solange man unsichere Optimierungen vermeidet, ist es in Ordnung
      Allerdings dachte ich auch, dass -march=native die Hauptverbesserung ist, die ich gesehen habe, und dieser Artikel zeigt, dass das nicht unbedingt so ist. Anwendungen, die Gleitkomma verwenden, haben möglicherweise auch mehr raue Kanten
    • Umgekehrt kann man Upstream-Entwickler, wenn Optimierungen auf mehreren Plattformen konsistent helfen, davon überzeugen, sie selbst zu implementieren. Es müssen nicht unbedingt alle Plattformen sein; wenn der Performancegewinn auf einer einzelnen Architektur groß genug ist, kann das ein Grund sein, die Einstellungen für diesen Build anzupassen
  • Es ist zwar eher so, dass es mit einem anderen Allocator neu gebaut wurde, der in bestimmten Workflows gute Benchmark-Ergebnisse liefert.

    • Praktisch alles ist besser als glibc malloc. Dass Distributionen statt mimalloc oder jemalloc weiterhin glibc malloc verwenden, ist im Grunde Arbeitsverweigerung.
    • Ist überhaupt bekannt, für welche Workloads glibc malloc gut ist?
  • Mich würde interessieren, wie die Performance im Vergleich zu diesem Rust-basierten jq-Klon ausfällt.
    cargo install --locked jaq
    Um Optimierungen für eine bestimmte CPU-Familie zu aktivieren, kann man auch RUSTFLAGS="-C target-cpu=native" hinzufügen. cargo install ist ein unterschätztes Rust-Feature, das genau zu der im Artikel beschriebenen Art von Nutzung passt. Da das Tool aus dem Source gebaut wird, kann man plattformspezifische Funktionen oder Instruktionen auswählen, die normalerweise aus Kompatibilitätsgründen mit älteren CPUs nicht in Binaries enthalten sind. Man muss kein Repository klonen oder herausfinden, wie es gebaut wird; das kommt einfach mit.
    jaq[1] und yq[2] sind meine erste Wahl, wenn ich jq nutze und eine schnelle, einfache Performance-Steigerung brauche.
    [1] https://github.com/01mf02/jaq
    [2] https://github.com/mikefarah/yq

    • Ab und zu vergleiche ich jaq mit jq und gojq, getestet mit meiner jq-Lösung für AoC 2022 Day 13.
      https://gist.github.com/oguz-ismail/8d0957dfeecc4f816ffee79d...
      Es liegt immer noch hinter beiden zurück.
    • Als Bonus, den manche vielleicht nicht kennen: Wenn man direkt ein Repository verwenden möchte, hat cargo install auch ein --git-Flag, mit dem man eine Repository-URL angeben kann. Das ist nützlich, wenn es kein öffentliches Paket gibt oder man einen aktuellen Commit will, der noch nicht veröffentlicht wurde.
      Ich habe das früher schon mehrfach genutzt, besonders als einfache Möglichkeit, schnell privat hastig gebaute Tools zu installieren, nachdem ich sie in ein Repository gepusht hatte, ohne einen Release-Prozess aufzusetzen oder Binaries manuell auf meine privaten Geräte zu kopieren und den exakten Commit zu verfolgen, mit dem gebaut wurde.
  • Wenn man das wirklich so machen will, sollte man Ubuntu einfach das genaue Source-Paket herunterladen lassen, das es erwartet. In diesem Fall nimmt man apt-get source jq.
    Danach kann man in das Paket gehen und nach Belieben neu kompilieren. Man kann es für Verteilung oder Archivierung auch wieder paketieren. So erhält man ein Ergebnis, das dem Upstream-Ubuntu deutlich näher ist, statt eine Menge seltsamer Fehler und Inkonsistenzen zu bekommen.

  • Der Titel ist irreführend. Gemeint sind 90% der schneller gewordenen Zeit; tatsächlich ist es etwa 45% schneller.
    Wenn einen interessiert, wie wir Sprache verwenden, ist das ein bisschen spannend. Man könnte auch sagen, dass in derselben Zeit 90% mehr Arbeit erledigt wird, und das passt zu anderen gebräuchlichen Geschwindigkeitseinheiten wie Meilen pro Stunde, Wörter pro Minute oder Bits pro Sekunde. Bei Computer-Performance ist es aber üblich, die Zeit zu messen, die für eine feste Arbeitsmenge benötigt wird. Vermutlich, weil die Arbeitsmenge meistens fest ist und sich nur die Wartezeit ändert. Genau das gilt auch für diesen Blogpost, deshalb landet die Zeit im Zähler. Der Artikel selbst ist sehr interessant und gut geschrieben, aber es ist nicht 90% schneller.

    • Noch irreführender ist die Nuance, dass man alle Pakete 90% schneller machen könne. Es geht hier um ein einzelnes bestimmtes Paket.
    • Hier scheint es sinnvoller, „90% faster“ als Durchsatzgröße zu verwenden, nicht als Zeiteinheit.
      Außerdem würde man bei Zeiteinheiten nicht das Wort „faster“ verwenden. „45% less time“ und „45% faster“ sind sehr unterschiedliche Aussagen, und beide haben innerhalb wie außerhalb der Programmierung eine Bedeutung.
    • Verwandter Artikel: https://randomascii.wordpress.com/2018/02/04/what-we-talk-ab...
    • Wenn ich weiter darüber nachdenke, glaube ich zu verstehen, warum es irreführend ist: Die Änderung des größeren Werts wird als Prozentsatz des kleineren Werts angegeben.
      Wenn man sagt, man habe „etwas um N% reduziert“, nimmt man normalerweise an, dass diese N% sich auf das reduzierte Objekt selbst beziehen, nicht auf einen anderen Wert.
    • Klingt richtig. Man kann sagen, dass das Paket, also der Code, 45% schneller ist, oder dass der Parsing-Durchsatz um 90% erhöht wurde. Aber beides zu vermischen ist verwirrend.
  • Als ich las, dass man mit so einer einfachen Änderung eine große Beschleunigung erzielen kann, war mein erster Gedanke, dass man die jq-Autoren informieren sollte. Es könnte Fallstricke geben, auf die man achten muss, und nach Tests könnte man es für alle schneller machen.
    Unabhängig vom Ergebnis scheint es sinnvoll, kurz Bescheid zu sagen. Im Artikel scheint diese Option aber nicht einmal erwogen zu werden, und auch hier in den Kommentaren sehe ich sie nicht. Übersehe ich etwas?

    • Ich frage mich, ob Intels Clear Linux durch die Nutzung neuerer Opcodes aus dem Instruktionssatz ähnliche Vorteile erzielt.
      Ich weiß nicht, ob dort der glibc-Allocator ebenfalls Standard ist.
      https://en.m.wikipedia.org/wiki/Clear_Linux_OS