Wenn derselbe Commit auf einem Cloud-Mac wiederholt gebaut wird und der erste Lauf 11 Minuten, der vierte jedoch 15 Minuten dauert, sollte die Diagnose nicht vorschnell „instabile Maschinenleistung“ lauten. Cache-Treffer, Indizierungsprozesse, parallele Tests und thermische Belastung können ähnliche Verläufe verursachen. Für eine belastbare Bewertung müssen die Variablen konstant gehalten und mehrere Messungen in Folge erfasst werden. Erst dann lässt sich feststellen, ob der Laufzeitanstieg mit Leistungsaufnahme und thermischem Druck zusammenfällt.
Vergleichbare Build-Läufe definieren
Gegenstand der Diagnose sollte ein zuverlässig reproduzierbarer Befehl sein und kein Scheme, das ein Entwickler kurzfristig in der Oberfläche auswählt. Commit, Xcode-Pfad, SDK, Ziel, Build-Konfiguration und DerivedData-Verzeichnis müssen konstant bleiben. Zusätzlich ist zu protokollieren, wie lange der Rechner nach dem Start im Leerlauf war. Wer vor jedem Lauf den Branch wechselt oder Abhängigkeiten aktualisiert, misst lediglich Veränderungen der Arbeitslast.
Auf den exklusiven physischen Nodes von VMOrbit werden die Host-Ressourcen nicht durch die Planung virtueller Maschinen anderer Mandanten beeinflusst. Innerhalb eines Nodes kann es dennoch zu Konkurrenz zwischen Indizierung, Auflösung von Abhängigkeiten, Simulatoren und verbliebenen Build-Prozessen kommen. Vor dem Test sollten deshalb zunächst die grundlegenden Umgebungsdaten gespeichert werden:
mkdir -p "$HOME/build-thermal-check"
system_profiler SPHardwareDataType > "$HOME/build-thermal-check/hardware.txt"
xcodebuild -version > "$HOME/build-thermal-check/xcode.txt"
pmset -g > "$HOME/build-thermal-check/power.txt"
ps -axo pid,%cpu,%mem,etime,command > "$HOME/build-thermal-check/processes-before.txt"
Kalte und warme Builds getrennt messen
Kalte Builds bilden die Last einer vollständigen Kompilierung ab, während warme Builds den inkrementellen Pfad untersuchen. Beide dürfen nicht in derselben Statistik zusammengefasst werden. Für einen kalten Build ist ein neues, ausschließlich dafür vorgesehenes DerivedData-Verzeichnis zu verwenden. Warme Builds nutzen dagegen das Verzeichnis des vorherigen Laufs weiter. Globale Caches sollten nicht gelöscht werden, da dies andere Aufgaben auf dem Node beeinträchtigen kann.
| Stichprobe | DerivedData | Hauptzweck |
|---|---|---|
| Kalter Build | Für jede Gruppe neu erstellen | Dauerlast einer vollständigen Kompilierung vergleichen |
| Warmer Build | Innerhalb derselben Gruppe wiederverwenden | Inkrementelle Builds und Cache-Wirkung prüfen |
| Leerlaufkontrolle | Keinen Build ausführen | Hintergrundprozesse und Leistungsaufnahme im Ausgangszustand erkennen |
Laufzeit und thermischen Druck gleichzeitig erfassen
Die Gesamtlaufzeit allein ist kein Nachweis für thermische Drosselung. Es sollten mindestens drei Läufe hintereinander ausgeführt und während jedes Builds gleichzeitig Daten mit powermetrics erfasst werden. Dieses Werkzeug benötigt üblicherweise Administratorrechte. Die Autorisierung sollte auf eindeutig festgelegte Befehle und Parameter beschränkt bleiben.
RUN_ROOT="$HOME/build-thermal-check/run-01"
mkdir -p "$RUN_ROOT/DerivedData"
sudo powermetrics --samplers cpu_power -i 1000 -n 900 \
-o "$RUN_ROOT/powermetrics.txt" &
METRIC_PID=$!
/usr/bin/time -lp xcodebuild \
-workspace App.xcworkspace \
-scheme App \
-configuration Release \
-destination 'generic/platform=iOS' \
-derivedDataPath "$RUN_ROOT/DerivedData" \
build > "$RUN_ROOT/build.log" 2> "$RUN_ROOT/time.txt"
wait "$METRIC_PID"
pmset -g therm > "$RUN_ROOT/thermal.txt"
-n 900 begrenzt die Erfassung auf höchstens 900 Messungen. Ist der Build früher abgeschlossen, läuft die Abtastung im Hintergrund trotzdem weiter. Ein Projektskript kann dem Messprozess nach Abschluss des Builds TERM senden. Dabei muss jedoch eine saubere Beendigung vorgesehen werden, damit in der CI keine Messprozesse zurückbleiben. Falls powermetrics nicht verfügbar ist, sollten mindestens die Ausgaben von pmset -g therm und time -lp sowie eine nach CPU-Auslastung sortierte Prozessaufnahme gespeichert werden.
Thermische Drosselung ist erst dann eine belastbare Diagnose, wenn sowohl der Laufzeitverlauf als auch die Systemsignale darauf hindeuten. Eine einmalig hohe CPU-Auslastung reicht dafür nicht aus.
Anhand des Verlaufs statt eines Einzelwerts urteilen
Für jeden Lauf sollten reale Laufzeit, maximaler residenter Speicher, thermischer Druck und gleichzeitig aktive Prozesse mit hoher Last in derselben Tabelle erfasst werden. Ist der erste Lauf langsam und werden die folgenden deutlich schneller, deutet das meist auf das Aufwärmen des Caches hin. Wird nur ein einzelner Lauf plötzlich langsamer und finden gleichzeitig Downloads von Abhängigkeiten oder Indizierungsprozesse statt, ist Ressourcenkonkurrenz die wahrscheinlichere Ursache.
Besonders relevant ist folgendes Muster: Code und Cache-Zustand bleiben unverändert, während die Laufzeiten über mehrere aufeinanderfolgende Durchgänge schrittweise steigen. Die CPU bleibt über längere Zeit vollständig ausgelastet, und gleichzeitig nehmen die Signale für thermischen Druck zu. Nach dem Beenden der Last und einer Ruhephase erreicht der nächste Lauf wieder das vorherige Niveau. Nur wenn diese Indizien gemeinsam auftreten, sollte thermische Drosselung als Hauptursache gelten.
Drei typische Fehlinterpretationen ausschließen
Zunächst ist zu prüfen, ob der Build unbemerkt Tests, eine Archivierung oder Downloads durch Skripte ausführt. Anschließend sollte die Anzahl der kompilierten Dateien in den Protokollen verglichen werden, um eine identische Arbeitsmenge pro Lauf sicherzustellen. Abschließend ist zu kontrollieren, ob mehrere xcodebuild-Instanzen, Simulatoren oder Paketverwaltungsprozesse parallel laufen. Für einen auffälligen Durchgang können folgende Daten gespeichert werden:
date -u
pgrep -alf 'xcodebuild|swift-frontend|clang|Simulator'
top -l 3 -o cpu -stats pid,command,cpu,mem,time
du -sh "$RUN_ROOT/DerivedData"
Die momentane Rangfolge in top zeigt lediglich, welche Prozesse zum Zeitpunkt der Messung Ressourcen verbrauchen. Sie kann die Ursache nicht allein belegen. Erst die Anzahl und Dauer der Aufgaben im Build-Protokoll stellen den Zusammenhang zwischen Systemzustand und Projektlast her.
Auslöser mit kontrollierten Experimenten bestimmen
Sobald sich ein Trend bestätigt, sollte pro Versuch nur eine Variable verändert werden. Zunächst wird die Zahl paralleler CI-Jobs auf 1 reduziert, während alle anderen Bedingungen unverändert bleiben. Danach wird die Parallelität wiederhergestellt, die Jobs werden jedoch zeitlich versetzt gestartet. Abschließend werden kalte und warme Builds miteinander verglichen. Stabilisiert sich der Laufzeitverlauf nach der Verringerung der Parallelität, liegt die Ursache eher in konkurrierenden Aufgaben auf demselben Node oder in einer dauerhaft zu hohen Leistungsaufnahme als in einer Verschlechterung des Compilers.
Ein aggressiveres Löschen der Caches sollte nicht unmittelbar als Lösung eingesetzt werden. Dadurch steigt die Arbeitsmenge des nächsten Laufs, und der ursprüngliche Verlauf wird verdeckt. Auch ein Neustart ist als alleiniger Nachweis ungeeignet: Er beendet gleichzeitig Hintergrundprozesse, löscht einen Teil des Systemzustands und sorgt für eine Abkühlphase. Damit bleibt unklar, welche dieser Veränderungen tatsächlich geholfen hat.
Build-Planung anpassen statt Momentantakten nachzujagen
Eine stabile Lösung besteht meist darin, die Zahl schwerer Aufgaben auf demselben Node zu begrenzen, Archivierung, vollständige Tests und statische Analyse zeitlich zu entzerren und jeder Pipeline ein eigenes DerivedData-Verzeichnis zuzuweisen. Müssen Aufgaben parallel laufen, kann zunächst die mediane Laufzeit bei einer Parallelität von 1, 2 und 3 gemessen werden. Gewählt wird anschließend die Stufe mit dem höchsten Gesamtdurchsatz, nicht diejenige mit der höchsten Spitzenleistung eines einzelnen Jobs.
Diagnoseergebnisse als CI-Baseline festhalten
Eine Baseline sollte den Befehl, den Commit, die Xcode-Version, den Build-Typ, die Zahl der aufeinanderfolgenden Läufe und die mediane Laufzeit enthalten. Der schnellste Einzellauf eignet sich nicht als Referenz. Ebenso sollte eine Zusammenführung nicht wegen einer einzigen Zeitüberschreitung blockiert werden. Belastbarer ist eine rollierende Auswertung der Medianwerte mehrerer aktueller Messungen. Erst wenn der Schwellenwert wiederholt überschritten wird, sollte ein Diagnosebericht erzeugt werden.
Zu den Ergebnissen gehören mindestens das Build-Protokoll, die Ausgabe von time, die Aufzeichnung des thermischen Drucks, Prozessaufnahmen und die verwendeten Messparameter. Muss das Problem eskaliert werden, sollten Node-Region, Zeitpunkt, Reproduktionsschritte und die unbedingt erforderlichen Protokolle beigefügt werden. Passwörter, private Schlüssel oder vollständige Zugangsdaten dürfen dabei nicht übermittelt werden. Tritt später eine ähnliche Schwankung auf, kann das Team dieselbe Messreihe direkt erneut ausführen, statt wieder von vorn über die Ursache zu spekulieren.
Häufig gestellte Fragen
Beweist ein einzelner langsamer Xcode-Build thermische Drosselung?
Nein. Quellstand, Xcode-Version, Cache-Zustand und Parallelität müssen konstant bleiben. Erst mindestens drei Läufe mit passenden Wärme-, Leistungs- und Prozessdaten erlauben eine belastbare Einordnung.
Benötigt powermetrics Administratorrechte?
In der Regel ja. Sinnvoll ist ein eng begrenzter sudo-Aufruf mit festgelegten Parametern und einem kontrollierten Ausgabeverzeichnis.
Build-Aufgaben auf einen dedizierten Cloud-Mac verlagern
Wählen Sie VMOrbit M4 oder VMOrbit M4 Pro und wählen Sie den Knoten passend zum Standort Ihres Teams. Die tatsächliche Verfügbarkeit und Bereitstellungsinformation richtet sich nach den Live-Angaben in der Konsole.