NonBlockingThreadPool
public class NonBlockingThreadPool<Environment> : ComputeThreadPool where Environment : ConcurrencyPlatform
An efficient, work-stealing, general purpose compute thread pool.
NonBlockingThreadPool
can be cleaned up by calling shutDown()
which will block until all
threads in the threadpool have exited. If shutDown()
is never called, NonBlockingThreadPool
will never be deallocated.
NonBlockingThreadPool uses atomics to implement a non-blocking thread pool. During normal
execution, no locks are acquired or released. This can result in efficient parallelism across
many cores. NonBlockingThreadPool
is designed to scale from laptops to high-core-count
servers. Although the thread pool size can be manually tuned, often the most efficient
configuration is a one-to-one mapping between hardware threads and worker threads, as this
allows full use of the hardware while avoiding unnecessary context switches. I/O heavy workloads
may want to reduce the thread pool count to dedicate a core or two to I/O processing.
Each thread managed by this thread pool maintains its own fixed-size pending task queue. The workers loop, trying to get their next tasks from their own queue first, and if that queue is empty, the worker tries to steal work from the pending task queues of other threads in the pool.
NonBlockingThreadPool
implements important optimizations based on the calling thread. There
are key fast-paths taken when calling functions on NonBlockingThreadPool
from threads that
have been registered with the pool (or from threads managed by the pool itself). In order
to help users build performant applications, NonBlockingThreadPool
will trap (and exit the
process) if functions are called on it from non-fast-path’d threads by default. You can change
this behavior by setting allowNonFastPathThreads: true
at initialization.
In order to avoid wasting excessive CPU cycles, the worker threads managed by
NonBlockingThreadPool
will suspend themselves (using locks to inform the host kernel).
NonBlockingThreadPool
is parameterized by an environment, which allows this thread pool to
seamlessly interoperate within a larger application by reusing its concurrency primitives (such
as locks and condition variables, which are used for thread parking), as well as even allowing
a custom thread allocator.
Local tasks typically execute in LIFO order, which is often optimal for cache locality of compute intensive tasks. Other threads attempt to steal work “FIFO”-style, which admits an efficient (dynamic) schedule for typical divide-and-conquor algorithms.
This implementation is inspired by the Eigen thread pool library, TFRT, as well as:
"Thread Scheduling for Multiprogrammed Multiprocessors"
Nimar S. Arora, Robert D. Blumofe, C. Greg Plaxton
-
Undocumented
Declaration
Swift
public typealias Task = () -> Void
-
Undocumented
Declaration
Swift
public typealias ThrowingTask = () throws -> Void
-
Initialize a new thread pool with
threadCount
threads using threading environmentenvironment
.Declaration
Swift
public init( name: String, threadCount: Int, environment: Environment, externalFastPathThreadCount: Int = 1, allowNonFastPathThreads: Bool = false )
Parameters
name
a human-readable name for the threadpool.
threadCount
the number of worker threads in the thread pool.
environment
an instance of the environment.
externalFastPathThreadCount
the maximum number of external threads with fast-path access to the threadpool.
allowNonFastPathThreads
true if non-fast-path’d threads are allowed to submit work into the pool or not. (Note: non-fast-path’d threads can always dispatch work into the pool.)
-
Registers the current thread with the thread pool for fast-path operation.
Declaration
Swift
public func registerCurrentThread()
-
Declaration
Swift
public func dispatch(_ fn: @escaping Task)
-
-
Declaration
Swift
public func join(_ a: ThrowingTask, _ b: ThrowingTask) throws
-
Executes
fn
, optionally in parallel, spanning the range0..<n
.Declaration
Swift
public func parallelFor(n: Int, _ fn: VectorizedParallelForBody)
-
Executes
fn
, optionally in parallel, spanning the range0..<n
.Declaration
Swift
public func parallelFor(n: Int, _ fn: ThrowingVectorizedParallelForBody) throws
-
Requests that all threads in the threadpool exit and cleans up their associated resources.
This function returns only once all threads have exited and their resources have been deallocated.
Note: if a work item was submitted to the threadpool that never completes (i.e. has an infinite loop), this function will never return.
Declaration
Swift
public func shutDown()
-
Declaration
Swift
public var maxParallelism: Int { get }
-
Declaration
Swift
public var currentThreadIndex: Int? { get }
-
Creates
self
using a default-initializedEnvironment
, and the specifiedname
andthreadCount
.Declaration
Swift
public convenience init(name: String, threadCount: Int)