r/swift • • 3d ago

Help! How do I limit the number of threads spawned by the cooperative runtime on Linux?

I want to limit the number of threads spawned by Swift's cooperative runtime to a number lower than the number of available CPUs. The reason is because I want to run Swift code in a Linux container that limits scheduling to only 8 CPUs regardless of how many the system has. I've tried setting LIBDISPATCH_COOPERATIVE_POOL_STRICT=n where n is the maximum number of threads, but apparently Swift doesn't use libdispatch on Linux so that does nothing. Can anyone tell whether there's a different environment variable that I can set, or at least a compile-time option or function that I must call in the code in order to set the number of threads spawned by the cooperative rumtime?

5 Upvotes

4 comments sorted by

9

u/fowllabs 3d ago

swift's default linux executor is libdispatch-backed. if your eight-cpu limit is only a quota, try a cpuset/affinity restriction before launch; libdispatch uses that mask for its non-overcommit worker pools, though this isn't a hard limit on total process threads: https://github.com/swiftlang/swift-corelibs-libdispatch/blob/main/src/shims/hw_config.h

1

u/Fridux 2d ago

You are right, I just checked ldd and my binary is indeed linked to libdispatch.so; not sure why just setting the environment variable didn't work. As for the solutions, unfortunately being in a container means that I do not have the cpuset pseudo filesystem mounted anywhere nor am I allowed to mount it myself, and I don't think that the choice of CPUs for task affinity is or should be mine, since I'm running on a cloud environment. That said I did test the task affinity solution and it worked.

1

u/Equivalent-Idea-116 9h ago

If the affinity route works but you can't rely on it in the container, I'd stop fighting the default pool and give the heavy work its own executor. Swift 6 has task executors (SE-0417), so you can write a small executor backed by a fixed number of threads, say 8, and run your parallel work with a task executor preference. That way the number of threads doing real work is whatever you pick, no matter how many CPUs the host reports. It also helps to cap concurrency yourself, for example a task group where you only keep 8 child tasks in flight at once, since that limits the load even if the runtime has more idle threads around.

1

u/Fridux 8h ago

Yeah, that's a good idea; I'm just not sure where detached tasks will be scheduled.