Hello guys, could you tell me guys. Do swift actors support parallel execution overall?
After some source code research and few articles read - I make assumption that all custom actor direct they calls to global executor, which manages global thread pool. Exception is MainActor which is whole standalone thing.
I know that blocking operations inside task is not allowed, it may broke dispatching of executor, but I don't know any other ideas how to test it.
So my example is:
actor SomeActor {
let name: String
init(_ name: String) { self.name = name }
func run() {
print("Start run: (name)")
sleep(5)
print("End run: (name)")
}
}
let act1 = SomeActor("A")
let act2 = SomeActor("B")
Task.detached {
await act1.run()
}
Task.detached {
await act2.run()
}
Output is:
Start run: A
End run: A
Start run: B
End run: B
Why do actors run serially, taking into account that they are invoked from detached task?
Do actors support parallelism, or they are concurrent only?
Will appreciate any ideas and assumptions)
If I change your code as follows to make it compile and to await the two tasks before exiting, and then run it on the command line (swift actors.swift), the two tasks do run concurrently as you would expect.
Tried your changes still serially, my config are:
macOS 12.4
Apple Swift version 5.6 (swiftlang-5.6.0.323.62 clang-1316.0.20.8)
Target: x86_64-apple-macosx12.0
Xcode Version 13.3 (13E113)
I don't get this. My code doesn't even compile for me in Xcode 13.3 (or 13.4.1), presumably because SE-0343 Concurrency in Top-level Code was only implemented in Swift 5.7.
$ sudo xcode-select --switch /Applications/Xcode-13.3.app/Contents/Developer/
$ swift --version
swift-driver version: 1.45.2 Apple Swift version 5.6 (swiftlang-5.6.0.323.62 clang-1316.0.20.8)
Target: arm64-apple-macosx12.0
$ swift actors.swift
actors.swift:23:7: error: 'async' property access in a function that does not support concurrency
await t1.value
^
actors.swift:24:7: error: 'async' property access in a function that does not support concurrency
await t2.value
^
I think it doesn't make any sense. Probably there are changes between versions of swift that bring something new.
Even these proposal could change it's implementation:
But if you do just that, the tasks won't even run to completion because the program will exit before the tasks finish, won't they?
With just this single change, the output is this for me (edit: macOS 12.4, Xcode 13.3 (Swift 5.6)):
$ swift actors.swift
Start run: B
Start run: A
# program exits here
$ (new prompt)
You'd need to add something like RunLoop.current.run() to the main program (at the top level) to keep the program running. And then it does the right thing again (the two actors run concurrently).
The simulator needs to run on older OS-es which don't have the new cooperative thread pool with kernel support. As such, we have to make locally optimal decisions in libdispatch on how many threads to bring up in libdispatch
If you had kicked off tasks with different QoS-es, you'd see that we will bring up at least a thread per QoS class but we don't have intelligent load balancing
So the simulator only uses one thread per task priority. Since your sleep call blocks the thread, nothing else can run on the cooperative thread pool in the meantime.
Thanks man, btw I recently noticed that on simulator Task.detached runs on: com.apple.root.default-qos.cooperative (serial)
And on device it's running on:
com.apple.root.default-qos.cooperative (concurrent)