title: Maximum number of threads with async-await task groups
body: <p>My intent is to understand the “cooperative thread pool” used by Swift 5.5’s async-await, and how task groups automatically constrain the degree of concurrency: Consider the following task group code, doing 32 calculations in parallel:</p>
  <pre class="lang-swift prettyprint-override"><code>func launchTasks() async {
      await withTaskGroup(of: Void.self) { group in
          for i in 0 ..&lt; 32 {
              group.addTask { [self] in
                  let value = await doSomething(with: i)
                  // do something with `value`
              }
          }
      }
  }
  </code></pre>
  <p>While I hoped it would constrain the degree of concurrency, as advertised, I'm only getting two (!) concurrent tasks at a time. That is far more constrained than I would have expected:</p>
  <p><a href="https://i.stack.imgur.com/EcLkn.png" rel="noreferrer"><img src="https://i.stack.imgur.com/EcLkn.png" alt="enter image description here" /></a></p>
  <p>If I use the old GCD <code>concurrentPerform</code> ...</p>
  <pre class="lang-swift prettyprint-override"><code>func launchTasks2() {
      DispatchQueue.global().async {
          DispatchQueue.concurrentPerform(iterations: 32) { [self] i in
              let value = doSomething(with: i)
              // do something with `value`
          }
      }
  }
  </code></pre>
  <p>... I get twelve at a time, taking full advantage of the device (iOS 15 simulator on my 6-core i9 MacBook Pro) while avoiding thread-explosion:</p>
  <p><a href="https://i.stack.imgur.com/V1T6R.png" rel="noreferrer"><img src="https://i.stack.imgur.com/V1T6R.png" alt="enter image description here" /></a></p>
  <p>(FWIW, both of these were profiled in Xcode 13.0 beta 1 (13A5154h) running on Big Sur. And please disregard the minor differences in the individual “jobs” in these two runs, as the function in question is just spinning for a random duration; the key observation is the degree of concurrency is what we would have expected.)</p>
  <p>It is excellent that this new async-await (and task groups) automatically limits the degree of parallelism, but the cooperative thread pool of async-await is far more constrained than I would have expected. And I see of no way to adjust these parameters of that pool. How can we better take advantage of our hardware while still avoiding thread explosion (without resorting to old techniques like non-zero semaphores or operation queues)?</p>