- Von 187 PRs sind 86,6 % warm — SLA im Alltag auf warm fokussieren, cold nicht in P95 mischen
- Dependency-Änderung, Cache-Löschung, Scheme-Wechsel → cold; 2–3× Wandzeit-Spread ist normal
- Auch bei warm: Cache-Miss, parallele Jobs auf derselben Platte, Tests + Signing in einem Job → Varianz bleibt
- Grobe Reihenfolge: Queue → cold/warm trennen → Cache → Concurrency → Hardware zuletzt (Wasserfall)
Vollständige Benchmark-Daten → Pillar · Benchmark
1. CI wie Lotterie: gleicher Commit, anderer Spread
Wer iOS CI langsam? Warum xcodebuild auf GitHub Actions schwankt sucht, kennt diese Bilder aus dem Alltag:
- Gleicher Commit, Re-Run — Wandzeit 2–3× auseinander
- Dashboard-P95 rot, Team-Gefühl: „Merge wartet eigentlich nicht so lang“
pod installmal 30 Sekunden, mal „hängt“- Freitagnachmittag kein Merge — Angst vor dem langsamen CI-Zug
Nicht immer fehlt CPU. In unserem 14-Tage-Shadow mit Teams war häufiger: Zahlen vermischt, Umgebung instabil — Cache hält nicht, Platte wird gekappt. Chip-Wechsel steht meist ganz hinten.
Diese Feldnotiz behandelt nur: warum Builds schwanken. Job wartet in der Queue? → Queue-Artikel. Cache-Setup, Kauf vs. Miete → Cache-Cluster und ROI.
2. cold und warm: nicht in einen Topf
Klassische Falle: alle Build-Zeiten in eine P95. Einzelne cold-Läufe ziehen den Schwanz — Daten sagen „täglich langsam“, Merge-Alltag sagt etwas anderes.
- warm: Dependencies unverändert, Cache da, gleiches Scheme — meist inkrementell; repräsentiert „normaler Merge“
- cold: Lockfile geändert, Cache weg, Target/Scheme neu, erster Lauf auf Runner — resolve, pod install, Voll-Rebuild
SLA: warm P50/P95. cold eigene Linie — nicht mit Merge-Erlebnis verknüpfen.
2.1 Wann wird es cold?
Auf macos-latest öfter cold — Workspace nach Job-Ende weg, Cache „zieht nicht ein“:
| Trigger | typ. Zusatzzeit | Log-Hinweise |
|---|---|---|
Podfile.lock geändert |
+3–8 Min. | pod install, Downloading dependencies |
| DerivedData miss / gelöscht | +5–15 Min. | CompileSwift, volle .o-Neuerstellung |
| Scheme / Target gewechselt | +2–10 Min. | anderes xcodebuild -scheme |
| SPM-Resolve geändert | +1–5 Min. | Resolve Package Graph |
| Xcode-Minor-Upgrade (macos-latest) | erster Build +10–20 Min. | neues SDK / Modul-Cache-Rebuild |
Viele Pod-Upgrades → mehr cold. Kein Hardware-Verfall, sondern andere Wochenarbeit. „Pod-Woche“ und „Dev-Woche“ getrennt reporten.
2.2 Warum warm trotzdem schwankt
Auch nur warm: Wandzeit kann ~30 % schwanken. Typisch:
- Cache-Miss: Key ohne
arm64, Branch nicht gebunden, Jobs teilen Slot - Viele macOS-Jobs in einer Org → Platte und Netz bremsen sich
- Mal nur main bauen, mal volle Unit+UI-Tests — andere Arbeit
- Änderungsfläche: Pod-Quellcode vs. eine SwiftUI-Zeile — anderer Compile-Umfang
3. Wo die Zeit hingeht
Workflow-Steps timen → Wandzeit in fünf Blöcke. Warm-Verteilung (projektabhängig):
① checkout + env setup ~0:30 – 1:30 ② pod install / SPM resolve ~0:30 – 2:00 (cold ↑↑) ③ xcodebuild compile+link ~3:00 – 8:00 (code change surface) ④ tests (simulator / unit) ~1:00 – 6:00 (optional, underestimated) ⑤ archive + codesign ~1:00 – 4:00 (release pipeline) warm P50 total typical: 6 – 14 min
Praxis: Step-Zeiten oder time für pod install, xcodebuild build, xcodebuild test. Block ② >5 Min. → cold/Cache; ③ schwankt → DerivedData/Concurrency; ④ dauernd lang → Tests auslagern oder nightly.
4. Tests & Signing: versteckte Bremsen
Viele „xcodebuild langsam“-Meldungen: Tests oder Signing im selben Build:
- Simulator cold start: erste Boot-Zeit auf CI — ohne Warm-up pro Job neu
- UI Tests eine Größenordnung langsamer als Unit; mit Compile in einem Job → hässliche P95
- Zertifikate, Keychain, Profile — auf Hosted Runner oft pro Job neu
- Archive/IPA = Release-Pipeline, nicht PR-Validierung
PR: build + leichte Tests, warm P95.
TestFlight/Release: eigener Workflow, eigene Metrik. Zusammen → „Merge-Wartezeit“ passt nie.
5. 14-Tage-Shadow-Messung
Dual-Track: macos-latest und dedizierter Mac mini M4, je 187 PRs, Xcode 16.2 / CocoaPods 1.15.2 aligned. Methodik: Pillar Benchmark.
| Klasse | Samples | Anteil | macos-latest P95 | dediz. M4 P95 |
|---|---|---|---|---|
| warm build | 162 | 86,6% | 14:12 | 6:05 |
| cold build | 25 | 13,4% | 19:40 | 11:20 |
| gemischt (Fehlinterpretation) | 187 | 100% | ~16:00+ | ~7:30+ |
cold+warm in einer P95 → Alltagserlebnis ~15–25 % zu pessimistisch → „sofort Hardware“. Besser: Merge = warm, Pod-Wochen = cold separat.
Auf dediziertem M4 bleibt warm schneller als cold — DerivedData/Pods mit festem Zuhause lohnt. Details: Cache-Cluster.
6. Reihenfolge: Klarheit → Cache
Nach Queue (oder wenn Queue klein ist) — nicht mit Kauf beginnen:
- Builds taggen: cold vs warm (
Podfile.lock, Cache-Hit) - SLA nur warm P95; cold wöchentlich oder eigene Linie
- DerivedData, Pods, SPM Cache-Strategie — Cache Best Practices (in Vorbereitung)
- macOS-Jobs nicht überfüllen: self-hosted 1–2 parallel; hosted wenig Workspace-Sharing
- PR-Validierung und Release/Signing trennen
- Cache reicht nicht → M4 / M4 Pro — Chip & Engineering Hours
- name: Classify build type
run: |
if git diff --name-only HEAD~1 | grep -q Podfile.lock; then
echo "BUILD_TYPE=cold" >> $GITHUB_ENV
else
echo "BUILD_TYPE=warm" >> $GITHUB_ENV
fi
- name: Record wall clock
run: |
echo "build_type=${BUILD_TYPE}" >> metrics.csv
echo "wall_sec=$(( $(date +%s) - START ))" >> metrics.csv
7. FAQ
2–3× Spread beim Re-Run — normal?
Auf macos-latest ja: einmal voller Cache-Hit, einmal Miss plus Queue. Zuerst pod install und Cache-Logs vergleichen, nicht sofort Chip tauschen.
P95 richtig berechnen?
Merge-Alltag: warm P95, ~30 Samples in zwei Wochen. cold separat — gemischt verzerrt Beschaffung.
pod install immer langsam — CocoaPods Schuld?
Auf Hosted Runner öfter: Pods ohne festen Ort, Cache-Key ohne Branch/Architektur. Self-hosted mit fester Platte: warm oft <30 Sek.
Mac mini M4 eliminiert Varianz?
warm P95 −57 %, σ −40 % — aber ohne cold/warm-Trennung und Cache-Design bleiben Spikes auf dedizierter Hardware.
Queue oder Build?
Log-Anfang: Waiting for a runner. Queue zählt zur Wandzeit, nicht zu GitHub-Minuten. Queue ≈0 und trotzdem langsam → Build-Seite dieser Notiz → Cache.
Weiterlesen?
Cache YAML → Cache-Cluster; Kauf vs. Miete → ROI und 500 Builds/Monat.
8. Fazit
„iOS CI langsam“ auf GitHub Actions ist selten nur CPU. Häufiger überlagert:
- cold und warm in einer P95
- Hosted Job räumt Platte → Cache weg
- Tests, Signing und PR-Build in einem Job
Erst warm P95 sauber, dann Cache → Concurrency → Hardware. Im 14-Tage-Shadow reichten Metrik + Cache für warm P95 14:12 → 6:05 — ohne vorschnellen Hardware-Beschluss.
DerivedData braucht ein festes Zuhause?
Vuncloud Cloud Mac M4 Pro: 1 TB Datenplatte, actions-runner vorinstalliert. DerivedData / Pods bleiben auf der Platte — weniger „jeder Lauf cold“ wie auf Hosted Runnern.