Skip to content

Commit

Permalink
Merge branch 'master' into WithTaskLocalValues
Browse files Browse the repository at this point in the history
  • Loading branch information
MasonProtter authored Mar 4, 2024
2 parents 6435fff + d44af6f commit 717799f
Show file tree
Hide file tree
Showing 12 changed files with 131 additions and 108 deletions.
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
OhMyThreads.jl Changelog
=========================

Version 0.4.7
Version 0.5.1
-------------

- ![feature][badge-feature] Added `OhMyThreads.WithTaskLocals` that represents a closure over `TaskLocalValues`, but can have those values materialized as an optimization (using `OhMyThreads.promise_task_local`)
- ![Enhancement][badge-enhancement] Made `@tasks` use `OhMyThreads.WithTaskLocals` automatically as an optimization.

Version 0.5.0
-------------

- ![Feature][badge-feature] `@set init = ...` may now be used to specify an initial value for a reduction (only has an effect in conjuction with `@set reducer=...` and triggers a warning otherwise).
- ![BREAKING][badge-breaking] Within a `@tasks` block, task-local values must from now on be defined via `@local` instead of `@init` (renamed).
- ![BREAKING][badge-breaking] The (already deprecated) `SpawnAllScheduler` has been dropped.

Version 0.4.6
-------------

Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "OhMyThreads"
uuid = "67456a42-1dca-4109-a031-0a68de7e3ad5"
authors = ["Carsten Bauer <[email protected]>", "Mason Protter <[email protected]>"]
version = "0.4.5"
version = "0.4.6"

[deps]
BangBang = "198e06fe-97b7-11e9-32a5-e1d131e6ad66"
Expand Down
2 changes: 1 addition & 1 deletion docs/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
DocumenterTools = "35a29f4d-8980-5a13-9543-d66fff28ecb8"

[compat]
Documenter = "1.2"
Documenter = "1.3"
DocumenterTools = "0.1"
6 changes: 3 additions & 3 deletions docs/src/literate/tls/tls.jl
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ res ≈ res_tlv
# (instead of $O(\textrm{iterations})$) allocations.
#
# Note that if you use our `@tasks` macro API, there is built-in support for task-local
# values via `@init`.
# values via `@local`.
#

using OhMyThreads: @tasks
Expand All @@ -174,7 +174,7 @@ function matmulsums_tlv_macro(As, Bs; kwargs...)
N = size(first(As), 1)
@tasks for i in eachindex(As,Bs)
@set collect=true
@init C::Matrix{Float64} = Matrix{Float64}(undef, N, N)
@local C::Matrix{Float64} = Matrix{Float64}(undef, N, N)
mul!(C, As[i], Bs[i])
sum(C)
end
Expand All @@ -183,7 +183,7 @@ end
res_tlv_macro = matmulsums_tlv_macro(As, Bs)
res res_tlv_macro

# Here, `@init` simply expands to the explicit pattern around `TaskLocalValue` above.
# Here, `@local` simply expands to the explicit pattern around `TaskLocalValue` above.
#
#
# ### Benchmark
Expand Down
6 changes: 3 additions & 3 deletions docs/src/literate/tls/tls.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ This solves our issues above and leads to $O(\textrm{parallel tasks})$
(instead of $O(\textrm{iterations})$) allocations.

Note that if you use our `@tasks` macro API, there is built-in support for task-local
values via `@init`.
values via `@local`.

````julia
using OhMyThreads: @tasks
Expand All @@ -224,7 +224,7 @@ function matmulsums_tlv_macro(As, Bs; kwargs...)
N = size(first(As), 1)
@tasks for i in eachindex(As,Bs)
@set collect=true
@init C::Matrix{Float64} = Matrix{Float64}(undef, N, N)
@local C::Matrix{Float64} = Matrix{Float64}(undef, N, N)
mul!(C, As[i], Bs[i])
sum(C)
end
Expand All @@ -238,7 +238,7 @@ res ≈ res_tlv_macro
true
````

Here, `@init` simply expands to the explicit pattern around `TaskLocalValue` above.
Here, `@local` simply expands to the explicit pattern around `TaskLocalValue` above.


### Benchmark
Expand Down
13 changes: 5 additions & 8 deletions docs/src/refs/api.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
# [Public API](@id API)

## Index

```@index
Pages = ["api.md"]
Order = [:function, :macro, :type]
```@meta
CollapsedDocStrings = true
```

# [Public API](@id API)

## Exported

### Macros
```@docs
@tasks
@set
@init
@local
```

### Functions
Expand Down
14 changes: 6 additions & 8 deletions docs/src/refs/internal.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
# Internal

**The following is internal, i.e. not public API, and might change at any point.**
```@meta
CollapsedDocStrings = true
```

## Index
# Internal

```@index
Pages = ["internal.md"]
Order = [:function, :macro]
```
!!! warning
**Everything on this page is internal and might change at any point!**

## References

Expand Down
9 changes: 4 additions & 5 deletions src/OhMyThreads.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module OhMyThreads

using StableTasks: StableTasks
for mac Symbol.(["@spawn", "@spawnat", "@fetch", "@fetchfrom"])
for mac in Symbol.(["@spawn", "@spawnat", "@fetch", "@fetchfrom"])
@eval const $mac = getproperty(StableTasks, $(QuoteNode(mac)))
end

Expand All @@ -16,12 +16,11 @@ include("macros.jl")

include("tools.jl")
include("schedulers.jl")
using .Schedulers: Scheduler,
DynamicScheduler, StaticScheduler, GreedyScheduler, SpawnAllScheduler
using .Schedulers: Scheduler, DynamicScheduler, StaticScheduler, GreedyScheduler
include("implementation.jl")

export @tasks, @set, @init
export @tasks, @set, @local
export treduce, tmapreduce, treducemap, tmap, tmap!, tforeach, tcollect
export Scheduler, DynamicScheduler, StaticScheduler, GreedyScheduler, SpawnAllScheduler
export Scheduler, DynamicScheduler, StaticScheduler, GreedyScheduler

end # module OhMyThreads
73 changes: 40 additions & 33 deletions src/macro_impl.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,9 @@ function tasks_macro(forex)

settings = Settings()

inits_before, inits_names = _maybe_handle_init_block!(forbody.args)
tls_names = isnothing(inits_before) ? [] : map(x -> x.args[1], inits_before)

_maybe_handle_set_block!(settings, forbody.args)
locals_before, local_inner = _maybe_handle_atlocal_block!(forbody.args)
tls_names = isnothing(locals_before) ? [] : map(x -> x.args[1], locals_before)
_maybe_handle_atset_block!(settings, forbody.args)

forbody = esc(forbody)
itrng = esc(itrng)
Expand All @@ -44,11 +43,13 @@ function tasks_macro(forex)
tmapreduce(mapping_function, $(settings.reducer), $(itrng); scheduler = $(settings.scheduler))
end
elseif settings.collect
maybe_warn_useless_init(settings)
quote
$make_mapping_function
tmap(mapping_function, $(itrng); scheduler = $(settings.scheduler))
end
else
maybe_warn_useless_init(settings)
quote
$make_mapping_function
tforeach(mapping_function, $(itrng); scheduler = $(settings.scheduler))
Expand All @@ -60,19 +61,25 @@ function tasks_macro(forex)
result = :(let
end)
push!(result.args[2].args, q)
if !isnothing(inits_before)
for x in inits_before
if !isnothing(locals_before)
for x in locals_before
push!(result.args[1].args, x)
end
end

result
end

function maybe_warn_useless_init(settings)
!isnothing(settings.init) &&
@warn("The @set init = ... settings won't have any effect because no reduction is performed.")
end

Base.@kwdef mutable struct Settings
scheduler::Expr = :(DynamicScheduler())
reducer::Union{Expr, Symbol, Nothing} = nothing
collect::Bool = false
init::Union{Expr, Symbol, Nothing} = nothing
end

function _sym2scheduler(s)
Expand All @@ -87,65 +94,65 @@ function _sym2scheduler(s)
end
end

function _maybe_handle_init_block!(args)
inits_before = nothing
init_inner = nothing
function _maybe_handle_atlocal_block!(args)
locals_before = nothing
local_inner = nothing
tlsidx = findfirst(args) do arg
arg isa Expr && arg.head == :macrocall && arg.args[1] == Symbol("@init")
arg isa Expr && arg.head == :macrocall && arg.args[1] == Symbol("@local")
end
if !isnothing(tlsidx)
inits_before, init_inner = _unfold_init_block(args[tlsidx].args[3])
locals_before, local_inner = _unfold_atlocal_block(args[tlsidx].args[3])
deleteat!(args, tlsidx)
end
return inits_before, init_inner
return locals_before, local_inner
end

function _unfold_init_block(ex)
inits_before = Expr[]
inits_names = Expr[]
function _unfold_atlocal_block(ex)
locals_before = Expr[]
locals_names = Expr[]
if ex.head == :(=)
initb, init_name = _init_assign_to_exprs(ex)
push!(inits_before, initb)
push!(inits_names, init_name)
localb, localn = _init_assign_to_exprs(ex)
push!(locals_before, initb)
push!(locals_names, localn)
elseif ex.head == :block
tlsexprs = filter(x -> x isa Expr, ex.args) # skip LineNumberNode
for x in tlsexprs
initb, initn = _init_assign_to_exprs(x)
push!(inits_before, initb)
push!(inits_names, initn)
localb, localn = _init_assign_to_exprs(x)
push!(locals_before, localb)
push!(locals_names, localn)
end
else
throw(ErrorException("Wrong usage of @init. You must either provide a typed assignment or multiple typed assignments in a `begin ... end` block."))
throw(ErrorException("Wrong usage of @local. You must either provide a typed assignment or multiple typed assignments in a `begin ... end` block."))
end
return inits_before, inits_names
return locals_before, local_inner
end

function _init_assign_to_exprs(ex)
function _atlocal_assign_to_exprs(ex)
left_ex = ex.args[1]
if left_ex isa Symbol || left_ex.head != :(::)
throw(ErrorException("Wrong usage of @init. Expected typed assignment, e.g. `A::Matrix{Float} = rand(2,2)`."))
throw(ErrorException("Wrong usage of @local. Expected typed assignment, e.g. `A::Matrix{Float} = rand(2,2)`."))
end
tls_sym = esc(left_ex.args[1])
tls_type = esc(left_ex.args[2])
tls_def = esc(ex.args[2])
@gensym tl_storage
init_before = :($(tl_storage) = TaskLocalValue{$tls_type}(() -> $(tls_def)))
init_name = :($(tls_sym))
return init_before, init_name
local_before = :($(tl_storage) = TaskLocalValue{$tls_type}(() -> $(tls_def)))
local_name = :($(tls_sym))
return local_before, local_name
end

function _maybe_handle_set_block!(settings, args)
function _maybe_handle_atset_block!(settings, args)
idcs = findall(args) do arg
arg isa Expr && arg.head == :macrocall && arg.args[1] == Symbol("@set")
end
isnothing(idcs) && return # no set block found
isnothing(idcs) && return # no @set block found
for i in idcs
ex = args[i].args[3]
if ex.head == :(=)
_handle_set_single_assign!(settings, ex)
_handle_atset_single_assign!(settings, ex)
elseif ex.head == :block
exprs = filter(x -> x isa Expr, ex.args) # skip LineNumberNode
_handle_set_single_assign!.(Ref(settings), exprs)
_handle_atset_single_assign!.(Ref(settings), exprs)
else
throw(ErrorException("Wrong usage of @set. You must either provide an assignment or multiple assignments in a `begin ... end` block."))
end
Expand All @@ -157,7 +164,7 @@ function _maybe_handle_set_block!(settings, args)
end
end

function _handle_set_single_assign!(settings, ex)
function _handle_atset_single_assign!(settings, ex)
if ex.head != :(=)
throw(ErrorException("Wrong usage of @set. Expected assignment, e.g. `scheduler = StaticScheduler()`."))
end
Expand Down
Loading

0 comments on commit 717799f

Please sign in to comment.