-
Notifications
You must be signed in to change notification settings - Fork 9
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
macro API #53
macro API #53
Conversation
@MasonProtter, would be great if you could take a look. I'm not that well versed in writing macros :) |
Bikeshedding:
|
One thought I had was trying to copy Chapel's @forall i in 1:10 begin
println(i)
end but it kinda sucks that we have to write the It kinda makes me think though we could just do this without a macro at all though: forall(1:10) do i
println(i)
end and forall(1:10; reducer=(+)) do i
println(i)
end but there's various things like the task local storage that might benefit from having a macro available. |
That's already available, just foreach(1:10) do i
println(i)
end
This. Also, whether we like it or not, people are (much) more used to writing for i in 1:10
# do something
end than foreach(1:10) do i
# do something
end I always have to explain the (Of course, we don't always have to give people what they want 😀) |
94d569c
to
70d3bcd
Compare
Added support for multiple task local variables @threaded scheduler=StaticScheduler() for j in 1:16
@tasklocal begin
C::Matrix{Int64} = fill(Threads.threadid(), 2,2)
D::Matrix{Float64} = rand(3,3)
end
sleep(0.01*j)
println(Threads.threadid(), " (", pointer_from_objref(C),") (", pointer_from_objref(D),")")
end To avoid "leaking" of the task local storages, we now wrap everything in a let var"##290" = OhMyThreads.TaskLocalValue{Matrix{Int64}}((()->begin
fill(Threads.threadid(), 2, 2)
end)), var"##291" = OhMyThreads.TaskLocalValue{Matrix{Float64}}((()->begin
rand(3, 3)
end))
begin
OhMyThreads.tforeach(1:16; scheduler = StaticScheduler()) do j
begin
C = var"##290"[]
D = var"##291"[]
end
begin
sleep(0.01j)
println(Threads.threadid(), " (", pointer_from_objref(C), ") (", pointer_from_objref(D), ")")
end
end
end
end |
Note to future self: It needs to be clearly documented that assignments within a |
Another (wild) thought I just had: I kind of dislike that all the keyword arguments (e.g. for i in 1:16 and, e.g., @threaded scheduler=DynamicScheduler(; nchunks=8, split=:scatter) reducer=(+) for j in 1:16 I was wondering whether we should instead use a separate block for all settings, e.g., @threaded for j in 1:16
@settings begin
scheduler=DynamicScheduler(; nchunks=8, split=:scatter)
reducer=(+)
end
println(j)
end or, equivalently, @threaded for j in 1:16
println(j)
@settings begin
scheduler=DynamicScheduler(; nchunks=8, split=:scatter)
reducer=(+)
end
end |
I like this idea a lot! |
Some bike shedding updates. I think I dislike
I also don't think that
For now, I'll go with / change to |
These all work now: @tasks for i in 1:10
@set scheduler=StaticScheduler()
println(i)
end
@tasks for i in 1:10
@set begin
scheduler=DynamicScheduler(; nchunks=4)
reducer= (a,b) -> a+b
end
sin(i)
end
@tasks for i in 1:10
@set scheduler=DynamicScheduler(; nchunks=4)
@set reducer=(+)
sin(i)
end
@tasks for i in 1:10
@init begin
C::Matrix{Int64} = fill(Threads.threadid(), 2,2)
D::Matrix{Float64} = rand(3,3)
end
println("asd")
end
@tasks scheduler=DynamicScheduler(; nchunks=4) for i in 1:10
@set scheduler=StaticScheduler() # takes precedence
println(i)
end |
One thought I had was when we construct the inner function to be passed to I think the way this would have to work though would be to have something like struct WithTaskLocalValue{F, TLVs <: Tuple{Vararg{TaskLocalValue}}}
func::F
tlvs::TLVs
end
initialize(f::WithTaskLocalValue) = f.func(map(x -> x[], f.tlvs)...)
initialize(f::Any) = f and then # DynamicScheduler: AbstractArray/Generic
function _tmapreduce(f,
op,
Arrs,
::Type{OutputType},
scheduler::DynamicScheduler,
mapreduce_kwargs)::OutputType where {OutputType}
(; nchunks, split, threadpool) = scheduler
check_all_have_same_indices(Arrs)
if chunking_enabled(scheduler)
tasks = map(chunks(first(Arrs); n = nchunks, split)) do inds
args = map(A -> view(A, inds), Arrs)
@spawn threadpool mapreduce(initialize(f), op, args...; $mapreduce_kwargs...) # <------- Change here
end
mapreduce(fetch, op, tasks)
else
tasks = map(eachindex(first(Arrs))) do i
args = map(A -> @inbounds(A[i]), Arrs)
@spawn threadpool initialize(f)(args...) # <------- Change here
end
mapreduce(fetch, op, tasks; mapreduce_kwargs...)
end
end This way, someone can do e.g. function matmulsums_tls(As, Bs)
N = size(first(As), 1)
tls = TaskLocalValue{Matrix{Float64}}(() -> Matrix{Float64}(undef, N, N))
f = WithTaskLocalValue(tls) do C
function (A, B)
mul!(C, A, B)
sum(C)
end
end
tmap(f, As, Bs)
end This is kinda ugly, but the idea is that the macro could then automatically handle the creation of the I can try to put this together in a followup PR to this one, or we can try to integrate it here from the get-go. |
|
This now also works: @tasks for i in 1:10
@set collect=true
i
end It translates to (BTW, I think I love the |
Let's do it in a separate PR. This one is anyway growing by the minute :D |
969979b
to
28321f1
Compare
28321f1
to
060a548
Compare
I think this is pretty much ready. Doc previews is here. |
|
So we'll save the channel support for a future PR? |
Basic attempt at providing a
@threaded [kwargs] for ...
API. The idea is to just transform the code intotforeach
ortmapreduce
calls. (Note, renamed to@tasks for ...
, see below.)Basic examples:
I also took a stab at adding a
@tasklocal
macro that can be used within the loop-body and essentially expands to the commonTaskLocalValue
pattern.Example
@tasklocal
:(with 8 threads)
This expands to
TODOs
@tasklocal
?gensym()
'ed variable name) is available after the loop. Could put everything into alet
block?But the user could be interested in this after loop. Maybe let them choose a name?@tasklocal
variables. (We now accept a single@tasklocal
which can either be a typed assignment or abegin ... end
block of typed assignments)Closes #49