Claude-skill-registry axiom-build-performance
Use when build times are slow, investigating build performance, analyzing Build Timeline, identifying type checking bottlenecks, enabling compilation caching, or optimizing incremental builds - comprehensive build optimization workflows including Xcode 26 compilation caching
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/axiom-build-performance" ~/.claude/skills/majiayu000-claude-skill-registry-axiom-build-performance && rm -rf "$T"
skills/data/axiom-build-performance/SKILL.mdBuild Performance Optimization
Overview
Systematic Xcode build performance analysis and optimization. Core principle: Measure before optimizing, then optimize the critical path first.
When to Use This Skill
- Build times have increased significantly
- Incremental builds taking too long
- Want to analyze Build Timeline
- Need to identify slow-compiling Swift code
- Optimizing CI/CD build times
- Build performance regression investigation
- Enabling Xcode 26 compilation caching
- Reducing module variants in explicitly built modules
- Understanding the three-phase build process (scan → modules → compile)
Quick Win: Run the Agent First
For automated scanning and quick wins:
/axiom:optimize-build
The build-optimizer agent scans for common issues and provides immediate fixes. Use this skill for deep analysis.
The Build Performance Workflow
Step 1: Measure Baseline (Required)
Why: You can't improve what you don't measure. Baseline prevents placebo optimizations.
# Clean build (eliminates all caching) xcodebuild clean build -scheme YourScheme # Measure time time xcodebuild build -scheme YourScheme # Or use Xcode UI Product → Perform Action → Build with Timing Summary
Record:
- Total build time
- Incremental build time (change one file, rebuild)
- Which phase takes longest (compilation vs linking vs scripts)
Example baseline:
Clean build: 247 seconds Incremental (1 file change): 12 seconds Longest phase: Compile Swift sources (189s)
Step 2: Analyze Build Timeline (Xcode 14+)
Access:
- Build your project (Cmd+B)
- Open Report Navigator (Cmd+9)
- Select latest build
- Show Assistant Editor (Cmd+Option+Return)
- Build Timeline appears alongside build log
What to look for:
Critical Path (The Build's Speed Limit)
The critical path is the shortest possible build time with unlimited CPU cores. It's defined by the longest chain of dependent tasks.
┌─────────────────────────────────────────┐ │ Critical Path: A → B → C → D (120s) │ │ │ │ Task A: 30s ─────────┐ │ │ Task B: 40s ├─→ D: 20s │ │ Task C: 30s ─────────┘ │ │ │ │ Even with 100 CPUs, build takes 120s │ └─────────────────────────────────────────┘
Goal: Shorten the critical path by breaking dependencies.
Timeline Red Flags
Empty vertical space: Tasks waiting for inputs
Timeline: ████████░░░░░░░░████████ ← Bad: idle cores waiting ████████████████████████ ← Good: continuous work
Long horizontal bars: Slow individual tasks
Task A: ████████████████████ (45 seconds) ← Investigate Task B: ███ (3 seconds) ← Fine
Serial target builds: Targets waiting unnecessarily
Framework: ████████░░░░░░░░░░ ← Waiting App: ░░░░░░░░░░████████ ← Delayed Better (parallel): Framework: ████████ App: ░░░░████████████
Step 3: Identify Bottlenecks (Decision Tree)
Is compilation the slowest phase? ├─ YES → Check type checking performance (Step 4) └─ NO → Is linking slow? ├─ YES → Check link dependencies (Step 5) └─ NO → Are scripts slow? ├─ YES → Optimize build phase scripts (Step 6) └─ NO → Check parallelization (Step 7)
Optimization Patterns
Pattern 1: Type Checking Performance (MEDIUM-HIGH IMPACT)
Symptom: "Compile Swift sources" takes >50% of build time.
Diagnosis:
Enable compiler warnings to find slow functions:
// Add to Debug build settings → Other Swift Flags -warn-long-function-bodies 100 -warn-long-expression-type-checking 100
Build → Xcode shows warnings:
MyView.swift:42: Function body took 247ms to type-check (limit: 100ms) LoginViewModel.swift:18: Expression took 156ms to type-check (limit: 100ms)
Fix slow type checking:
// ❌ SLOW - Complex type inference (247ms) func calculateTotal(items: [Item]) -> Double { return items .filter { $0.isActive } .map { $0.price * $0.quantity } .reduce(0, +) } // ✅ FAST - Explicit types (12ms) func calculateTotal(items: [Item]) -> Double { let activeItems: [Item] = items.filter { $0.isActive } let prices: [Double] = activeItems.map { $0.price * $0.quantity } let total: Double = prices.reduce(0, +) return total }
Common slow patterns:
- Complex chained operations without intermediate types
- Deeply nested closures
- Large literals (dictionaries, arrays)
- Operator overloading in complex expressions
Expected impact: 10-30% faster compilation for affected files.
Pattern 2: Build Phase Script Optimization (HIGH IMPACT)
Symptom: Build Timeline shows long script phases in Debug builds.
Common culprits:
- dSYM/Crashlytics uploads running in Debug
- Asset processing on every build
- Code generation scripts without caching
Fix: Make scripts conditional
# ❌ BAD - Runs in ALL configurations (adds 6+ seconds to debug builds) #!/bin/bash firebase crashlytics upload-symbols # ✅ GOOD - Skip in Debug #!/bin/bash if [ "${CONFIGURATION}" = "Release" ]; then firebase crashlytics upload-symbols fi # Example savings: 6.3 seconds per incremental debug build
Script Phase Sandboxing (Xcode 14+)
Enable to prevent data races and improve parallelization:
Build Settings → User Script Sandboxing → YES
Why: Forces you to declare inputs/outputs explicitly, enabling parallel execution.
# Script phase with proper inputs/outputs Input Files: $(SRCROOT)/input.txt $(DERIVED_FILE_DIR)/checksum.txt Output Files: $(DERIVED_FILE_DIR)/output.html # Now Xcode knows dependencies and can parallelize safely
Parallel Script Execution:
Build Settings → FUSE_BUILD_SCRIPT_PHASES → YES
⚠️ WARNING: Only enable if ALL scripts have correct inputs/outputs declared. Otherwise you'll get data races.
Expected impact: 5-10 seconds saved per incremental debug build.
Pattern 3: Compilation Mode Settings (CRITICAL)
Symptom: Incremental builds recompile entire modules.
Check current settings:
# In project.pbxproj grep "SWIFT_COMPILATION_MODE" project.pbxproj
Optimal configuration:
| Configuration | Setting | Why |
|---|---|---|
| Debug | (Incremental) | Only recompiles changed files |
| Release | | Maximum optimization |
// ❌ BAD - Whole module in Debug SWIFT_COMPILATION_MODE = wholemodule; // ALL configs // ✅ GOOD - Incremental for Debug Debug: SWIFT_COMPILATION_MODE = singlefile; Release: SWIFT_COMPILATION_MODE = wholemodule;
How to fix:
- Project → Build Settings
- Filter: "Compilation Mode"
- Set Debug to "Incremental"
- Set Release to "Whole Module"
Expected impact: 40-60% faster incremental debug builds.
Pattern 4: Build Active Architecture Only (HIGH IMPACT)
Symptom: Debug builds compile for multiple architectures (x86_64 + arm64).
Check:
grep "ONLY_ACTIVE_ARCH" project.pbxproj
Fix:
| Configuration | Setting | Why |
|---|---|---|
| Debug | | Only build for current device (arm64 OR x86_64) |
| Release | | Build universal binary |
How to fix:
- Build Settings → "Build Active Architecture Only"
- Set Debug to YES
- Keep Release as NO
Expected impact: 40-50% faster debug builds (half the architectures).
Pattern 5: Debug Information Format (MEDIUM IMPACT)
Symptom: Debug builds generating dSYMs unnecessarily.
Optimal configuration:
| Configuration | Setting | Why |
|---|---|---|
| Debug | | Embedded debug info, faster |
| Release | | Separate dSYM for crash reporting |
# Check current grep "DEBUG_INFORMATION_FORMAT" project.pbxproj
How to fix:
- Build Settings → "Debug Information Format"
- Set Debug to "DWARF"
- Set Release to "DWARF with dSYM File"
Expected impact: 3-5 seconds saved per debug build.
Pattern 6: Target Parallelization (WWDC 2018-408)
Symptom: Build Timeline shows targets building sequentially when they could be parallel.
Check scheme configuration:
- Product → Scheme → Edit Scheme
- Build tab
- Check "Parallelize Build" checkbox
- Verify target order allows parallelization
Dependency graph example:
App ──┬──→ Framework A └──→ Framework B Framework A ──→ Utilities Framework B ──→ Utilities
Timeline (bad - serial):
Utilities: ████████░░░░░░░░░░░░░░ Framework A: ░░░░░░░░████████░░░░░░ Framework B: ░░░░░░░░░░░░░░░░████████ App: ░░░░░░░░░░░░░░░░░░░░░░████
Timeline (good - parallel):
Utilities: ████████ Framework A: ░░░░░░░░████████ Framework B: ░░░░░░░░████████ App: ░░░░░░░░░░░░░░░░████
Expected impact: Proportional to number of independent targets (e.g., 2 parallel targets = ~2x faster).
Pattern 7: Emit Module Optimization (Xcode 14+, Swift 5.7+)
What it is: Swift modules are produced separately from compilation, unblocking downstream targets faster.
Before (Xcode 13):
Framework: Compile ████████████ → Emit Module █ App: ░░░░░░░░░░░░░░░░░░░░░░░░░█████████ ↑ Waiting for Framework compilation to finish
After (Xcode 14+):
Framework: Compile ████████████ Emit Module ███ App: ░░░░░░███████████ ↑ Starts as soon as module emitted
Automatic: No configuration needed, works in Xcode 14+ with Swift 5.7+.
Expected impact: Reduces idle time in multi-target builds by 20-40%.
Pattern 8: Eager Linking (Xcode 14+)
What it is: Linking can start before all compilation finishes if the module is ready.
Impact: Further reduces critical path in dependency chains.
Automatic: Works in Xcode 14+ automatically.
Pattern 9: Compilation Caching (Xcode 26+, CRITICAL)
What it is: Xcode 26 introduces compilation caching that reuses previously compiled artifacts across clean builds.
Build Settings:
Build Settings → COMPILATION_CACHE_ENABLE_CACHING → YES
How it works:
- Caches compilation results based on input file content and compiler flags
- Works across clean builds — even after
, cached artifacts can be reusedxcodebuild clean - Significantly reduces CI/CD build times where clean builds are common
When to enable:
- CI/CD pipelines with frequent clean builds
- Teams sharing build artifacts
- Projects with stable dependencies
Verification:
# Build with caching enabled xcodebuild build -scheme YourScheme \ COMPILATION_CACHE_ENABLE_CACHING=YES # Check build log for cache information
Current limitations (Xcode 26):
- Swift Package Manager dependencies not yet cacheable
- CompileStoryboard, CompileXIB, DataModelCompile, Ld tasks not cacheable
- Cache requires time to populate on first run
Expected impact: 20-40% faster clean builds after initial cache population (up to 70%+ for favorable projects).
Pattern 10: Explicitly Built Modules (Xcode 16+, HIGH IMPACT)
What it is: Xcode splits module compilation into explicit build tasks instead of implicit on-demand compilation. Enabled by default for Swift in Xcode 26.
The Problem with Implicit Modules (Pre-Xcode 16):
When a compiler encounters an import, it builds the module on-demand:
Compile A.swift ─── needs UIKit ───→ (builds UIKit.pcm) ───→ continues Compile B.swift ─── needs UIKit ───→ (waits for A to finish) ───→ uses cached Compile C.swift ─── needs UIKit ───→ (waits) ───→ uses cached
Problems:
- One task blocks others waiting for the same module
- Non-deterministic: whoever gets there first builds it
- Build failures hard to reproduce (depends on task order)
Explicitly Built Modules Solution:
Xcode now separates compilation into three phases:
Phase 1: SCAN Phase 2: BUILD MODULES Phase 3: COMPILE ┌──────────────────┐ ┌──────────────────────┐ ┌──────────────────┐ │ Scan A.swift │ │ Build UIKit.pcm │ │ Compile A.swift │ │ Scan B.swift │ → │ Build Foundation.pcm │ → │ Compile B.swift │ │ Scan C.swift │ │ Build SwiftUI.pcm │ │ Compile C.swift │ └──────────────────┘ └──────────────────────┘ └──────────────────┘ (fast) (parallel) (parallel)
Benefits:
- More reliable builds: Precise dependencies, deterministic build graphs
- More efficient scheduling: Build system knows exactly what's needed
- Better debugging: Debugger reuses built modules (no separate rebuild)
- Visible module tasks: See "Compile Clang Module" and "Compile Swift Module" in build log
Enable/Disable (if needed):
Build Settings → Explicitly Built Modules → YES (default in Xcode 26 for Swift)
Module Variants (WWDC 2024-10171)
The same module may be built multiple times with different settings:
Build Log: Compile Clang module 'UIKit' (hash: abc123) ← Variant 1 Compile Clang module 'UIKit' (hash: def456) ← Variant 2 Compile Swift module 'UIKit' (hash: ghi789) ← Variant 3
Common causes of variants:
- Different preprocessor macros between targets
- Mixed C and Objective-C language modes
- Different C language versions (C11 vs C17)
- Disabling ARC on some targets
Diagnose variants:
- Build with Timing Summary:
Product → Perform Action → Build with Timing Summary - Filter build log: Type "modules report" in filter box
- View Clang and Swift module reports showing variant counts
Reduce variants (unify settings at project/workspace level):
# Check for macro differences grep "GCC_PREPROCESSOR_DEFINITIONS" project.pbxproj # Move target-specific macros to project level where possible Project → Build Settings → Preprocessor Macros → [unify here]
Example (from WWDC 2024-10171):
Before: 4 UIKit variants (2 Swift × 2 Clang) After: 2 UIKit variants (unified settings) Impact: Fewer module builds = faster incremental builds
Expected impact: 10-30% faster builds by reducing duplicate module compilation.
Note: Swift Build (Xcode 26+): Xcode now uses Swift Build, Apple's open-source build engine. This provides more predictable builds, better SPM integration, and cross-platform support (Linux, Windows, Android). No configuration needed.
Measurement & Verification
Before and After Comparison
Required steps:
-
Baseline (before changes):
xcodebuild clean build -scheme YourScheme 2>&1 | tee baseline.log -
Apply ONE optimization at a time
-
Measure improvement:
xcodebuild clean build -scheme YourScheme 2>&1 | tee optimized.log -
Compare:
# Extract build time from logs grep "Build succeeded" baseline.log grep "Build succeeded" optimized.log
Example:
Baseline: Build succeeded (247.3 seconds) Optimized: Build succeeded (156.8 seconds) Improvement: 90.5 seconds (36.6% faster)
Build Timeline Visual Verification
Before optimization:
- Look for empty vertical space (idle cores)
- Long horizontal bars (slow tasks)
- Serial target builds
After optimization:
- Timeline should be more "filled"
- Shorter horizontal bars
- Parallel target builds
Critical path: Should be visibly shorter.
Real-World Optimization Examples
Example 1: Large iOS App (50+ source files)
Baseline:
- Clean build: 247 seconds
- Incremental (1 file): 12 seconds
Optimizations applied:
- Debug compilation mode: singlefile (saved 89s)
- Build Active Architecture: YES (saved 45s)
- Conditional dSYM upload script (saved 6.3s per incremental)
Result:
- Clean build: 156 seconds (36% faster)
- Incremental: 5.7 seconds (52% faster)
Example 2: Multi-Framework Project
Baseline:
- 5 frameworks built serially
- Total: 189 seconds
Optimizations applied:
- Enabled parallel builds in scheme
- Fixed unnecessary dependencies
- Emit module optimization (automatic in Xcode 14)
Result:
- Total: 94 seconds (50% faster)
- Critical path reduced from 189s to 94s
Common Pitfalls
Pitfall 1: Optimizing Without Measuring
Mistake: "I think this will help" → make change → no measurement.
Why bad: Placebo improvements, wasted time, actual regressions unnoticed.
Fix: Always measure before → change one thing → measure after.
Pitfall 2: Optimizing Release Builds for Speed
Mistake: Set Release to incremental compilation for "faster builds".
Why bad: Release builds should optimize for runtime performance, not build speed. You ship Release builds to users.
Fix: Only optimize Debug builds for speed. Keep Release optimized for runtime.
Pitfall 3: Breaking Dependencies for Parallelization
Mistake: Remove legitimate dependencies to "make builds parallel".
Why bad: Build errors, undefined behavior, race conditions.
Fix: Only parallelize truly independent targets. Use Build Timeline to identify safe opportunities.
Pitfall 4: Enabling FUSE_BUILD_SCRIPT_PHASES Without Sandboxing
Mistake: Enable parallel scripts but don't declare inputs/outputs.
Why bad: Data races, non-deterministic build failures, incorrect builds.
Fix: First enable
ENABLE_USER_SCRIPT_SANDBOXING = YES, fix all errors, THEN enable FUSE_BUILD_SCRIPT_PHASES.
Troubleshooting
Problem: Builds Still Slow After Optimizations
Check:
- Did you clean before measuring? (
)xcodebuild clean - Are you measuring the right build? (Debug vs Release)
- Is your machine thermal throttling? (Activity Monitor → CPU tab)
- Are other apps using CPU? (Quit Xcode, Docker, VMs during measurement)
Problem: Build Timeline Shows No Parallelization
Check:
- Scheme → Parallelize Build checked?
- Are targets actually independent? (Check dependency graph)
- Do targets have unnecessary explicit dependencies?
Problem: Type Checking Warnings Don't Appear
Check:
- Added flags to correct configuration? (Debug, not Release)
- Syntax correct?
(with hyphen)-warn-long-function-bodies 100 - Building the right scheme?
- Clean build to force recompilation
Advanced: Analyzing Build Logs
Extract Compilation Times
# Find slowest files to compile xcodebuild -workspace YourApp.xcworkspace \ -scheme YourScheme \ clean build \ OTHER_SWIFT_FLAGS="-Xfrontend -debug-time-function-bodies" 2>&1 | \ grep ".[0-9]ms" | \ sort -nr | \ head -20
Output:
247.3ms MyViewModel.swift:42:1 func calculateTotal 156.8ms LoginView.swift:18:3 var body 89.2ms NetworkManager.swift:67:1 func handleResponse ...
Action: Add explicit types to slowest functions.
Extract Build Phase Times
# From build log Build target 'MyApp' (project 'MyApp') Compile Swift source files (128.4 seconds) Link MyApp (12.3 seconds) Run custom shell script (6.7 seconds)
Action: Optimize the longest phase first.
Checklist: Build Performance Audit
Before considering your build optimized:
Measurement
- Measured baseline (clean + incremental)
- Verified improvement in Build Timeline
- Documented baseline → optimized comparison
Compilation Settings
- Debug uses incremental compilation
- Build Active Architecture = YES (Debug only)
- Debug uses DWARF (not dSYM)
- Type checking warnings enabled
- Fixed slow type-checking functions (>100ms)
Parallelization
- Parallelize Build enabled in scheme
- No unnecessary target dependencies
- Build phase scripts are conditional (skip in Debug when possible)
- Enabled script sandboxing if using parallel scripts
Xcode 26+ (if applicable)
- Compilation caching enabled for CI/CD (
)COMPILATION_CACHE_ENABLE_CACHING - Checked module variants (Modules Report in build log, see Pattern 10)
- Unified build settings at project level to reduce module variants
- Explicitly Built Modules enabled (default for Swift in Xcode 26)
Resources
WWDC: 2018-408, 2022-110364, 2024-10171, 2025-247
Docs: /xcode/improving-the-speed-of-incremental-builds, /xcode/building-your-project-with-explicit-module-dependencies
Tools: Xcode Build Timeline (Xcode 14+), Build with Timing Summary (Product → Perform Action), Modules Report (Xcode 16+), Instruments Time Profiler
Remember: Build performance optimization is about systematic measurement and targeted improvements. Optimize the critical path first, measure everything, and verify improvements in the Build Timeline.