Loading...
Loading...
Use when optimizing Swift code performance, reducing memory usage, improving runtime efficiency, dealing with COW, ARC overhead, generics specialization, or collection optimization
npx skill4agent add charleswiltgen/axiom axiom-swift-performance@concurrentaxiom-performance-profilingaxiom-swiftui-performanceaxiom-build-performanceaxiom-swift-concurrencyaxiom-performance-profilingaxiom-swiftui-performanceaxiom-build-performancePerformance issue identified?
│
├─ Profiler shows excessive copying?
│ └─ → Part 1: Noncopyable Types
│ └─ → Part 2: Copy-on-Write
│
├─ Retain/release overhead in Time Profiler?
│ └─ → Part 4: ARC Optimization
│
├─ Generic code in hot path?
│ └─ → Part 5: Generics & Specialization
│
├─ Collection operations slow?
│ └─ → Part 7: Collection Performance
│
├─ Async/await overhead visible?
│ └─ → Part 8: Concurrency Performance
│
├─ Struct vs class decision?
│ └─ → Part 3: Value vs Reference
│
└─ Memory layout concerns?
└─ → Part 9: Memory Layout// Noncopyable type
struct FileHandle: ~Copyable {
private let fd: Int32
init(path: String) throws {
self.fd = open(path, O_RDONLY)
guard fd != -1 else { throw FileError.openFailed }
}
deinit {
close(fd)
}
// Must explicitly consume
consuming func close() {
_ = consume self
}
}
// Usage
func processFile() throws {
let handle = try FileHandle(path: "/data.txt")
// handle is automatically consumed at end of scope
// Cannot accidentally copy handle
}// consuming - takes ownership, caller cannot use after
func process(consuming data: [UInt8]) {
// data is consumed
}
// borrowing - temporary access without ownership
func validate(borrowing data: [UInt8]) -> Bool {
// data can still be used by caller
return data.count > 0
}
// inout - mutable access
func modify(inout data: [UInt8]) {
data.append(0)
}var array1 = [1, 2, 3] // Single allocation
var array2 = array1 // Share storage (no copy)
array2.append(4) // Now copies (array1 modified array2)final class Storage<T> {
var data: [T]
init(_ data: [T]) { self.data = data }
}
struct COWArray<T> {
private var storage: Storage<T>
init(_ data: [T]) {
self.storage = Storage(data)
}
// COW check before mutation
private mutating func ensureUnique() {
if !isKnownUniquelyReferenced(&storage) {
storage = Storage(storage.data)
}
}
mutating func append(_ element: T) {
ensureUnique() // Copy if shared
storage.data.append(element)
}
subscript(index: Int) -> T {
get { storage.data[index] }
set {
ensureUnique() // Copy before mutation
storage.data[index] = newValue
}
}
}// ❌ Accidental copy in loop
for i in 0..<array.count {
array[i] = transform(array[i]) // Copy on first mutation if shared!
}
// ✅ Reserve capacity first (ensures unique)
array.reserveCapacity(array.count)
for i in 0..<array.count {
array[i] = transform(array[i])
}
// ❌ Multiple mutations trigger multiple uniqueness checks
array.append(1)
array.append(2)
array.append(3)
// ✅ Single reservation
array.reserveCapacity(array.count + 3)
array.append(contentsOf: [1, 2, 3])structclass| Factor | Use Struct | Use Class |
|---|---|---|
| Size | ≤ 64 bytes | > 64 bytes or contains large data |
| Identity | No identity needed | Needs identity (===) |
| Inheritance | Not needed | Inheritance required |
| Mutation | Infrequent | Frequent in-place updates |
| Sharing | No sharing needed | Must be shared across scope |
// ✅ Fast - fits in registers, no heap allocation
struct Point {
var x: Double // 8 bytes
var y: Double // 8 bytes
} // Total: 16 bytes - excellent for struct
struct Color {
var r, g, b, a: UInt8 // 4 bytes total - perfect for struct
}// ❌ Slow - excessive copying
struct HugeData {
var buffer: [UInt8] // 1MB
var metadata: String
}
func process(_ data: HugeData) { // Copies 1MB!
// ...
}
// ✅ Use reference semantics for large data
final class HugeData {
var buffer: [UInt8]
var metadata: String
}
func process(_ data: HugeData) { // Only copies pointer (8 bytes)
// ...
}// Best of both worlds
struct LargeDataWrapper {
private final class Storage {
var largeBuffer: [UInt8]
init(_ buffer: [UInt8]) { self.largeBuffer = buffer }
}
private var storage: Storage
init(buffer: [UInt8]) {
self.storage = Storage(buffer)
}
// Value semantics externally, reference internally
var buffer: [UInt8] {
get { storage.largeBuffer }
set {
if !isKnownUniquelyReferenced(&storage) {
storage = Storage(newValue)
} else {
storage.largeBuffer = newValue
}
}
}
}class Parent {
var child: Child?
}
class Child {
// ❌ Weak adds overhead (optional, thread-safe zeroing)
weak var parent: Parent?
}
// ✅ Unowned when you know lifetime guarantees
class Child {
unowned let parent: Parent // No overhead, crashes if parent deallocated
}unownedweakclass DataProcessor {
var data: [Int]
// ❌ Captures self strongly, then uses weak - unnecessary weak overhead
func process(completion: @escaping () -> Void) {
DispatchQueue.global().async { [weak self] in
guard let self else { return }
self.data.forEach { print($0) }
completion()
}
}
// ✅ Capture only what you need
func process(completion: @escaping () -> Void) {
let data = self.data // Copy value type
DispatchQueue.global().async {
data.forEach { print($0) } // No self captured
completion()
}
}
}// ❌ Multiple retain/release pairs
for object in objects {
process(object) // retain, release
}
// ✅ Single retain for entire loop
func processAll(_ objects: [MyClass]) {
// Compiler optimizes to single retain/release
for object in objects {
process(object)
}
}// ❌ Relying on observed lifetime is fragile
class Traveler {
weak var account: Account?
deinit {
print("Deinitialized") // May run BEFORE expected with ARC optimizations!
}
}
func test() {
let traveler = Traveler()
let account = Account(traveler: traveler)
// traveler's last use is above - may deallocate here!
account.printSummary() // weak reference may be nil!
}
// ✅ Explicitly extend lifetime when needed
func test() {
let traveler = Traveler()
let account = Account(traveler: traveler)
withExtendedLifetime(traveler) {
account.printSummary() // traveler guaranteed to live
}
}
// Alternative: defer at end of scope
func test() {
let traveler = Traveler()
defer { withExtendedLifetime(traveler) {} }
let account = Account(traveler: traveler)
account.printSummary()
}// Generic function
func process<T>(_ value: T) {
print(value)
}
// Calling with concrete type
process(42) // Compiler specializes: process_Int(42)
process("hello") // Compiler specializes: process_String("hello")protocol Drawable {
func draw()
}
// ❌ Existential container - expensive (heap allocation, indirection)
func drawAll(shapes: [any Drawable]) {
for shape in shapes {
shape.draw() // Dynamic dispatch through witness table
}
}
// ✅ Generic with constraint - can specialize
func drawAll<T: Drawable>(shapes: [T]) {
for shape in shapes {
shape.draw() // Static dispatch after specialization
}
}any Protocol// Existential Container Memory Layout (64-bit systems)
//
// Small Type (≤24 bytes):
// ┌──────────────────┬──────────────┬────────────────┐
// │ Value (inline) │ Type │ Protocol │
// │ 3 words max │ Metadata │ Witness Table │
// │ (24 bytes) │ (8 bytes) │ (8 bytes) │
// └──────────────────┴──────────────┴────────────────┘
// ↑ No heap allocation - value stored directly
//
// Large Type (>24 bytes):
// ┌──────────────────┬──────────────┬────────────────┐
// │ Heap Pointer → │ Type │ Protocol │
// │ (8 bytes) │ Metadata │ Witness Table │
// │ │ (8 bytes) │ (8 bytes) │
// └──────────────────┴──────────────┴────────────────┘
// ↑ Heap allocation required - pointer to actual value
//
// Total container size: 40 bytes (5 words on 64-bit)
// Threshold: 3 words (24 bytes) determines inline vs heap
// Small type example - stored inline (FAST)
struct Point: Drawable {
var x, y, z: Double // 24 bytes - fits inline!
}
let drawable: any Drawable = Point(x: 1, y: 2, z: 3)
// ✅ Point stored directly in container (no heap allocation)
// Large type example - heap allocated (SLOWER)
struct Rectangle: Drawable {
var x, y, width, height: Double // 32 bytes - exceeds inline buffer
}
let drawable: any Drawable = Rectangle(x: 0, y: 0, width: 10, height: 20)
// ❌ Rectangle allocated on heap, container stores pointer
// Performance comparison:
// - Small existential (≤24 bytes): ~5ns access time
// - Large existential (>24 bytes): ~15ns access time (heap indirection)
// - Generic `some Drawable`: ~2ns access time (no container)any Protocolsome Protocolany Protocol@_specialize// Force specialization for common types
@_specialize(where T == Int)
@_specialize(where T == String)
func process<T: Comparable>(_ value: T) -> T {
// Expensive generic operation
return value
}
// Compiler generates:
// - func process_Int(_ value: Int) -> Int
// - func process_String(_ value: String) -> String
// - Generic fallback for other typesanysome// ❌ any - existential, runtime overhead
func makeDrawable() -> any Drawable {
return Circle() // Heap allocation
}
// ✅ some - opaque type, compile-time type
func makeDrawable() -> some Drawable {
return Circle() // No overhead, type known at compile time
}// ✅ Small, frequently called functions
@inlinable
public func fastAdd(_ a: Int, _ b: Int) -> Int {
return a + b
}
// ❌ Large functions - code bloat
@inlinable // Don't do this!
public func complexAlgorithm() {
// 100 lines of code...
}// Framework code
public struct Point {
public var x: Double
public var y: Double
// ✅ Inlinable for cross-module optimization
@inlinable
public func distance(to other: Point) -> Double {
let dx = x - other.x
let dy = y - other.y
return sqrt(dx*dx + dy*dy)
}
}
// Client code
let p1 = Point(x: 0, y: 0)
let p2 = Point(x: 3, y: 4)
let d = p1.distance(to: p2) // Inlined across module boundary@usableFromInline// Internal helper that can be inlined
@usableFromInline
internal func helperFunction() { }
// Public API that uses it
@inlinable
public func publicAPI() {
helperFunction() // Can inline internal function
}@inlinable// ❌ Array<T> - may use NSArray bridging (Swift/ObjC interop)
let array: Array<Int> = [1, 2, 3]
// ✅ ContiguousArray<T> - guaranteed contiguous memory (no bridging)
let array: ContiguousArray<Int> = [1, 2, 3]ContiguousArray// ❌ Multiple reallocations
var array: [Int] = []
for i in 0..<10000 {
array.append(i) // Reallocates ~14 times
}
// ✅ Single allocation
var array: [Int] = []
array.reserveCapacity(10000)
for i in 0..<10000 {
array.append(i) // No reallocations
}struct BadKey: Hashable {
var data: [Int]
// ❌ Expensive hash (iterates entire array)
func hash(into hasher: inout Hasher) {
for element in data {
hasher.combine(element)
}
}
}
struct GoodKey: Hashable {
var id: UUID // Fast hash
var data: [Int] // Not hashed
// ✅ Hash only the unique identifier
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
}// Traditional Array - heap allocated, COW overhead
var sprites: [Sprite] = Array(repeating: .default, count: 40)
// InlineArray - stack allocated, no COW
var sprites = InlineArray<40, Sprite>(repeating: .default)
// Alternative syntax (if available)
var sprites: [40 of Sprite] = ...let inline = InlineArray<10, Int>(repeating: 0)
// ✅ Stack allocated - no heap
print(MemoryLayout.size(ofValue: inline)) // 80 bytes (10 × 8)
// ✅ Value semantics - but eagerly copied (not COW!)
var copy = inline // Copies all 10 elements immediately
copy[0] = 100 // No COW check needed
// ✅ Provides Span access for zero-copy operations
let span = inline.span // Read-only view
let mutableSpan = inline.mutableSpan // Mutable view// Array: Heap allocation + COW overhead
var array = Array(repeating: 0, count: 100)
// - Allocation: ~1μs (heap)
// - Copy: ~50ns (COW reference bump)
// - Mutation: ~50ns (uniqueness check)
// InlineArray: Stack allocation, no COW
var inline = InlineArray<100, Int>(repeating: 0)
// - Allocation: 0ns (stack frame)
// - Copy: ~400ns (eager copy all 100 elements)
// - Mutation: 0ns (no uniqueness check)// Existential containers store ≤24 bytes inline
struct Small: Protocol {
var a, b, c: Int64 // 24 bytes - fits inline
}
// InlineArray of 3 Int64s also ≤24 bytes
let inline = InlineArray<3, Int64>(repeating: 0)
// Size: 24 bytes - same threshold, different purpose
// Both avoid heap allocation at this size
let existential: any Protocol = Small(...) // Inline storage
let array = inline // Stack storage// ❌ Unexpected: InlineArray copies eagerly
func processLarge(_ data: InlineArray<1000, UInt8>) {
// Copies all 1000 bytes on call!
}
// ✅ Use Span to avoid copy
func processLarge(_ data: Span<UInt8>) {
// Zero-copy view, no matter the size
}
// Best practice: Store InlineArray, pass Span
struct Buffer {
var storage = InlineArray<1000, UInt8>(repeating: 0)
func process() {
helper(storage.span) // Pass view, not copy
}
}// ❌ Eager evaluation - processes entire array
let result = array
.map { expensive($0) }
.filter { $0 > 0 }
.first // Only need first element!
// ✅ Lazy evaluation - stops at first match
let result = array
.lazy
.map { expensive($0) }
.filter { $0 > 0 }
.first // Only evaluates until first matchactor Counter {
private var value = 0
// ❌ Actor call overhead for simple operation
func increment() {
value += 1
}
}
// Calling from different isolation domain
for _ in 0..<10000 {
await counter.increment() // 10,000 actor hops!
}
// ✅ Batch operations to reduce actor overhead
actor Counter {
private var value = 0
func incrementBatch(_ count: Int) {
value += count
}
}
await counter.incrementBatch(10000) // Single actor hop// async/await overhead: ~20-30μs per suspension point
// ❌ Unnecessary async for fast synchronous operation
func compute() async -> Int {
return 42 // Instant, but pays async overhead
}
// ✅ Keep synchronous operations synchronous
func compute() -> Int {
return 42 // No overhead
}// ❌ Creating task per item (~100μs overhead each)
for item in items {
Task {
await process(item)
}
}
// ✅ Single task for batch
Task {
for item in items {
await process(item)
}
}
// ✅ Or use TaskGroup for parallelism
await withTaskGroup(of: Void.self) { group in
for item in items {
group.addTask {
await process(item)
}
}
}@concurrent// Force background execution
@concurrent
func expensiveComputation() -> Int {
// Always runs on background thread, even if called from MainActor
return complexCalculation()
}
// Safe to call from main actor without blocking
@MainActor
func updateUI() async {
let result = await expensiveComputation() // Guaranteed off main thread
label.text = "\(result)"
}actor DataStore {
private var data: [Int] = []
// ❌ Isolated - actor overhead even for read-only
func getCount() -> Int {
data.count
}
// ✅ nonisolated for immutable state
nonisolated var storedCount: Int {
// Must be immutable
return data.count // Error: cannot access isolated property
}
}// ❌ Poor layout (24 bytes due to padding)
struct BadLayout {
var a: Bool // 1 byte + 7 padding
var b: Int64 // 8 bytes
var c: Bool // 1 byte + 7 padding
}
print(MemoryLayout<BadLayout>.size) // 24 bytes
// ✅ Optimized layout (16 bytes)
struct GoodLayout {
var b: Int64 // 8 bytes
var a: Bool // 1 byte
var c: Bool // 1 byte + 6 padding
}
print(MemoryLayout<GoodLayout>.size) // 16 bytes// Query alignment
print(MemoryLayout<Double>.alignment) // 8
print(MemoryLayout<Int32>.alignment) // 4
// Structs align to largest member
struct Mixed {
var int32: Int32 // 4 bytes, 4-byte aligned
var double: Double // 8 bytes, 8-byte aligned
}
print(MemoryLayout<Mixed>.alignment) // 8 (largest member)// ❌ Poor cache locality
struct PointerBased {
var next: UnsafeMutablePointer<Node>? // Pointer chasing
}
// ✅ Array-based for cache locality
struct ArrayBased {
var data: ContiguousArray<Int> // Contiguous memory
}
// Array iteration ~10x faster due to cache prefetching// Untyped - existential container for error
func fetchData() throws -> Data {
// Can throw any Error
throw NetworkError.timeout
}
// Typed - concrete error type
func fetchData() throws(NetworkError) -> Data {
// Can only throw NetworkError
throw NetworkError.timeout
}// Measure with tight loop
func untypedThrows() throws -> Int {
throw GenericError.failed
}
func typedThrows() throws(GenericError) -> Int {
throw GenericError.failed
}
// Benchmark: typed ~5-10% faster (no existential overhead)UnsafeBufferPointer// Traditional unsafe approach
func processUnsafe(_ data: UnsafeMutableBufferPointer<UInt8>) {
data[100] = 0 // Crashes if out of bounds!
}
// Safe Span approach
func processSafe(_ data: MutableSpan<UInt8>) {
data[100] = 0 // Traps with clear error if out of bounds
}| Use Case | Recommendation |
|---|---|
| Own the data | Array (full ownership, COW) |
| Temporary view for reading | Span (safe, fast) |
| Temporary view for writing | MutableSpan (safe, fast) |
| C interop, performance-critical | RawSpan (untyped bytes) |
| Unsafe performance | UnsafeBufferPointer (legacy, avoid) |
let array = [1, 2, 3, 4, 5]
// Get read-only span
let span = array.span
print(span[0]) // 1
print(span.count) // 5
// Iterate safely
for element in span {
print(element)
}
// Slicing (creates new span, no copy)
let slice = span[1..<3] // Span<Int> viewing [2, 3]var array = [10, 20, 30, 40, 50]
// Get mutable span
let mutableSpan = array.mutableSpan
// Modify through span
mutableSpan[0] = 100
mutableSpan[1] = 200
print(array) // [100, 200, 30, 40, 50]
// Safe bounds checking
// mutableSpan[10] = 0 // Fatal error: Index out of rangestruct PacketHeader {
var version: UInt8
var flags: UInt8
var length: UInt16
}
func parsePacket(_ data: RawSpan) -> PacketHeader? {
guard data.count >= MemoryLayout<PacketHeader>.size else {
return nil
}
// Safe byte-level access
let version = data[0]
let flags = data[1]
let lengthLow = data[2]
let lengthHigh = data[3]
return PacketHeader(
version: version,
flags: flags,
length: UInt16(lengthHigh) << 8 | UInt16(lengthLow)
)
}
// Usage
let bytes: [UInt8] = [1, 0x80, 0x00, 0x10] // Version 1, flags 0x80, length 16
let rawSpan = bytes.rawSpan
if let header = parsePacket(rawSpan) {
print("Packet version: \(header.version)")
}// Array provides .span and .mutableSpan
let array = [1, 2, 3]
let span: Span<Int> = array.span
// ContiguousArray provides spans
let contiguous = ContiguousArray([1, 2, 3])
let span2 = contiguous.span
// UnsafeBufferPointer provides .span (migration path)
let buffer: UnsafeBufferPointer<Int> = ...
let span3 = buffer.span // Modern safe wrapper// ❌ Array copy - heap allocation
func process(_ array: [Int]) {
// Array copied if passed across module boundary
}
// ❌ UnsafeBufferPointer - no bounds checking
func process(_ buffer: UnsafeBufferPointer<Int>) {
buffer[100] // Crash or memory corruption!
}
// ✅ Span - no copy, bounds-checked, temporal safety
func process(_ span: Span<Int>) {
span[100] // Safe trap if out of bounds
}
// Performance: Span is as fast as UnsafeBufferPointer (~2ns access)
// but with safety guarantees (bounds checks are optimized away when safe)// ✅ Safe - span lifetime bound to array
func useSpan() {
let array = [1, 2, 3, 4, 5]
let span = array.span
process(span) // Safe - array still alive
}
// ❌ Compiler prevents this
func dangerousSpan() -> Span<Int> {
let array = [1, 2, 3]
return array.span // Error: Cannot return non-escapable value
}
// This is what temporal safety prevents
// (Compare to UnsafeBufferPointer which ALLOWS this bug!)// InlineArray provides span access
let inline = InlineArray<10, UInt8>()
let span: Span<UInt8> = inline.span
let mutableSpan: MutableSpan<UInt8> = inline.mutableSpan
// Efficient zero-copy parsing
func parseHeader(_ span: Span<UInt8>) -> Header {
// Direct access to inline storage via span
Header(
magic: span[0],
version: span[1],
flags: span[2]
)
}
let header = parseHeader(inline.span) // No heap allocation!// Old pattern (unsafe)
func processLegacy(_ buffer: UnsafeBufferPointer<Int>) {
for i in 0..<buffer.count {
print(buffer[i])
}
}
// New pattern (safe)
func processModern(_ span: Span<Int>) {
for element in span { // Safe iteration
print(element)
}
}
// Migration bridge
let buffer: UnsafeBufferPointer<Int> = ...
let span = buffer.span // Wrap unsafe pointer in safe span
processModern(span)// Pattern 1: Binary parsing with RawSpan
func parse<T>(_ span: RawSpan) -> T? {
guard span.count >= MemoryLayout<T>.size else {
return nil
}
return span.load(as: T.self) // Safe type reinterpretation
}
// Pattern 2: Chunked processing
func processChunks(_ data: Span<UInt8>, chunkSize: Int) {
var offset = 0
while offset < data.count {
let end = min(offset + chunkSize, data.count)
let chunk = data[offset..<end] // Span slice, no copy
processChunk(chunk)
offset += chunkSize
}
}
// Pattern 3: Safe C interop
func sendToC(_ span: Span<UInt8>) {
span.withUnsafeBufferPointer { buffer in
// Only escape to unsafe inside controlled scope
c_function(buffer.baseAddress, buffer.count)
}
}// ❌ Don't use Span for ownership
struct Document {
var data: Span<UInt8> // Error: Span can't be stored
}
// ✅ Use Array for owned data
struct Document {
var data: [UInt8]
// Provide span access when needed
var dataSpan: Span<UInt8> {
data.span
}
}
// ❌ Don't try to escape Span from scope
func getSpan() -> Span<Int> { // Error: Non-escapable
let array = [1, 2, 3]
return array.span
}
// ✅ Process in scope, return owned data
func processAndReturn() -> [Int] {
let array = [1, 2, 3]
process(array.span) // Process with span
return array // Return owned data
}final class Storage<T> {
var value: T
init(_ value: T) { self.value = value }
}
struct COWWrapper<T> {
private var storage: Storage<T>
init(_ value: T) {
storage = Storage(value)
}
var value: T {
get { storage.value }
set {
if !isKnownUniquelyReferenced(&storage) {
storage = Storage(newValue)
} else {
storage.value = newValue
}
}
}
}func processLargeArray(_ input: [Int]) -> [Int] {
var result = ContiguousArray<Int>()
result.reserveCapacity(input.count)
for element in input {
result.append(transform(element))
}
return Array(result)
}private var cache: [Key: Value] = [:]
@inlinable
func getCached(_ key: Key) -> Value? {
return cache[key] // Inlined across modules
}// Don't optimize without measuring first!
// ❌ Complex optimization with no measurement
struct OverEngineered {
@usableFromInline var data: ContiguousArray<UInt8>
// 100 lines of COW logic...
}
// ✅ Start simple, measure, then optimize
struct Simple {
var data: [UInt8]
}
// Profile → Optimize if neededclass Manager {
// ❌ Unnecessary weak reference overhead
weak var delegate: Delegate?
weak var dataSource: DataSource?
weak var observer: Observer?
}
// ✅ Use unowned when lifetime is guaranteed
class Manager {
unowned let delegate: Delegate // Delegate outlives Manager
weak var dataSource: DataSource? // Optional, may be nil
}// ❌ Actor overhead for simple synchronous data
actor SimpleCounter {
private var count = 0
func increment() {
count += 1
}
}
// ✅ Use lock-free atomics or @unchecked Sendable
import Atomics
struct AtomicCounter: @unchecked Sendable {
private let count = ManagedAtomic<Int>(0)
func increment() {
count.wrappingIncrement(ordering: .relaxed)
}
}isKnownUniquelyReferencedreserveCapacitysomeany@_specializeContiguousArrayArrayhash(into:)async@concurrent"I want to optimize effectively. Let me spend 30 minutes with Instruments
to find the actual bottleneck. This prevents wasting time on code that's
not the problem. I've seen this save days of work.""Actors are great for isolation, but they add overhead. For this simple
counter, lock-free atomics are 10x faster. Let's use actors where we need
them—shared mutable state—and avoid them for pure value types."@inlinable@inlinable"Inlining trades code size for speed. The compiler already inlines when
beneficial. Manual @inlinable should be for small, frequently called
functions. Let's profile and inline the 3 actual hotspots, not everything."// Original code
func processImages(_ images: [UIImage]) -> [ProcessedImage] {
var results: [ProcessedImage] = []
for image in images {
results.append(expensiveProcess(image)) // Reallocations!
}
return results
}func processImages(_ images: [UIImage]) -> [ProcessedImage] {
var results = ContiguousArray<ProcessedImage>()
results.reserveCapacity(images.count) // Single allocation
for image in images {
results.append(expensiveProcess(image))
}
return Array(results)
}// Original - 10,000 actor hops
for _ in 0..<10000 {
await counter.increment() // ~100μs each = 1 second total!
}// Batch operations
actor Counter {
private var value = 0
func incrementBatch(_ count: Int) {
value += count
}
}
await counter.incrementBatch(10000) // Single actor hop// Original - existential overhead
func render(shapes: [any Shape]) {
for shape in shapes {
shape.draw() // Dynamic dispatch
}
}// Specialized generic
func render<S: Shape>(shapes: [S]) {
for shape in shapes {
shape.draw() // Static dispatch after specialization
}
}
// Or use @_specialize
@_specialize(where S == Circle)
@_specialize(where S == Rectangle)
func render<S: Shape>(shapes: [S]) { }// Key performance wins from Swift's features:
// 1. Deterministic memory management (no GC pauses)
// - No stop-the-world garbage collection
// - Predictable latency for real-time processing
// 2. Value semantics + COW
// - Efficient data sharing without defensive copying
// - Reduced memory churn
// 3. Zero-cost abstractions
// - Generic specialization eliminates runtime overhead
// - Protocol conformances optimized away@concurrent