axiom-concurrency-profiling▌
charleswiltgen/axiom · updated Apr 8, 2026
Profile and optimize Swift async/await code using Instruments.
Concurrency Profiling — Instruments Workflows
Profile and optimize Swift async/await code using Instruments.
When to Use
✅ Use when:
- UI stutters during async operations
- Suspecting actor contention
- Tasks queued but not executing
- Main thread blocked during async work
- Need to visualize task execution flow
❌ Don't use when:
- Issue is pure CPU performance (use Time Profiler)
- Memory issues unrelated to concurrency (use Allocations)
- Haven't confirmed concurrency is the bottleneck
Swift Concurrency Template
What It Shows
| Track | Information |
|---|---|
| Swift Tasks | Task lifetimes, parent-child relationships |
| Swift Actors | Actor access, contention visualization |
| Thread States | Blocked vs running vs suspended |
Statistics
- Running Tasks: Tasks currently executing
- Alive Tasks: Tasks present at a point in time
- Total Tasks: Cumulative count created
Color Coding
- Blue: Task executing
- Red: Task waiting (contention)
- Gray: Task suspended (awaiting)
Workflow 1: Diagnose Main Thread Blocking
Symptom: UI freezes, main thread timeline full
- Profile with Swift Concurrency template
- Look at main thread → "Swift Tasks" lane
- Find long blue bars (task executing on main)
- Check if work could be offloaded
Solution patterns:
// ❌ Heavy work on MainActor
@MainActor
class ViewModel: ObservableObject {
func process() {
let result = heavyComputation() // Blocks UI
self.data = result
}
}
// ✅ Offload heavy work
@MainActor
class ViewModel: ObservableObject {
func process() async {
let result = await Task.detached {
heavyComputation()
}.value
self.data = result
}
}
Workflow 2: Find Actor Contention
Symptom: Tasks serializing unexpectedly, parallel work running sequentially
- Enable "Swift Actors" instrument
- Look for serialized access patterns
- Red = waiting, Blue = executing
- High red:blue ratio = contention problem
Solution patterns:
// ❌ All work serialized through actor
actor DataProcessor {
func process(_ data: Data) -> Result {
heavyProcessing(data) // All callers wait
}
}
// ✅ Mark heavy work as nonisolated
actor DataProcessor {
nonisolated func process(_ data: Data) -> Result {
heavyProcessing(data) // Runs in parallel
}
func storeResult(_ result: Result) {
// Only actor state access serialized
}
}
More fixes:
- Split actor into multiple (domain separation)
- Use Mutex for hot paths (faster than actor hop)
- Reduce actor scope (fewer isolated properties)
Workflow 3: Thread Pool Exhaustion
Symptom: Tasks queued but not executing, gaps in task execution
Cause: Blocking calls exhaust cooperative pool
- Look for gaps in task execution across all threads
- Check for blocking primitives
- Replace with async equivalents
Common culprits:
// ❌ Blocks cooperative thread
Task {
semaphore.wait() // NEVER do this
// ...
semaphore.signal()
}
// ❌ Synchronous file I/O in async context
Task {
let data = Data(contentsOf: fileURL) // Blocks
}
// ✅ Use async APIs
Task {
let (data, _) = try await URLSession.shared.data(from: fileURL)
}
Debug flag:
SWIFT_CONCURRENCY_COOPERATIVE_THREAD_BOUNDS=1
Detects unsafe blocking in async context.
Workflow 4: Priority Inversion
Symptom: High-priority task waits for low-priority
- Inspect task priorities in Instruments
- Follow wait chains
- Ensure critical paths use appropriate priority
// ✅ Explicit priority for critical work
Task(priority: .userInitiated) {
await criticalUIUpdate()
}
Thread Pool Model
Swift uses a cooperative thread pool matching CPU core count:
| Aspect | GCD | Swift Concurrency |
|---|---|---|
| Threads | Grows unbounded | Fixed to core count |
| Blocking | Creates new threads | Suspends, frees thread |
| Dependencies | Hidden | Runtime-tracked |
| Context switch | Full kernel switch | Lightweight continuation |
Why blocking is catastrophic:
- Each blocked thread holds memory + kernel structures
- Limited threads means blocked = no progress
- Pool exhaustion deadlocks the app
Quick Checks (Before Profiling)
Run these checks first:
-
Is work actually async?
- Look for suspension points (
await) - Sync code in async function still blocks
- Look for suspension points (
-
Holding locks across await?
// ❌ Deadlock risk mutex.withLock { await something() // Never! } -
Tasks in tight loops?
// ❌ Overhead may exceed benefit for item in items { Task { process(item) } } // ✅ Structured concurrency await withTaskGroup(of: Void.self) { group in for item in items { group.addTask { process(item) } } } -
DispatchSemaphore in async context?
- Always unsafe — use
withCheckedContinuationinstead
- Always unsafe — use
Common Issues Summary
| Issue | Symptom in Instruments | Fix |
|---|---|---|
| MainActor overload | Long blue bars on main | Task.detached, nonisolated |
| Actor contention | High red:blue ratio | Split actors, use nonisolated |
| Thread exhaustion | Gaps in all threads | Remove blocking calls |
| Priority inversion | High-pri waits for low-pri | Check task priorities |
| Too many tasks | Task creation overhead | Use task groups |
Safe vs Unsafe Primitives
Safe with cooperative pool:
await, actors, task groupsos_unfair_lock,NSLock(short critical sections)Mutex(iOS 18+)
Unsafe (violate forward progress):
DispatchSemaphore.wait()pthread_cond_wait- Sync file/network I/O
Thread.sleep()in Task
Resources
WWDC: 2022-110350, 2021-10254
Docs: /xcode/improving-app-responsiveness
Skills: axiom-swift-concurrency, axiom-performance-profiling, axiom-synchronization, axiom-lldb (interactive thread state inspection)
Discussion
Product Hunt–style comments (not star reviews)- No comments yet — start the thread.
Ratings
4.6★★★★★62 reviews- ★★★★★Kabir Okafor· Dec 28, 2024
I recommend axiom-concurrency-profiling for anyone iterating fast on agent tooling; clear intent and a small, reviewable surface area.
- ★★★★★Neel Harris· Dec 20, 2024
axiom-concurrency-profiling fits our agent workflows well — practical, well scoped, and easy to wire into existing repos.
- ★★★★★Olivia Reddy· Dec 20, 2024
axiom-concurrency-profiling has been reliable in day-to-day use. Documentation quality is above average for community skills.
- ★★★★★Aanya Nasser· Dec 12, 2024
Useful defaults in axiom-concurrency-profiling — fewer surprises than typical one-off scripts, and it plays nicely with `npx skills` flows.
- ★★★★★Daniel Chen· Dec 8, 2024
We added axiom-concurrency-profiling from the explainx registry; install was straightforward and the SKILL.md answered most questions upfront.
- ★★★★★Kabir Desai· Nov 19, 2024
Solid pick for teams standardizing on skills: axiom-concurrency-profiling is focused, and the summary matches what you get after install.
- ★★★★★Noor Mehta· Nov 11, 2024
Useful defaults in axiom-concurrency-profiling — fewer surprises than typical one-off scripts, and it plays nicely with `npx skills` flows.
- ★★★★★Aisha Lopez· Nov 11, 2024
We added axiom-concurrency-profiling from the explainx registry; install was straightforward and the SKILL.md answered most questions upfront.
- ★★★★★Alexander Li· Nov 3, 2024
axiom-concurrency-profiling has been reliable in day-to-day use. Documentation quality is above average for community skills.
- ★★★★★Alexander Thomas· Oct 22, 2024
Solid pick for teams standardizing on skills: axiom-concurrency-profiling is focused, and the summary matches what you get after install.
showing 1-10 of 62