All,
I’m happy to announce a (breaking) API upgrade to ThreadPools.jl to better separate choices around handling background tasks and handling nonuniform tasks. Many thanks to @tkf and @ianshmean and many others who contributed to the ideas.
Overview
ThreadPools.jl is a simple package for thread parallelization that exposes a few macros and functions that mimic Base.Threads.@threads
, Base.map
, and Base.foreach
. These macros/functions handle cases that the built-in functions are not always well-suited for:
- Groups of tasks that the user wants to keep off of the primary thread
- Groups of tasks that are very nonuniform in duration
For the first case, ThreadPools exposes a @bthreads
(“background threads”) macro that behaves identically to Threads.@threads
, but keeps the primary thread job-free. For the second case, the package exposes a @qthreads
(“queued threads”) macro. This macro queues up the jobs before thread assignment, only starting a new job when a job on any previous thread has finished. The background version of the same is the @qbthreads
(“queued background threads”) macro. Versions of map
and foreach
with the same prefixes are also available:
Foreground (primary allowed) | Background (primary forbidden) | |
---|---|---|
Uniform tasks |
|
|
Nonuniform tasks |
|
|
The package also exposes a lower-level @pspawnat
macro that mimics the Base.Threads.@spawn
macro, but allows direct thread assignment for users who want to develop their own scheduling.
Logging
There are also logging versions of each of the above:
julia> using Plots
pool = logpforeach(x -> sleep(0.01*x), 1:64);
plot(pool)
pool = logqforeach(x -> sleep(0.01*x), 1:64);
plot(pool)
Note that the above examples demonstrate the two scheduling strategies far better than I can describe.
Composing
There may be times where the default macros don’t quite do what you want. For those cases, a composable API is exposed. For example, perhaps we want to limit usage to only two threads off of the primary:
julia> pwith(ThreadPools.QueuePool(2,2)) do pool
pforeach(pool, x -> println((x,Threads.threadid())), 1:8)
end;
(2, 2)
(1, 3)
(3, 2)
(4, 3)
(5, 2)
(6, 3)
(7, 2)
(8, 3)
Here, only threads 2 and 3 are used (in this case, on an 8-thread machine).
Anyway, thanks for all the early-adopter inputs, and I hope that some find the package useful. Feel free to submit issues at ThreadPools.jl if you find problems or have requests. Cheers, all!