@@ -27,6 +27,11 @@ its result will be passed into the function receiving the argument. If the
2727argument is * not* an [ ` DTask ` ] ( @ref ) (instead, some other type of Julia object),
2828it'll be passed as-is to the function ` f ` (with some exceptions).
2929
30+ !!! note "Task / thread occupancy"
31+ By default, ` Dagger ` assumes that tasks saturate the thread they are running on and does not try to schedule other tasks on the thread.
32+ This default can be controlled by specifying [ ` Sch.ThunkOptions ` ] ( @ref ) (more details can be found under [ Scheduler and Thunk options] ( @ref ) ).\
33+ The section [ Changing the thread occupancy for low-utilization tasks] ( @ref ) shows a runnable example of how to achieve this.
34+
3035## Options
3136
3237The [ ` Options ` ] (@ref Dagger.Options) struct in the second argument position is
@@ -215,3 +220,55 @@ Dagger.spawn(+, Dagger.Options(;single=1), 1, 2)
215220
216221delayed (+ ; single= 1 )(1 , 2 )
217222```
223+
224+ ### Changing the thread occupancy for low-utilization tasks
225+
226+ One of the supported [ ` Sch.ThunkOptions ` ] ( @ref ) is the ` occupancy ` keyword.
227+ The basic usage looks like this:
228+
229+ ``` julia
230+ Dagger. @spawn occupancy= Dict (Dagger. ThreadProc=> 0 ) fn
231+ ```
232+
233+ Consider the following function definitions:
234+
235+ ``` @example sleep
236+ using Dagger
237+
238+ function inner()
239+ sleep(0.1)
240+ end
241+
242+ function outer_full_occupancy()
243+ @sync for _ in 1:2
244+ # By default, full occupancy is assumed
245+ Dagger.@spawn inner()
246+ end
247+ end
248+
249+ function outer_low_occupancy()
250+ @sync for _ in 1:2
251+ # Here, we're explicitly telling the scheduler to assume low occupancy
252+ Dagger.@spawn occupancy=Dict(Dagger.ThreadProc => 0) inner()
253+ end
254+ end
255+ nothing #hide
256+ ```
257+
258+ When running the first outer function N times in parallel, you should see parallelization until all threads are blocked:
259+
260+ ``` @example sleep
261+ fetch.([Dagger.@spawn outer_full_occupancy() for _ in 1:1]); # hide
262+ for N in [1, 2, 4, 8, 16]
263+ @time fetch.([Dagger.@spawn outer_full_occupancy() for _ in 1:N])
264+ end
265+ ```
266+
267+ Whereas running the outer function that communicates a low occupancy (` outer_low_occupancy ` ) should run fully in parallel:
268+
269+ ``` @example sleep
270+ fetch.([Dagger.@spawn outer_low_occupancy() for _ in 1:1]); # hide
271+ for N in [1, 2, 4, 8, 16]
272+ @time fetch.([Dagger.@spawn outer_low_occupancy() for _ in 1:N])
273+ end
274+ ```
0 commit comments