build-time-optimizer
Analyze and optimize Xcode build times by identifying slow compilation units and suggesting improvements
- Category: discovered
- Tags: github, community
- Version: 1.0.0
- Author: walid
- Downloads: 0
- Updated: 2025-11-22
Download the latest version (v1.0.0) from this page.
SKILL.md
---
description: Analyze and optimize Xcode build times by identifying slow compilation units and suggesting improvements
---
# Build Time Optimizer
Analyze and optimize Xcode build times by identifying slow compilation units and suggesting improvements.
## Capabilities
1. **Build Time Analysis**
- Measure file-by-file compilation times
- Identify slowest compilation units
- Track build time trends
- Compare incremental vs full builds
2. **Compilation Bottleneck Detection**
- Find type-checker performance issues
- Identify expensive expressions
- Detect slow generics instantiation
- Find protocol witness table generation overhead
3. **Optimization Suggestions**
- Suggest type annotation additions
- Recommend breaking up complex expressions
- Identify modularization opportunities
- Propose build settings improvements
4. **Dependency Optimization**
- Reduce header imports
- Minimize cross-module dependencies
- Suggest forward declarations
- Optimize framework linking
5. **Reporting**
- Generate build time reports
- Show compilation timeline
- Track improvements over time
- Export analysis data
## Workflow
When invoked, this skill will:
1. **Profile**: Run instrumented build
2. **Analyze**: Parse build logs and timings
3. **Identify**: Find bottlenecks and issues
4. **Recommend**: Suggest specific improvements
5. **Measure**: Verify optimizations
## Usage Instructions
When the user invokes this skill:
1. Ask analysis scope:
- Full build analysis
- Incremental build check
- Specific target
- Recent changes impact
2. Choose detail level:
- Quick overview
- Per-file breakdown
- Function-level analysis
- Deep type-checker profiling
3. Run analysis
4. Generate report with recommendations
5. Apply suggested fixes
## Build Time Measurement
### 1. Enable Build Timing
```bash
# Add to Xcode build settings or .xcconfig file
OTHER_SWIFT_FLAGS = -Xfrontend -debug-time-function-bodies -Xfrontend -debug-time-compilation
# For detailed type checking times
OTHER_SWIFT_FLAGS = -Xfrontend -warn-long-function-bodies=100 -Xfrontend -warn-long-expression-type-checking=100
```
### 2. Build and Extract Timing
```bash
#!/bin/bash
# measure-build-time.sh
# Clean build with timing
xcodebuild clean build \
-project PaleoRose.xcodeproj \
-scheme PaleoRose \
-configuration Debug \
OTHER_SWIFT_FLAGS="-Xfrontend -debug-time-function-bodies" \
| tee build-log.txt
# Extract timing information
grep "warning.*took" build-log.txt | \
sed 's/.*warning: //' | \
sort -t' ' -k3 -n -r | \
head -20
```
### 3. Parse Build Logs
```swift
#!/usr/bin/swift
import Foundation
struct BuildTimeParser {
struct CompilationTime {
let file: String
let duration: Double
let warnings: [String]
}
struct FunctionTime {
let function: String
let file: String
let line: Int
let duration: Double
}
func parseXcodeBuildLog(_ logPath: String) -> [CompilationTime] {
guard let content = try? String(contentsOfFile: logPath) else {
return []
}
var times: [CompilationTime] = []
// Parse compilation times
let pattern = #"CompileSwift.*?(\S+\.swift).*?(\d+\.\d+) seconds"#
let regex = try! NSRegularExpression(pattern: pattern)
let matches = regex.matches(
in: content,
range: NSRange(content.startIndex..., in: content)
)
for match in matches {
if let fileRange = Range(match.range(at: 1), in: content),
let timeRange = Range(match.range(at: 2), in: content) {
let file = String(content[fileRange])
let duration = Double(String(content[timeRange])) ?? 0
times.append(CompilationTime(
file: file,
duration: duration,
warnings: []
))
}
}
return times.sorted { $0.duration > $1.duration }
}
func parseFunctionTimes(_ logPath: String) -> [FunctionTime] {
guard let content = try? String(contentsOfFile: logPath) else {
return []
}
var times: [FunctionTime] = []
// Parse function body compilation warnings
let pattern = #"(\S+\.swift):(\d+):\d+: warning: .*'(.+?)' took (\d+)ms"#
let regex = try! NSRegularExpression(pattern: pattern)
let matches = regex.matches(
in: content,
range: NSRange(content.startIndex..., in: content)
)
for match in matches {
if let fileRange = Range(match.range(at: 1), in: content),
let lineRange = Range(match.range(at: 2), in: content),
let funcRange = Range(match.range(at: 3), in: content),
let timeRange = Range(match.range(at: 4), in: content) {
times.append(FunctionTime(
function: String(content[funcRange]),
file: String(content[fileRange]),
line: Int(String(content[lineRange])) ?? 0,
duration: Double(String(content[timeRange])) ?? 0
))
}
}
return times.sorted { $0.duration > $1.duration }
}
func generateReport(
files: [CompilationTime],
functions: [FunctionTime]
) -> String {
var report = "# Build Time Analysis Report\n\n"
// File compilation times
report += "## Slowest Files to Compile\n\n"
for (index, file) in files.prefix(20).enumerated() {
report += "\(index + 1). \(file.file): \(file.duration)s\n"
}
// Function compilation times
report += "\n## Slowest Functions to Type-Check\n\n"
for (index, func) in functions.prefix(20).enumerated() {
report += "\(index + 1). \(func.function) (\(func.file):\(func.line)): \(func.duration)ms\n"
}
// Summary stats
let totalTime = files.reduce(0) { $0 + $1.duration }
let avgTime = totalTime / Double(files.count)
report += "\n## Summary\n\n"
report += "- Total files: \(files.count)\n"
report += "- Total compilation time: \(totalTime)s\n"
report += "- Average per file: \(String(format: "%.2f", avgTime))s\n"
report += "- Slowest file: \(files.first?.file ?? "N/A") (\(files.first?.duration ?? 0)s)\n"
return report
}
}
// Usage
let parser = BuildTimeParser()
let files = parser.parseXcodeBuildLog("build-log.txt")
let functions = parser.parseFunctionTimes("build-log.txt")
let report = parser.generateReport(files: files, functions: functions)
print(report)
// Save report
try? report.write(toFile: "build-time-report.md", atomically: true, encoding: .utf8)
```
## Common Optimization Strategies
### 1. Add Type Annotations
```swift
// Before: Slow type inference
let graphics = layers
.filter { $0.isVisible }
.flatMap { layer in
(0..<binCount).map { bin in
createGraphic(layer, bin) // Complex inference
}
}
// After: Explicit types help compiler
let graphics: [Graphic] = layers
.filter { $0.isVisible }
.flatMap { (layer: Layer) -> [Graphic] in
(0..<binCount).map { (bin: Int) -> Graphic in
createGraphic(layer, bin)
}
}
```
### 2. Break Up Complex Expressions
```swift
// Before: Single complex expression (slow to type-check)
let result = data
.filter { $0.value > threshold && $0.isValid }
.map { calculateComplexValue($0) }
.reduce(initialValue) { acc, val in
acc + val * multiplier - offset
}
// After: Split into steps
let filtered = data.filter { $0.value > threshold && $0.isValid }
let mapped = filtered.map(calculateComplexValue)
let result = mapped.reduce(initialValue) { acc, val in
acc + val * multiplier - offset
}
```
### 3. Simplify Generic Constraints
```swift
// Before: Complex generic constraints
func process<T: Codable & Hashable & CustomStringConvertible>(
items: [T]
) where T: Comparable {
// ...
}
// After: Create protocol composition
protocol ProcessableItem: Codable, Hashable, CustomStringConvertible, Comparable {}
func process<T: ProcessableItem>(items: [T]) {
// Faster to compile
}
```
### 4. Use Concrete Types in Internal APIs
```swift
// Before: Generic internal function (slower)
private func formatValue<T: CustomStringConvertible>(_ value: T) -> String {
return "Value: \(value.description)"
}
// After: Concrete type for internal use
private func formatValue(_ value: String) -> String {
return "Value: \(value)"
}
```
### 5. Reduce @objc Dynamic Dispatch
```swift
// Before: Unnecessary @objc (bridges to Obj-C runtime)
class GraphicPetal: Graphic {
@objc var lineWidth: Float = 1.0
@objc func calculateGeometry() { }
}
// After: Remove if not needed by Obj-C
class GraphicPetal: Graphic {
var lineWidth: Float = 1.0
func calculateGeometry() { }
}
```
### 6. Optimize Protocol Witness Tables
```swift
// Before: Many small protocol conformances
extension GraphicPetal: CustomStringConvertible { }
extension GraphicPetal: CustomDebugStringConvertible { }
extension GraphicPetal: Equatable { }
extension GraphicPetal: Hashable { }
// After: Consolidate in main declaration
class GraphicPetal: Graphic,
CustomStringConvertible,
CustomDebugStringConvertible,
Equatable,
Hashable {
// All conformances in one place - faster compilation
}
```
### 7. Module Organization
```swift
// Before: Everything in one target
// PaleoRose target contains:
// - Graphics (100 files)
// - Database (50 files)
// - UI (80 files)
// Total: 230 files rebuild on any change
// After: Separate frameworks
// PaleoRoseGraphics.framework (100 files)
// PaleoRoseDatabase.framework (50 files)
// PaleoRose app (80 files + frameworks)
// Only changed framework rebuilds
```
## Build Settings Optimization
### Xcode Build Settings
```ruby
# .xcconfig or Build Settings
# Parallel compilation
SWIFT_COMPILATION_MODE = wholemodule # For release
# OR
SWIFT_COMPILATION_MODE = incremental # For debug (faster incremental)
# Optimization level
SWIFT_OPTIMIZATION_LEVEL = -O # Release
SWIFT_OPTIMIZATION_LEVEL = -Onone # Debug (faster compile)
# Module linking
SWIFT_WHOLE_MODULE_OPTIMIZATION = YES # Release
SWIFT_WHOLE_MODULE_OPTIMIZATION = NO # Debug
# Parallel build
SWIFT_PARALLEL_COMPILATION = YES
# Index while building
COMPILER_INDEX_STORE_ENABLE = NO # Faster build, no indexing
# Debug info
DEBUG_INFORMATION_FORMAT = dwarf # Faster than dwarf-with-dsym for debug
```
### Project-Specific Settings
```ruby
# PaleoRose.xcconfig
# Increase build performance
SWIFT_PARALLEL_COMPILATION = YES
SWIFT_COMPILATION_MODE = incremental
# Only for debug builds
SWIFT_ACTIVE_COMPILATION_CONDITIONS[config=Debug] = DEBUG
SWIFT_OPTIMIZATION_LEVEL[config=Debug] = -Onone
# Optimize release builds
SWIFT_OPTIMIZATION_LEVEL[config=Release] = -O
SWIFT_COMPILATION_MODE[config=Release] = wholemodule
```
## Automated Analysis Tools
### Build Time Tracker Script
```swift
#!/usr/bin/swift
import Foundation
struct BuildTimeTracker {
let projectPath: String
func track() {
print("🔨 Building project...")
let startTime = Date()
// Run build
let result = shell(
"""
cd \(projectPath) && \
xcodebuild clean build \
-project PaleoRose.xcodeproj \
-scheme PaleoRose \
-configuration Debug \
OTHER_SWIFT_FLAGS="-Xfrontend -debug-time-function-bodies" \
| tee build.log
"""
)
let duration = Date().timeIntervalSince(startTime)
print("✅ Build completed in \(duration)s")
// Parse and analyze
analyzeBuildLog()
}
func analyzeBuildLog() {
let parser = BuildTimeParser()
let files = parser.parseXcodeBuildLog("build.log")
// Save historical data
saveHistoricalData(files)
// Generate report
generateReport(files)
}
func saveHistoricalData(_ files: [BuildTimeParser.CompilationTime]) {
let data = BuildTimeData(
date: Date(),
totalDuration: files.reduce(0) { $0 + $1.duration },
fileCount: files.count,
slowestFiles: Array(files.prefix(10))
)
// Append to historical log
// (JSON or SQLite database)
}
func generateReport(_ files: [BuildTimeParser.CompilationTime]) {
print("\n📊 Build Time Report\n")
print("Top 10 Slowest Files:")
for (index, file) in files.prefix(10).enumerated() {
let fileName = URL(fileURLWithPath: file.file).lastPathComponent
print("\(index + 1). \(fileName): \(file.duration)s")
}
// Check for regressions
checkRegressions(files)
}
func checkRegressions(_ files: [BuildTimeParser.CompilationTime]) {
// Compare against historical baseline
// Alert if any file >20% slower than average
}
private func shell(_ command: String) -> String {
let task = Process()
task.launchPath = "/bin/bash"
task.arguments = ["-c", command]
let pipe = Pipe()
task.standardOutput = pipe
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
return String(data: data, encoding: .utf8) ?? ""
}
}
struct BuildTimeData: Codable {
let date: Date
let totalDuration: Double
let fileCount: Int
let slowestFiles: [BuildTimeParser.CompilationTime]
}
```
### CI Integration
```yaml
# .github/workflows/build-performance.yml
name: Build Performance
on:
pull_request:
push:
branches: [main]
jobs:
measure-build-time:
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- name: Measure Build Time
run: |
swift run BuildTimeTracker --project .
- name: Upload Report
uses: actions/upload-artifact@v3
with:
name: build-time-report
path: build-time-report.md
- name: Comment PR
if: github.event_name == 'pull_request'
uses: actions/github-script@v6
with:
script: |
const fs = require('fs');
const report = fs.readFileSync('build-time-report.md', 'utf8');
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '## Build Time Report\n\n' + report
});
```
## Visualization
### Build Time Trend Chart
```html
<!DOCTYPE html>
<html>
<head>
<title>Build Time Trends</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body>
<canvas id="buildTimeChart"></canvas>
<script>
const ctx = document.getElementById('buildTimeChart');
new Chart(ctx, {
type: 'line',
data: {
labels: ['Week 1', 'Week 2', 'Week 3', 'Week 4'],
datasets: [{
label: 'Build Time (seconds)',
data: [120, 115, 108, 95],
borderColor: 'rgb(75, 192, 192)',
tension: 0.1
}]
},
options: {
scales: {
y: {
beginAtZero: true
}
}
}
});
</script>
</body>
</html>
```
## Quick Wins Checklist
- [ ] Enable incremental compilation for Debug builds
- [ ] Disable index-while-building for faster iteration
- [ ] Add explicit type annotations to complex expressions
- [ ] Break up files >500 lines
- [ ] Remove unused imports
- [ ] Consolidate protocol conformances
- [ ] Use concrete types for internal APIs
- [ ] Extract slow functions to separate files
- [ ] Consider modularization for large targets
- [ ] Profile and optimize top 10 slowest files
## Configuration
Store optimization settings in `.build-optimizer.json`:
```json
{
"trackHistory": true,
"historyPath": "./BuildMetrics/history.json",
"thresholds": {
"fileCompilationWarning": 5.0,
"functionTypeCheckWarning": 100
},
"xcodeBuildSettings": {
"debug": {
"SWIFT_COMPILATION_MODE": "incremental",
"SWIFT_OPTIMIZATION_LEVEL": "-Onone"
},
"release": {
"SWIFT_COMPILATION_MODE": "wholemodule",
"SWIFT_OPTIMIZATION_LEVEL": "-O"
}
}
}
```