0

I'm processing ~10k events between two different classes. One is fetching them, and the other is storing them in a dictionary. Now since the fetching class is also doing more stuff with the data than just passing it to the second class, it really doesn't make a lot of sense to send them over as a big bulk, but rather I'm processing them like

actor Fetcher {
  let someProcessor = Processor()

  func getData() async {
    let results = await Rest.getData()
    for result in results {
      await someProcessor.doStuff(with: result)
      await someOtherObject.handle(result)
    }
  }
}

actor Processor {
  func doStuff(with result: Result) async {
    // ...
  }
}

now maybe you can see the problem. With both of them being actors, I keep sending around data between threads. Processing ~10k results thus takes 8 seconds. This is mostly because of thread switches. If I make my code non-thread-safe by removing the actor keyword it takes less than a second. It would remove some functionality of my code if I did that though. Is there a way to tell swift that these two actors should always run in the same Thread to avoid the switching?

yspreen
  • 1,759
  • 2
  • 20
  • 44
  • 4
    Actors are not directly related to threads, actors run on actors and different actors run on different ones. A TaskGroup would likely help you maximize time. – lorem ipsum Jan 25 '23 at 01:40
  • See AsyncChannel (https://github.com/apple/swift-async-algorithms/blob/main/Sources/AsyncAlgorithms/AsyncAlgorithms.docc/Guides/Channel.md). You can let Processor and `someOtherObject` communicate without needing to switch to Fetcher's context (but still without them knowing each other). `doStuff` will asynchronously return a stream of values that `handle` will consume. – Rob Napier Jan 25 '23 at 03:20
  • There’s not enough here for us to advise you, but doing 10k synchronizations will likely be much slower than one big synchronization. And it seems like there is an opportunity to enjoy some parallelism with task groups, but it’s hard to advise you without more details and or a [reproducible example](https://stackoverflow.com/help/minimal-reproducible-example). But right now you have tons of suspension points and no parallelism. – Rob Jan 25 '23 at 05:24
  • By the way, if you are interested in better understanding the Swift concurrency threading model, see WWDC 2021 video [Swift concurrency: Behind the scenes](https://developer.apple.com/videos/play/wwdc2021/10254/). As you'll see there, Swift concurrency system is quite clever about minimizing costly thread switches. The problem is unlikely to be related to thread switches, but rather all of these suspension points. – Rob Jan 25 '23 at 19:54
  • thanks for the feedback. how would you reduce suspension points in the above code @Rob? – yspreen Jan 26 '23 at 04:26
  • E.g., I’m wondering if you can do something like https://stackoverflow.com/a/71104015/1271826 or https://stackoverflow.com/a/74416553/1271826, doing `doStuff` concurrently in a task group and build the dictionary (and if you want to update the dictionary in the actor at the end, fine, do that). Also, make sure to test this (a) on a release build; and (b) not on a simulator (because the cooperative thread pool is artificially constrained on simulator). If you can edit your question with a reproducible example that actually manifests the behavior you describe, we could offer better counsel. – Rob Jan 26 '23 at 06:55

0 Answers0