| 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
-
Build Time Analysis
- Measure file-by-file compilation times
- Identify slowest compilation units
- Track build time trends
- Compare incremental vs full builds
-
Compilation Bottleneck Detection
- Find type-checker performance issues
- Identify expensive expressions
- Detect slow generics instantiation
- Find protocol witness table generation overhead
-
Optimization Suggestions
- Suggest type annotation additions
- Recommend breaking up complex expressions
- Identify modularization opportunities
- Propose build settings improvements
-
Dependency Optimization
- Reduce header imports
- Minimize cross-module dependencies
- Suggest forward declarations
- Optimize framework linking
-
Reporting
- Generate build time reports
- Show compilation timeline
- Track improvements over time
- Export analysis data
Workflow
When invoked, this skill will:
- Profile: Run instrumented build
- Analyze: Parse build logs and timings
- Identify: Find bottlenecks and issues
- Recommend: Suggest specific improvements
- Measure: Verify optimizations
Usage Instructions
When the user invokes this skill:
-
Ask analysis scope:
- Full build analysis
- Incremental build check
- Specific target
- Recent changes impact
-
Choose detail level:
- Quick overview
- Per-file breakdown
- Function-level analysis
- Deep type-checker profiling
-
Run analysis
-
Generate report with recommendations
-
Apply suggested fixes
Build Time Measurement
1. Enable Build Timing
OTHER_SWIFT_FLAGS = -Xfrontend -debug-time-function-bodies -Xfrontend -debug-time-compilation
OTHER_SWIFT_FLAGS = -Xfrontend -warn-long-function-bodies=100 -Xfrontend -warn-long-expression-type-checking=100
2. Build and Extract Timing
#!/bin/bash
xcodebuild clean build \
-project PaleoRose.xcodeproj \
-scheme PaleoRose \
-configuration Debug \
OTHER_SWIFT_FLAGS="-Xfrontend -debug-time-function-bodies" \
| tee build-log.txt
grep "warning.*took" build-log.txt | \
sed 's/.*warning: //' | \
sort -t' ' -k3 -n -r | \
head -20
3. Parse Build Logs
#!/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] = []
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] = []
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"
report += "## Slowest Files to Compile\n\n"
for (index, file) in files.prefix(20).enumerated() {
report += "\(index + 1). \(file.file): \(file.duration)s\n"
}
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"
}
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
}
}
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)
try? report.write(toFile: "build-time-report.md", atomically: true, encoding: .utf8)
Common Optimization Strategies
1. Add Type Annotations
let graphics = layers
.filter { $0.isVisible }
.flatMap { layer in
(0..<binCount).map { bin in
createGraphic(layer, bin)
}
}
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
let result = data
.filter { $0.value > threshold && $0.isValid }
.map { calculateComplexValue($0) }
.reduce(initialValue) { acc, val in
acc + val * multiplier - offset
}
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
func process<T: Codable & Hashable & CustomStringConvertible>(
items: [T]
) where T: Comparable {
}
protocol ProcessableItem: Codable, Hashable, CustomStringConvertible, Comparable {}
func process<T: ProcessableItem>(items: [T]) {
}
4. Use Concrete Types in Internal APIs
private func formatValue<T: CustomStringConvertible>(_ value: T) -> String {
return "Value: \(value.description)"
}
private func formatValue(_ value: String) -> String {
return "Value: \(value)"
}
5. Reduce @objc Dynamic Dispatch
class GraphicPetal: Graphic {
@objc var lineWidth: Float = 1.0
@objc func calculateGeometry() { }
}
class GraphicPetal: Graphic {
var lineWidth: Float = 1.0
func calculateGeometry() { }
}
6. Optimize Protocol Witness Tables
extension GraphicPetal: CustomStringConvertible { }
extension GraphicPetal: CustomDebugStringConvertible { }
extension GraphicPetal: Equatable { }
extension GraphicPetal: Hashable { }
class GraphicPetal: Graphic,
CustomStringConvertible,
CustomDebugStringConvertible,
Equatable,
Hashable {
}
7. Module Organization
Build Settings Optimization
Xcode Build Settings
SWIFT_COMPILATION_MODE = wholemodule
SWIFT_COMPILATION_MODE = incremental
SWIFT_OPTIMIZATION_LEVEL = -O
SWIFT_OPTIMIZATION_LEVEL = -Onone
SWIFT_WHOLE_MODULE_OPTIMIZATION = YES
SWIFT_WHOLE_MODULE_OPTIMIZATION = NO
SWIFT_PARALLEL_COMPILATION = YES
COMPILER_INDEX_STORE_ENABLE = NO
DEBUG_INFORMATION_FORMAT = dwarf
Project-Specific Settings
SWIFT_PARALLEL_COMPILATION = YES
SWIFT_COMPILATION_MODE = incremental
SWIFT_ACTIVE_COMPILATION_CONDITIONS[config=Debug] = DEBUG
SWIFT_OPTIMIZATION_LEVEL[config=Debug] = -Onone
SWIFT_OPTIMIZATION_LEVEL[config=Release] = -O
SWIFT_COMPILATION_MODE[config=Release] = wholemodule
Automated Analysis Tools
Build Time Tracker Script
#!/usr/bin/swift
import Foundation
struct BuildTimeTracker {
let projectPath: String
func track() {
print("🔨 Building project...")
let startTime = Date()
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")
analyzeBuildLog()
}
func analyzeBuildLog() {
let parser = BuildTimeParser()
let files = parser.parseXcodeBuildLog("build.log")
saveHistoricalData(files)
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))
)
}
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")
}
checkRegressions(files)
}
func checkRegressions(_ files: [BuildTimeParser.CompilationTime]) {
}
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
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
<!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
Configuration
Store optimization settings in .build-optimizer.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"
}
}
}