From 0b0e3af88113d1f87e33928f6f81e0d408a99d91 Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Thu, 24 Aug 2023 14:31:06 -0400 Subject: [PATCH 1/3] Add reducer --- src/ScopedValues.jl | 2 ++ src/reducers.jl | 25 +++++++++++++++++++++++++ test/reducers.jl | 39 +++++++++++++++++++++++++++++++++++++++ test/runtests.jl | 2 ++ 4 files changed, 68 insertions(+) create mode 100644 src/reducers.jl create mode 100644 test/reducers.jl diff --git a/src/ScopedValues.jl b/src/ScopedValues.jl index a188d01..87c1c39 100644 --- a/src/ScopedValues.jl +++ b/src/ScopedValues.jl @@ -156,4 +156,6 @@ end end # isdefined +include("reducers.jl") + end # module ScopedValues diff --git a/src/reducers.jl b/src/reducers.jl new file mode 100644 index 0000000..550008f --- /dev/null +++ b/src/reducers.jl @@ -0,0 +1,25 @@ +mutable struct Reducer{T, Op, Init} + @atomic value::T + const op::Op + const init::Init +end +Reducer(op::Op, init::Init) where {Op, Init} = Reducer(init(), op, init) + +Base.getindex(r::Reducer) = r.value +function Base.setindex!(r::Reducer{T, Op}, val::T) where {T, Op} + _, new = @atomic r.value r.op val + new +end + +split(r::Reducer{T, Op, Init}) where {T, Op, Init} = Reducer(r.init()::T, r.op, r.init) +function join!(r::Reducer{T, Op, Init}, r2::Reducer{T, Op, Init}) where {T, Op, Init} + r[] = r2[] +end + +function split(f, val::ScopedValue{<:Reducer}) + reducer = split(val[]) + @scoped val => reducer begin + f() + end + join!(val, reducer) +end \ No newline at end of file diff --git a/test/reducers.jl b/test/reducers.jl new file mode 100644 index 0000000..c038ff0 --- /dev/null +++ b/test/reducers.jl @@ -0,0 +1,39 @@ +import Base.Threads: @spawn +import ScopedValues: Reducer, split, join! + +# This is not a good example for ScopedValue + +function splitting_reduce(op::Op, arr::Array{T}, initf=()->zero(T); grainsize=16) where {T, Op} + if length(arr) <= grainsize + return reduce(op, arr; init=initf()::T) + end + reducer = Reducer(op, initf) + splitting_reduce(ScopedValue(reducer), arr, 1:length(arr), grainsize) + return reducer[] +end + +function splitting_reduce(scoped_reducer::ScopedValue{Reducer{T, Op, Init}}, arr, range, grainsize) where {T, Op, Init} + reducer = scoped_reducer[] + if length(range) <= grainsize + reducer[] = reduce(reducer.op, view(arr, range); init=reducer.init()::T) + return + end + midpoint = length(range) ÷ 2 + range_a = first(range):(first(range)+midpoint) + range_b = (first(range)+midpoint+1):last(range) + + task = @scoped scoped_reducer => split(reducer) begin + @spawn begin + splitting_reduce(scoped_reducer, arr, range_a, grainsize) + join!(reducer, scoped_reducer[]) + end + end + @scoped scoped_reducer => split(reducer) begin + splitting_reduce(scoped_reducer, arr, range_b, grainsize) + join!(reducer, scoped_reducer[]) + end + wait(task) + return reducer[] +end + +@test splitting_reduce(+, ones(1024)) == 1024 \ No newline at end of file diff --git a/test/runtests.jl b/test/runtests.jl index 7a0e912..a6ba8cc 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -120,3 +120,5 @@ end end end +include("reducers.jl") + From d94eb2ad0028ecf04af99e793914629386a39409 Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Thu, 24 Aug 2023 16:30:11 -0400 Subject: [PATCH 2/3] make it an actual nice example --- src/reducers.jl | 6 +++--- test/reducers.jl | 41 ++++++++++++++++------------------------- 2 files changed, 19 insertions(+), 28 deletions(-) diff --git a/src/reducers.jl b/src/reducers.jl index 550008f..9903563 100644 --- a/src/reducers.jl +++ b/src/reducers.jl @@ -12,8 +12,8 @@ function Base.setindex!(r::Reducer{T, Op}, val::T) where {T, Op} end split(r::Reducer{T, Op, Init}) where {T, Op, Init} = Reducer(r.init()::T, r.op, r.init) -function join!(r::Reducer{T, Op, Init}, r2::Reducer{T, Op, Init}) where {T, Op, Init} - r[] = r2[] +function join!(r::Reducer{T, Op, Init}, other_r::Reducer{T, Op, Init}) where {T, Op, Init} + r[] = other_[] end function split(f, val::ScopedValue{<:Reducer}) @@ -21,5 +21,5 @@ function split(f, val::ScopedValue{<:Reducer}) @scoped val => reducer begin f() end - join!(val, reducer) + join!(val[], reducer) end \ No newline at end of file diff --git a/test/reducers.jl b/test/reducers.jl index c038ff0..eafabcd 100644 --- a/test/reducers.jl +++ b/test/reducers.jl @@ -1,39 +1,30 @@ import Base.Threads: @spawn import ScopedValues: Reducer, split, join! -# This is not a good example for ScopedValue - function splitting_reduce(op::Op, arr::Array{T}, initf=()->zero(T); grainsize=16) where {T, Op} if length(arr) <= grainsize return reduce(op, arr; init=initf()::T) end - reducer = Reducer(op, initf) - splitting_reduce(ScopedValue(reducer), arr, 1:length(arr), grainsize) - return reducer[] -end + reducer = ScopedValue(Reducer(op, initf)) -function splitting_reduce(scoped_reducer::ScopedValue{Reducer{T, Op, Init}}, arr, range, grainsize) where {T, Op, Init} - reducer = scoped_reducer[] - if length(range) <= grainsize - reducer[] = reduce(reducer.op, view(arr, range); init=reducer.init()::T) - return - end - midpoint = length(range) ÷ 2 - range_a = first(range):(first(range)+midpoint) - range_b = (first(range)+midpoint+1):last(range) + function reduce_impl(arr, range, grainsize) + if length(range) <= grainsize + reducer[][] = reduce(op, view(arr, range); init=initf()::T) + return + end + midpoint = length(range) ÷ 2 + range_a = first(range):(first(range)+midpoint) + range_b = (first(range)+midpoint+1):last(range) - task = @scoped scoped_reducer => split(reducer) begin - @spawn begin - splitting_reduce(scoped_reducer, arr, range_a, grainsize) - join!(reducer, scoped_reducer[]) + @sync begin + @spawn split(reducer) do + reduce_impl(arr, range_a, grainsize) + end + reduce_impl(arr, range_b, grainsize) end end - @scoped scoped_reducer => split(reducer) begin - splitting_reduce(scoped_reducer, arr, range_b, grainsize) - join!(reducer, scoped_reducer[]) - end - wait(task) - return reducer[] + reduce_impl(arr, 1:length(arr), grainsize) + return reducer[][] end @test splitting_reduce(+, ones(1024)) == 1024 \ No newline at end of file From e3cdaf6c51dc2eecd592dbad7e681109c3e333b0 Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Thu, 24 Aug 2023 16:32:32 -0400 Subject: [PATCH 3/3] fixup! make it an actual nice example --- src/reducers.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/reducers.jl b/src/reducers.jl index 9903563..a2e276b 100644 --- a/src/reducers.jl +++ b/src/reducers.jl @@ -13,7 +13,7 @@ end split(r::Reducer{T, Op, Init}) where {T, Op, Init} = Reducer(r.init()::T, r.op, r.init) function join!(r::Reducer{T, Op, Init}, other_r::Reducer{T, Op, Init}) where {T, Op, Init} - r[] = other_[] + r[] = other_r[] end function split(f, val::ScopedValue{<:Reducer})