|
1 | 1 | module Schedulers
|
2 | 2 |
|
3 | 3 | using Base.Threads: nthreads
|
4 |
| -using ChunkSplitters: Split, Consecutive, RoundRobin |
| 4 | +using ChunkSplitters: Split, Consecutive, RoundRobin, ChunkSplitters |
5 | 5 |
|
6 | 6 | # Used to indicate that a keyword argument has not been set by the user.
|
7 | 7 | # We don't use Nothing because nothing maybe sometimes be a valid user input (e.g. for init)
|
@@ -65,70 +65,61 @@ struct NoChunking <: ChunkingMode end
|
65 | 65 | struct FixedCount <: ChunkingMode end
|
66 | 66 | struct FixedSize <: ChunkingMode end
|
67 | 67 |
|
| 68 | +chunksplitter_mode(::Type{FixedCount}) = ChunkSplitters.Internals.FixedCount |
| 69 | +chunksplitter_mode(::Type{FixedSize}) = ChunkSplitters.Internals.FixedSize |
| 70 | + |
68 | 71 | """
|
69 |
| - ChunkingArgs{C, S <: Split}(n::Int, size::Int, split::S) |
70 |
| - ChunkingArgs(Sched::Type{<:Scheduler}, n::MaybeInteger, size::MaybeInteger, split::Union{Symbol, Split}; chunking) |
| 72 | + ChunkingArgs{C, S <: Split}(n::Union{Int, Nothing}, size::Union{Int, Nothing}, minsize::Union{Int, Nothing}, split::S) |
| 73 | + ChunkingArgs(Sched::Type{<:Scheduler}; n = nothing, size = nothing, minsize = nothing, split::Union{Symbol, Split}; chunking) |
71 | 74 |
|
72 | 75 | Stores all the information needed for chunking. The type parameter `C` is the chunking mode
|
73 |
| -(`NoChunking`, `FixedSize`, or `FixedCount`). |
74 |
| -
|
75 |
| -`MaybeInteger` arguments are arguments that can be `NotGiven`. If it is the case, the |
76 |
| -constructor automatically throws errors or gives defaults values while taking into account |
77 |
| -the kind of scheduler (provided by `Sched`, e.g. `DynamicScheduler`). The `chunking` keyword |
78 |
| -argument is a boolean and if true, everything is skipped and `C = NoChunking`. |
| 76 | +(`NoChunking`, `FixedSize`, or `FixedCount`). The `chunking` keyword argument is a boolean |
| 77 | +and if true, everything is skipped and `C = NoChunking`. |
79 | 78 |
|
80 | 79 | Once the object is created, use the `has_fieldname(object)` function (e.g. `has_size(object)`)
|
81 |
| -to know if the field is effectively used, since it is no longer |
82 |
| -`NotGiven` for type stability. |
| 80 | +to know if the field is effectively used. |
83 | 81 | """
|
84 | 82 | struct ChunkingArgs{C, S <: Split}
|
85 |
| - n::Int |
86 |
| - size::Int |
87 |
| - split::S |
| 83 | + n::Union{Int, Nothing} |
| 84 | + size::Union{Int, Nothing} |
88 | 85 | minsize::Union{Int, Nothing}
|
| 86 | + split::S |
| 87 | +end |
| 88 | +function ChunkingArgs(::Type{NoChunking}) |
| 89 | + ChunkingArgs{NoChunking, NoSplit}(nothing, nothing, nothing, NoSplit()) |
89 | 90 | end
|
90 |
| -ChunkingArgs(::Type{NoChunking}) = ChunkingArgs{NoChunking, NoSplit}(-1, -1, NoSplit(), nothing) |
91 | 91 | function ChunkingArgs(
|
92 |
| - Sched::Type{<:Scheduler}, |
93 |
| - n::MaybeInteger, |
94 |
| - size::MaybeInteger, |
95 |
| - split::Union{Symbol, Split}; |
96 |
| - minsize=nothing, |
| 92 | + Sched::Type{<:Scheduler}; |
| 93 | + n = nothing, |
| 94 | + size = nothing, |
| 95 | + minsize = nothing, |
| 96 | + split::Union{Symbol, Split}, |
97 | 97 | chunking
|
98 | 98 | )
|
99 | 99 | chunking || return ChunkingArgs(NoChunking)
|
100 | 100 |
|
101 |
| - if !isgiven(n) && !isgiven(size) |
| 101 | + if isnothing(n) && isnothing(size) |
102 | 102 | n = default_nchunks(Sched)
|
103 |
| - size = -1 |
104 |
| - else |
105 |
| - n = isgiven(n) ? n : -1 |
106 |
| - size = isgiven(size) ? size : -1 |
| 103 | + elseif !isnothing(n) && !isnothing(size) |
| 104 | + throw(ArgumentError("nchunks and chunksize are mutually exclusive")) |
107 | 105 | end
|
108 |
| - |
109 |
| - chunking_mode = size > 0 ? FixedSize : FixedCount |
| 106 | + chunking_mode = isnothing(n) ? FixedSize : FixedCount |
110 | 107 | split = _parse_split(split)
|
111 |
| - result = ChunkingArgs{chunking_mode, typeof(split)}(n, size, split, minsize) |
112 |
| - |
113 |
| - # argument names in error messages are those of the scheduler constructor instead |
114 |
| - # of ChunkingArgs because the user should not be aware of the ChunkingArgs type |
115 |
| - # (e.g. `nchunks` instead of `n`) |
116 |
| - if !(has_n(result) || has_size(result)) |
117 |
| - throw(ArgumentError("Either `nchunks` or `chunksize` must be a positive integer (or chunking=false).")) |
118 |
| - end |
119 |
| - if has_n(result) && has_size(result) |
120 |
| - throw(ArgumentError("`nchunks` and `chunksize` are mutually exclusive and only one of them may be a positive integer")) |
121 |
| - end |
122 |
| - return result |
| 108 | + return ChunkingArgs{chunking_mode, typeof(split)}(n, size, minsize, split) |
123 | 109 | end
|
124 | 110 |
|
125 | 111 | chunking_mode(::ChunkingArgs{C}) where {C} = C
|
126 |
| -has_n(ca::ChunkingArgs) = ca.n > 0 |
127 |
| -has_size(ca::ChunkingArgs) = ca.size > 0 |
| 112 | +has_n(ca::ChunkingArgs) = !isnothing(ca.n) |
| 113 | +has_size(ca::ChunkingArgs) = !isnothing(ca.size) |
128 | 114 | has_split(::ChunkingArgs{C, S}) where {C, S} = S !== NoSplit
|
129 | 115 | has_minsize(ca::ChunkingArgs) = !isnothing(ca.minsize)
|
130 | 116 | chunking_enabled(ca::ChunkingArgs) = chunking_mode(ca) != NoChunking
|
131 | 117 |
|
| 118 | +function chunkingargs_to_kwargs(ca::ChunkingArgs, arg) |
| 119 | + minsize = !has_minsize(ca) ? nothing : min(ca.minsize, length(arg)) |
| 120 | + return (; ca.n, ca.size, minsize, ca.split) |
| 121 | +end |
| 122 | + |
132 | 123 | _chunkingstr(ca::ChunkingArgs{NoChunking}) = "none"
|
133 | 124 | function _chunkingstr(ca::ChunkingArgs{FixedCount})
|
134 | 125 | str = "fixed count ($(ca.n)), split :$(_splitid(ca.split))"
|
@@ -157,6 +148,10 @@ has_chunksize(sched::Scheduler) = has_size(chunking_args(sched))
|
157 | 148 | has_chunksplit(sched::Scheduler) = has_split(chunking_args(sched))
|
158 | 149 | has_minchunksize(sched::Scheduler) = has_minsize(chunking_args(sched))
|
159 | 150 |
|
| 151 | +function chunkingargs_to_kwargs(sched::Scheduler, arg) |
| 152 | + chunkingargs_to_kwargs(chunking_args(sched), arg) |
| 153 | +end |
| 154 | + |
160 | 155 | chunking_mode(sched::Scheduler) = chunking_mode(chunking_args(sched))
|
161 | 156 | chunking_enabled(sched::Scheduler) = chunking_enabled(chunking_args(sched))
|
162 | 157 | _chunkingstr(sched::Scheduler) = _chunkingstr(chunking_args(sched))
|
@@ -203,43 +198,45 @@ with other multithreaded code.
|
203 | 198 | * Possible options are `:default` and `:interactive`.
|
204 | 199 | * The high-priority pool `:interactive` should be used very carefully since tasks on this threadpool should not be allowed to run for a long time without `yield`ing as it can interfere with [heartbeat](https://en.wikipedia.org/wiki/Heartbeat_(computing)) processes.
|
205 | 200 | """
|
206 |
| -struct DynamicScheduler{C <: ChunkingMode, S <: Split} <: Scheduler |
207 |
| - threadpool::Symbol |
| 201 | +struct DynamicScheduler{C <: ChunkingMode, S <: Split, threadpool} <: Scheduler |
208 | 202 | chunking_args::ChunkingArgs{C, S}
|
209 | 203 |
|
210 | 204 | function DynamicScheduler(threadpool::Symbol, ca::ChunkingArgs)
|
211 | 205 | if !(threadpool in (:default, :interactive))
|
212 | 206 | throw(ArgumentError("threadpool must be either :default or :interactive"))
|
213 | 207 | end
|
214 |
| - new{chunking_mode(ca), typeof(ca.split)}(threadpool, ca) |
| 208 | + new{chunking_mode(ca), typeof(ca.split), threadpool}(ca) |
215 | 209 | end
|
216 | 210 | end
|
217 | 211 |
|
218 | 212 | function DynamicScheduler(;
|
219 | 213 | threadpool::Symbol = :default,
|
220 |
| - nchunks::MaybeInteger = NotGiven(), |
221 |
| - ntasks::MaybeInteger = NotGiven(), # "alias" for nchunks |
222 |
| - chunksize::MaybeInteger = NotGiven(), |
223 |
| - chunking::Bool = true, |
| 214 | + nchunks = nothing, |
| 215 | + ntasks = nothing, # "alias" for nchunks |
| 216 | + chunksize = nothing, |
224 | 217 | split::Union{Split, Symbol} = Consecutive(),
|
225 |
| - minchunksize::Union{Nothing, Int}=nothing) |
226 |
| - if isgiven(ntasks) |
227 |
| - if isgiven(nchunks) |
| 218 | + minchunksize = nothing, |
| 219 | + chunking::Bool = true |
| 220 | +) |
| 221 | + if !isnothing(ntasks) |
| 222 | + if !isnothing(nchunks) |
228 | 223 | throw(ArgumentError("For the dynamic scheduler, nchunks and ntasks are aliases and only one may be provided"))
|
229 | 224 | end
|
230 | 225 | nchunks = ntasks
|
231 | 226 | end
|
232 |
| - ca = ChunkingArgs(DynamicScheduler, nchunks, chunksize, split; chunking, minsize=minchunksize) |
| 227 | + ca = ChunkingArgs(DynamicScheduler; |
| 228 | + n = nchunks, size = chunksize, minsize = minchunksize, split, chunking) |
233 | 229 | return DynamicScheduler(threadpool, ca)
|
234 | 230 | end
|
235 | 231 | from_symbol(::Val{:dynamic}) = DynamicScheduler
|
236 | 232 | chunking_args(sched::DynamicScheduler) = sched.chunking_args
|
| 233 | +threadpool(::DynamicScheduler{C, S, T}) where {C, S, T} = T |
237 | 234 |
|
238 | 235 | function Base.show(io::IO, mime::MIME{Symbol("text/plain")}, s::DynamicScheduler)
|
239 | 236 | print(io, "DynamicScheduler", "\n")
|
240 | 237 | cstr = _chunkingstr(s.chunking_args)
|
241 | 238 | println(io, "├ Chunking: ", cstr)
|
242 |
| - print(io, "└ Threadpool: ", s.threadpool) |
| 239 | + print(io, "└ Threadpool: ", threadpool(s)) |
243 | 240 | end
|
244 | 241 |
|
245 | 242 | """
|
@@ -278,19 +275,21 @@ struct StaticScheduler{C <: ChunkingMode, S <: Split} <: Scheduler
|
278 | 275 | end
|
279 | 276 |
|
280 | 277 | function StaticScheduler(;
|
281 |
| - nchunks::MaybeInteger = NotGiven(), |
282 |
| - ntasks::MaybeInteger = NotGiven(), # "alias" for nchunks |
283 |
| - chunksize::MaybeInteger = NotGiven(), |
284 |
| - chunking::Bool = true, |
| 278 | + nchunks = nothing, |
| 279 | + ntasks = nothing, # "alias" for nchunks |
| 280 | + chunksize = nothing, |
| 281 | + minchunksize = nothing, |
285 | 282 | split::Union{Split, Symbol} = Consecutive(),
|
286 |
| - minchunksize::Union{Nothing, Int} = nothing) |
287 |
| - if isgiven(ntasks) |
288 |
| - if isgiven(nchunks) |
| 283 | + chunking::Bool = true |
| 284 | +) |
| 285 | + if !isnothing(ntasks) |
| 286 | + if !isnothing(nchunks) |
289 | 287 | throw(ArgumentError("For the static scheduler, nchunks and ntasks are aliases and only one may be provided"))
|
290 | 288 | end
|
291 | 289 | nchunks = ntasks
|
292 | 290 | end
|
293 |
| - ca = ChunkingArgs(StaticScheduler, nchunks, chunksize, split; chunking, minsize=minchunksize) |
| 291 | + ca = ChunkingArgs(StaticScheduler; |
| 292 | + n = nchunks, size = chunksize, minsize = minchunksize, split, chunking) |
294 | 293 | return StaticScheduler(ca)
|
295 | 294 | end
|
296 | 295 | from_symbol(::Val{:static}) = StaticScheduler
|
@@ -349,15 +348,17 @@ end
|
349 | 348 |
|
350 | 349 | function GreedyScheduler(;
|
351 | 350 | ntasks::Integer = nthreads(),
|
352 |
| - nchunks::MaybeInteger = NotGiven(), |
353 |
| - chunksize::MaybeInteger = NotGiven(), |
354 |
| - chunking::Bool = false, |
| 351 | + nchunks = nothing, |
| 352 | + chunksize = nothing, |
| 353 | + minchunksize = nothing, |
355 | 354 | split::Union{Split, Symbol} = RoundRobin(),
|
356 |
| - minchunksize::Union{Nothing, Int} = nothing) |
357 |
| - if isgiven(nchunks) || isgiven(chunksize) |
| 355 | + chunking::Bool = false |
| 356 | +) |
| 357 | + if !(isnothing(nchunks) && isnothing(chunksize)) |
358 | 358 | chunking = true
|
359 | 359 | end
|
360 |
| - ca = ChunkingArgs(GreedyScheduler, nchunks, chunksize, split; chunking, minsize=minchunksize) |
| 360 | + ca = ChunkingArgs(GreedyScheduler; |
| 361 | + n = nchunks, size = chunksize, minsize = minchunksize, split, chunking) |
361 | 362 | return GreedyScheduler(ntasks, ca)
|
362 | 363 | end
|
363 | 364 | from_symbol(::Val{:greedy}) = GreedyScheduler
|
|
0 commit comments