Description
It occurred to me that it might be nice to have @threads f(...)
rewrite the function call to something like f(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 a sum(::ThreadsCall{true})
method rather than having a sum_threads
function. (Of course, in an ideal world with no threading overhead, we could have sum
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:
f(args...) = f(ThreadsCall{false}(), args...) # default is non-threaded
function f(::ThreadsCall{TC}, args...) where {TC}
...some calculations....
if TC
... threaded algorithm ...
else
... serial algorithm ...
end
@maybethreads TC for ... # flag version of @threads for to enable/disable threading
end
... some other calculations ...
return g(ThreadsCall{TC}(), ...) # call subsidiary function and pass TC
end
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 into
if bool
@threads for ...
else
for ...
end
so 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 below
For #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.