@@ -22,14 +22,14 @@ See also: `BLAS.get_num_threads` and `BLAS.set_num_threads` in the
22
22
"""
23
23
nthreads () = Int (unsafe_load (cglobal (:jl_n_threads , Cint)))
24
24
25
- function threading_run (func)
26
- ccall (:jl_enter_threaded_region , Cvoid, ())
25
+ function threading_run (func, static )
26
+ static && ccall (:jl_enter_threaded_region , Cvoid, ())
27
27
n = nthreads ()
28
28
tasks = Vector {Task} (undef, n)
29
29
for i = 1 : n
30
30
t = Task (func)
31
- t. sticky = true
32
- ccall (:jl_set_task_tid , Cint, (Any, Cint), t, i- 1 )
31
+ t. sticky = static
32
+ static && ccall (:jl_set_task_tid , Cint, (Any, Cint), t, i- 1 )
33
33
tasks[i] = t
34
34
schedule (t)
35
35
end
@@ -38,7 +38,7 @@ function threading_run(func)
38
38
wait (tasks[i])
39
39
end
40
40
finally
41
- ccall (:jl_exit_threaded_region , Cvoid, ())
41
+ static && ccall (:jl_exit_threaded_region , Cvoid, ())
42
42
end
43
43
end
44
44
@@ -86,15 +86,17 @@ function _threadsfor(iter, lbody, schedule)
86
86
end
87
87
end
88
88
end
89
- if threadid () != 1 || ccall (:jl_in_threaded_region , Cint, ()) != 0
89
+ if $ (schedule === :dynamic )
90
+ threading_run (threadsfor_fun, false )
91
+ elseif threadid () != 1 || ccall (:jl_in_threaded_region , Cint, ()) != 0
90
92
$ (if schedule === :static
91
93
:(error (" `@threads :static` can only be used from thread 1 and not nested" ))
92
94
else
93
- # only use threads when called from thread 1, outside @threads
95
+ # only use threads when called from thread 1, outside @threads :static
94
96
:(Base. invokelatest (threadsfor_fun, true ))
95
- end )
97
+ end )
96
98
else
97
- threading_run (threadsfor_fun)
99
+ threading_run (threadsfor_fun, true )
98
100
end
99
101
nothing
100
102
end
@@ -110,15 +112,48 @@ A barrier is placed at the end of the loop which waits for all tasks to finish
110
112
execution.
111
113
112
114
The `schedule` argument can be used to request a particular scheduling policy.
113
- The only currently supported value is `:static`, which creates one task per thread
114
- and divides the iterations equally among them. Specifying `:static` is an error
115
- if used from inside another `@threads` loop or from a thread other than 1.
115
+ Options are:
116
+ - `:static` is the default schedule which creates one task per thread and divides the
117
+ iterations equally among them, assigning each task specifically to each thread.
118
+ Specifying `:static` is an error if used from inside another `@threads` loop
119
+ or from a thread other than 1.
120
+ - `:dynamic` is like `:static` except the tasks are assigned to threads dynamically,
121
+ allowing more flexible scheduling if other tasks are active on other threads.
122
+ Specifying `:dynamic` is allowed from inside another `@threads` loop and from
123
+ threads other than 1.
116
124
117
125
The default schedule (used when no `schedule` argument is present) is subject to change.
118
126
127
+ For example, here an illustration of the different scheduling strategies, where `busywait`
128
+ is a non-yielding timed loop that runs for a number of seconds.
129
+
130
+ ```julia-repl
131
+ julia> @time begin
132
+ Threads.@spawn busywait(5)
133
+ Threads.@threads :static for i in 1:Threads.nthreads()
134
+ busywait(1)
135
+ end
136
+ end
137
+ 6.003001 seconds (16.33 k allocations: 899.255 KiB, 0.25% compilation time)
138
+
139
+ julia> @time begin
140
+ Threads.@spawn busywait(5)
141
+ Threads.@threads :dynamic for i in 1:Threads.nthreads()
142
+ busywait(1)
143
+ end
144
+ end
145
+ 2.012056 seconds (16.05 k allocations: 883.919 KiB, 0.66% compilation time)
146
+ ```
147
+
148
+ The `:dynamic` example takes 2 seconds because one of the non-occupied threads is able
149
+ to run two of the 1-second iterations to complete the for loop.
150
+
119
151
!!! compat "Julia 1.5"
120
152
The `schedule` argument is available as of Julia 1.5.
121
153
154
+ !!! compat "Julia 1.8"
155
+ The `:dynamic` option for the `schedule` argument is available as of Julia 1.8.
156
+
122
157
See also: [`@spawn`](@ref Threads.@spawn), [`nthreads()`](@ref Threads.nthreads),
123
158
[`threadid()`](@ref Threads.threadid), `pmap` in [`Distributed`](@ref man-distributed),
124
159
`BLAS.set_num_threads` in [`LinearAlgebra`](@ref man-linalg).
@@ -133,7 +168,7 @@ macro threads(args...)
133
168
# for now only allow quoted symbols
134
169
sched = nothing
135
170
end
136
- if sched != = :static
171
+ if sched != = :static && sched != = :dynamic
137
172
throw (ArgumentError (" unsupported schedule argument in @threads" ))
138
173
end
139
174
elseif na == 1
0 commit comments