-
-
Notifications
You must be signed in to change notification settings - Fork 5.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
@threads f(...)
syntax to tell callee to use threaded version?
#40329
Comments
I have found myself wanting something exactly like this and this. We've also been discussing and working on a lot of multithreading integration in DataFrames.jl for the 1.0 release and there have been several times where it's unclear exactly the API to provide users to control threading. cc: @nalimilan @bkamins |
@quinnj, your first example could be simplified if @maybespawn TC for .... end to use the boolean type parameter |
Alternatively, if the boolean-flag version of @maybethreads bool for ... end # uses @threads for if bool == true
@maybethreads bool f(args...) # calls @threads f(args...) if bool == true
@maybespawn bool something # uses @spawn something if bool == true
@maybesync bool something # uses @sync something if bool == true which you could use with the type parameter |
Let me give one example from DataFrames.jl that would be great to simplify:
(of course it is more complex than it needs to be also because we also support Julia 1.0 - and once new LTS is out the code will be cleaned up, but the point is that it would be great to have something that would allow to enable/disable |
I think all of the proposals here can live in a package, e.g. https://github.com/tkf/ThreadsX.jl. For higher order functions in |
Second, I specifically don't want to replace (In my mind, there's not much benefit to writing
It's certainly true that the proposed functionality could be put into a ThreadCalls.jl package to start with (e.g. as But it seems like a uniform syntax to opt-in to threaded versions of functions, and to share code between threaded and serial versions, was fundamental enough to consider for Base. (Also, I wanted to bounce the general design questions off of the core-dev audience.) |
cc @tkf |
I think the underlying infrastructure for this already exists in JuliaFolds, especially as the executor. The surface API closest to the one mentioned in OP is provided from Folds.jl. Furthermore, all executors can also be used with FLoops.jl for the familiar There's a quick introduction in [ANN] Folds.jl: threaded, distributed, and GPU-based high-level data-parallel interface for Julia - Community / Package announcements - JuliaLang. I also discussed how we can build an ecosystem for parallel computing by extending the infrastructure in JuliaFolds. I'd also note that specifying "threaded" using Folds
xs = (f(x) for x in 1:10 if p(x)) # arbitrary splittable data collection and iterator comprehension
Folds.sum(xs) # default to ThreadedEx
Folds.sum(xs, ThreadedEx())
Folds.sum(xs, SequentialEx()) # single-threaded
Folds.sum(xs, DistributedEx()) # Distributed.jl
Folds.sum(xs, CUDAEx()) # using FoldsCUDA.jl (CUDA)
Folds.sum(xs, KAEx()) # using FoldsKernelAbstractions.jl (GPU)
Folds.sum(xs, DaggerExEx()) # using FoldsDagger.jl (distributed) Additionally, there are various thread-based executors in FoldsThreads.jl. See [ANN] FoldsThreads.jl: A zoo of pluggable thread-based data-parallel execution mechanisms - Community / Package announcements - JuliaLang for what it has and the benchmarks demonstrating their performance characteristics. That is to say, even just "threaded" version of a given function can have a gazillion of variants. There are a lot of things to consider if we want to build a composable and extensible infrastructure for parallel computing. We need to carefully decompose the existing notion of the algorithm, data structure, and execution mechanism. I hope we don't add any premature APIs in Base. |
It occurred to me that it might be nice to have
@threads f(...)
rewrite the function call to something likef(Threads.ThreadsCall{true}(), ...)
, if only to provide a uniform syntax for requesting a multi-threaded version of an algorithm.For example, we could write
@threads sum(x)
to call asum(::ThreadsCall{true})
method rather than having asum_threads
function. (Of course, in an ideal world with no threading overhead, we could havesum
always use a@threads for
, but for the foreseeable future this will pay a big price for summing short arrays.)The reason I suggest
ThreadsCall{true}
is that this allows you to define functions that share code between the threaded and serial versions:in order to share code between the threaded and non-threaded version, and to pass the choice of whether or not to thread through to subsidiary functions.
As suggested above, it would be nice to also make
@maybethreads bool for ...
turn intoso that you could write a single loop and use
TC
from::ThreadsCall{TC}
to decide whether to thread it. Similarly for@maybespawn
,@maybesync
as suggested belowFor #19777, we could write a
broadcast(::ThreadsCall{true}, ...)
method and then make@threads f.(...)
call it.cc: @jw3126, @baggepinnen, @mohamed82008, who have all written packages with threaded variants of stdlib functions. Eventually it would be nice to fold some of this into base with
@threads map(...)
etcetera.The text was updated successfully, but these errors were encountered: