Actors 101

More Interesting Topics

Measuring Actor performance

Post

Summary
Try This
import Foundation

@main
enum Test {
    static func main () async  {
        testStateHolderLocked()
        await testStateHolderActor()
        await testStateHolderActor2()
    }
}

// [https://forums.swift.org/t/i-was-playing-with-measuring-actor-performance/75005]

final class StateHolderLock {
    var lock = os_unfair_lock_s()

    init() {}

    var sum = 0

    var onNewValueReceived: ((Int) -> Void)!

    func handleValueRecieved(_ val: Int) {
        os_unfair_lock_lock(&lock)
        sum += val
        os_unfair_lock_unlock(&lock)
        onNewValueReceived(val)
    }
}

final actor StateHolderActor {
    init() {}

    var sum = 0

    nonisolated(unsafe) var onNewValueReceived: ((Int) -> Void)!

    func handleValueRecieved(_ val: Int) {
        sum += val
        onNewValueReceived(val)
    }
}

let iterations = 1000000
func testStateHolderActor() async {
    await measure ("Actor:") {
        let actor = StateHolderActor()
        
        var sum = 0
        actor.onNewValueReceived = { val in
            sum += val
        }
        
        for _ in 0 ..< iterations {
            await actor.handleValueRecieved(1)
        }
        
    }
}

func testStateHolderLocked() {
    measure ("Locked:") {
        let actor = StateHolderLock()
        
        var sum = 0
        actor.onNewValueReceived = { val in
            sum += val
        }
        
        for _ in 0 ..< iterations {
            actor.handleValueRecieved(1)
        }
        
    }
}

// [https://forums.swift.org/t/i-was-playing-with-measuring-actor-performance/75005/2]

func testStateHolderActor2 () async {
    await measure ("Actor2 :") {
        let actor = StateHolderActor()
        
        var sum = 0
        actor.onNewValueReceived = { val in
            sum += val
        }
        
        func run (actor: isolated StateHolderActor) async {
            for _ in 0 ..< iterations {
                actor.handleValueRecieved(1)
            }
        }
        
        await run (actor: actor)
    }
}

func measure (_ prefix: String, _ f: () -> Void) {
    let d = ContinuousClock ().measure {
        f ()
    }
    print (prefix, d)
}

func measure (_ prefix: String, _ f: () async -> Void) async {
    let d = await ContinuousClock ().measure {
        await f ()
    }
    print (prefix, d)
}

Guaranteeing an actor executes off the main thread

Post

Summary

How to share isolation between two actors

Post

How to “fire and forget” work on an actor?

Post

Try This
@main
enum Actors {
    static func main () async throws {
        let u = FooActor ()
        await print (u.x)
        u.incrementEventually()
        u.incrementEventually()
        u.incrementEventually()
        for _ in 0..<5 {
            await print (u.x)
            await hibernate (seconds: 3)
        }
    }
}

func hibernate (seconds v: Int) async {
    try! await Task.sleep(until: .now + .seconds(v))
}

// [https://forums.swift.org/t/how-to-fire-and-forget-work-on-an-actor/75157]


actor FooActor {
    var x = 0

    func incrementX () {
        self.x += 1
    }
    
    nonisolated func incrementEventually() {
        Task {
            await hibernate (seconds: 3)
            await self.incrementX()
        }
    }
}

1 Like