JuliaFolds2 / ChunkSplitters.jl

Simple chunk splitters for parallel loop executions
MIT License
40 stars 5 forks source link

`minchunksize` + `split::SplitStrategy` (new default) #46

Closed carstenbauer closed 1 day ago

carstenbauer commented 1 day ago

Implements the minchunksize option.

carstenbauer commented 1 day ago

@lmiq, with the changes I made we at least get

julia> function f(x; n=nothing, size=nothing)
           s = zero(eltype(x))
           for inds in chunks(x; n=n, size=size)
               for i in inds
                   s += x[i]
               end
           end
           return s
       end
f (generic function with 1 method)

julia> @benchmark f($(rand(10^3)); n=4) samples=1 evals=1
BenchmarkTools.Trial: 1 sample with 1 evaluation.
 Single result which took 959.000 ns (0.00% GC) to evaluate,
 with a memory estimate of 0 bytes, over 0 allocations.

julia> VERSION
v"1.10.5"

If one wants to stay at 0 allocations, one has to provide split in form of a SplitterType rather than a Symbol.

ulia> function f(x; n=nothing, size=nothing)
           s = zero(eltype(x))
           for inds in chunks(x; n=n, size=size, split=:batch)
               for i in inds
                   s += x[i]
               end
           end
           return s
       end
f (generic function with 1 method)

julia> @benchmark f($(rand(10^3)); n=4) samples=1 evals=1
BenchmarkTools.Trial: 1 sample with 1 evaluation.
 Single result which took 958.000 ns (0.00% GC) to evaluate,
 with a memory estimate of 32 bytes, over 1 allocations.

julia> function f(x; n=nothing, size=nothing)
           s = zero(eltype(x))
           for inds in chunks(x; n=n, size=size, split=ChunkSplitters.BatchSplitter())
               for i in inds
                   s += x[i]
               end
           end
           return s
       end
f (generic function with 1 method)

julia> @benchmark f($(rand(10^3)); n=4) samples=1 evals=1
BenchmarkTools.Trial: 1 sample with 1 evaluation.
 Single result which took 958.000 ns (0.00% GC) to evaluate,
 with a memory estimate of 0 bytes, over 0 allocations.

I think this isn't too bad: Most people won't specify split anyways and won't notice anything. Those who do set split can set it in a slightly different way and still get 0 allocations. And the 1 allocation isn't so bad that one necessarily has to care about it.

lmiq commented 1 day ago

Honestly I find that magical... but if it works, it works. Its fine for me.

carstenbauer commented 1 day ago

It's not magical: Julia specializes on types (also those of keyword arguments). Hence it can figure out from the function call what kind of splitter we use etc. This is different if you specify split as a Symbol because then Julia has to look at values to deduce the splitter type etc.

lmiq commented 1 day ago

Ah, of course, at first I didn't realize that passing a symbol would still allocate.

lmiq commented 1 day ago

I guess we need to document the passing of types then. Maybe remove the symbol option in a 3.0.

carstenbauer commented 1 day ago

I'm doing it. Let me make a few more changes and then you can make a pass.

carstenbauer commented 1 day ago

Alright, feel free to review. Tests should all pass.