HeadlinesBriefing favicon HeadlinesBriefing.com

Buildprof visualisiert Kompilierzeiten

Hacker News •
×

Ich habe buildprof (GitHub) erstellt, ein Open-Source-Tool, das zeigt, wo die Zeit verbracht wird, wenn Sie Software unter Linux kompilieren. Hier ist ein Echtzeit-Video, wie es eine saubere Kompilierung von ripgrep profiliert: Schauen Sie sich das buildprof-Demo an. Manchmal sind Builds langsam, weil einfach viel Code kompiliert werden muss.

Aber häufiger gibt es behebbare Probleme: schlechte Parallellisierung, wiederholte Arbeit, Abhängigkeitsdownloads oder eine riesige Compiler/Linker-Aufrufung. buildprof macht all dies deutlich sichtbar, damit Sie sehen können, was es zu untersuchen und zu optimieren gilt. Sie führen es aus, indem Sie buildprof -- vor jeden Build-Befehl setzen, den Sie bereits verwenden: buildprof -- make -j16, buildprof -- cargo build, buildprof -- ninja -C out/target, buildprof -- just build, buildprof -- ./dev/custom-build-script.sh. buildprof protokolliert jeden Prozess, den Ihr Build-Befehl startet, einschließlich ihrer Subprocesses (und ihrer Subprocesses…), und legt sie in einer Zeitachse aus. Die Zeit bewegt sich von links nach rechts, die Balkenbreite zeigt die Dauer an, und Kindprozesse erscheinen unter dem, was sie gestartet hat.

Ich habe buildprof erstellt, weil dieses Tweet von Jarred Sumner, dem Chief Architect des Bun JavaScript-Laufzeit, seit einiger Zeit in meinem Kopf herumspukt: Genauer gesagt, die Behauptung, dass Buns neue Rust-Kompilierung mehr als 5-mal schneller als ihre alte Zig-Kompilierung ist. In meiner Erfahrung wurden Zig-Projekte meist schneller kompiliert als Rust-Projekte ähnlicher Komplexität. Diese Intuition war genug, um das Gefühl zu haben, es gäbe ein Rätsel zu lösen.

Das wurde zusätzlich dadurch erschwert, dass ein weiteres, aber leicht übersehbares Detail in dem Tweet war: Die Zig-Kompilierung verwendete Full LTO, während die Rust-Kompilierung Thin LTO verwendete. Compiler optimieren separate Compileinheiten largely in Isolation. Link-time optimization (LTO) lässt sie über diese Grenzen optimieren.

Full LTO bringt diese Einheiten zu einer großen Optimierungsaufgabe zusammen, während Thin LTO mehr Trennung bewahrt, sodass viel der Arbeit parallel laufen kann. Basierend auf meiner bisherigen Erfahrung kann dieser Unterschied einen enormen Einfluss auf die Build-Zeit haben. Der Tweet erwähnte es am Rande, aber ich fragte mich, wie viel der Überschrift Verbesserung es tatsächlich erklärt.

Ich versuchte, die Zahlen zu reproduzieren. Die Zahlen reproduzierten sich. Aber jetzt? Ich habe Bun 1.3.14 und Bun 1.4.0 heruntergeladen und Skripte geschrieben, um ihre Linux x64 CI Builds auf einer 6-Kern, 12-Thread-Linux-VM nachzuspielen.

Die Skripte haben die Build-Schritte und ihre Abhängigkeiten beibehalten und alles auf einer Maschine ausgeführt. Meine Timings lagen im gleichen Bereich wie bei Jarred: Linux x64 build Zig era 30m06s, Rust era 5m37s. Meine single-machine CI-profile replay 24m24s 5m40s.

OK, also erschien die Lücke auch auf meiner Maschine. Aber zwischen den beiden Messungen hatte sich viel geändert neben der Sprache; also was tatsächlich verantwortlich? War es der Zig-Compiler, der all diese zusätzliche Zeit in Anspruch nahm? Oder vielleicht war es der Full LTO-Link? Oder vielleicht gab es etwas anderes in Buns Build, das ich gar nicht in Betracht gezogen hatte? Hier ist mein Profiling- und Entwicklertools-Gehirn ins Spiel gekommen. Normalerweise möchte ich, wenn ich versuche zu verstehen, warum etwas langsam ist, einen Trace: Was ist passiert, wann und wie lange es dauerte.

Es wäre wirklich cool, wenn es für diese Builds einen Trace gäbe, um sie auf einer Zeitachse abzulegen und zu sehen, wo ihre Zeit tatsächlich hingeht. Aber ein Build umfasst viele verschiedene Tools, von denen jedes seine eigene Vorstellung davon hat, was passiert. Was könnte ich aufzeichnen, um quer durch alle davon zu sehen? Builds sind Prozessbäume.

Wenn Sie cargo build oder zig build ausführen, scheint es, als würden Sie ein Programm ausführen. Das Build-System ermittelt, was neu gebaut werden muss, die Reihenfolge zwischen diesen Teilen und was parallel laufen kann. Aber allgemein führt es diese Arbeit nicht selbst aus; es startet Compiler, Code-Generatoren, Archivierer, Linker und beliebige Skripte.

Diese können weitere Programme starten, die wiederum weitere starten… Verschiedene Build-Systeme beschreiben diese Arbeit unterschiedlich. Cargo sieht Crates, Ninja sieht Build-Kanten und CMake generiert Anweisungen für ein anderes Build-System. Vom Betriebssystem.