From 53dfeb5a1f3de9aeaec1292ae5d650cb74a15c4e Mon Sep 17 00:00:00 2001 From: Ian Date: Fri, 11 Feb 2022 22:24:05 -0500 Subject: [PATCH 1/3] make `:dynamic` the default schedule for `@threads` --- NEWS.md | 6 +++--- base/threadingconstructs.jl | 33 +++++++++++---------------------- 2 files changed, 14 insertions(+), 25 deletions(-) diff --git a/NEWS.md b/NEWS.md index a1167283d0b63..426a5b516d8c2 100644 --- a/NEWS.md +++ b/NEWS.md @@ -73,9 +73,9 @@ Command-line option changes Multi-threading changes ----------------------- -* A new `:dynamic` schedule option for `Threads.@threads` which is similar to the default behavior except iterations - will be scheduled dynamically to available worker threads rather than pinned to each thread. This option is more - composable with (possibly nested) `@spawn` and `@threads` loops ([#43919]) +* `Threads.@threads` now defaults to a new `:dynamic` schedule option which is similar to the previous behavior except + that iterations will be scheduled dynamically to available worker threads rather than pinned to each thread. This + behavior is more composable with (possibly nested) `@spawn` and `@threads` loops ([#43919]) Build system changes -------------------- diff --git a/base/threadingconstructs.jl b/base/threadingconstructs.jl index 123fa62b12082..74234c48bf242 100644 --- a/base/threadingconstructs.jl +++ b/base/threadingconstructs.jl @@ -85,16 +85,11 @@ function _threadsfor(iter, lbody, schedule) end end end - if $(schedule === :dynamic) + if $(schedule === :dynamic || schedule === :default) threading_run(threadsfor_fun, false) - elseif ccall(:jl_in_threaded_region, Cint, ()) != 0 - $(if schedule === :static - :(error("`@threads :static` cannot be used concurrently or nested")) - else - # only use threads when called from outside @threads - :(threadsfor_fun(onethread = true)) - end) - else + elseif ccall(:jl_in_threaded_region, Cint, ()) != 0 # :static + error("`@threads :static` cannot be used concurrently or nested") + else # :static threading_run(threadsfor_fun, true) end nothing @@ -126,21 +121,21 @@ For example, the above conditions imply that: - Communicating between iterations using blocking primitives like `Channel`s is incorrect. - Write only to locations not shared across iterations (unless a lock or atomic operation is used). - Schedule options are: - `:static` creates one task per thread and divides the iterations equally among them, assigning each task specifically to each thread. Specifying `:static` is an error if used from inside another `@threads` loop or from a thread other than 1. -- `:dynamic` will schedule iterations dynamically to available worker threads, +- `:dynamic` (default) will schedule iterations dynamically to available worker threads, assuming that the workload for each iteration is uniform. -Without the scheduler argument, the exact scheduling is unspecified; i.e. it may be -different across Julia releases. Currently, the behavior is dependent on the calling thread. -The default is `:static` when called from thread 1. The loop will be executed without threading -when called from other threads. +Without the scheduler argument, the exact scheduling is unspecified and varies across Julia releases. -The default schedule (used when no `schedule` argument is present) is subject to change. +!!! compat "Julia 1.5" + The `schedule` argument is available as of Julia 1.5. + +!!! compat "Julia 1.8" + The `:dynamic` option for the `schedule` argument is available and the default as of Julia 1.8. For example, an illustration of the different scheduling strategies where `busywait` is a non-yielding timed loop that runs for a number of seconds. @@ -172,12 +167,6 @@ julia> @time begin The `:dynamic` example takes 2 seconds since one of the non-occupied threads is able to run two of the 1-second iterations to complete the for loop. -!!! compat "Julia 1.5" - The `schedule` argument is available as of Julia 1.5. - -!!! compat "Julia 1.8" - The `:dynamic` option for the `schedule` argument is available as of Julia 1.8. - See also: [`@spawn`](@ref Threads.@spawn), [`nthreads()`](@ref Threads.nthreads), [`threadid()`](@ref Threads.threadid), `pmap` in [`Distributed`](@ref man-distributed), `BLAS.set_num_threads` in [`LinearAlgebra`](@ref man-linalg). From 0cf2c172c0fa16e79fd5e757214e0a9de9ad4f7d Mon Sep 17 00:00:00 2001 From: Ian Date: Fri, 11 Feb 2022 22:29:34 -0500 Subject: [PATCH 2/3] make some `@threads` tests `:static` that are dependent on threadid --- test/threads_exec.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/threads_exec.jl b/test/threads_exec.jl index 7b03f48f5eec6..1b146f48e8c57 100644 --- a/test/threads_exec.jl +++ b/test/threads_exec.jl @@ -503,7 +503,7 @@ function test_thread_cfunction() @test cfs[2] == cf(fs[2]) @test length(unique(cfs)) == 1000 ok = zeros(Int, nthreads()) - @threads for i in 1:10000 + @threads :static for i in 1:10000 i = mod1(i, 1000) fi = fs[i] cfi = cf(fi) @@ -705,9 +705,9 @@ let ch = Channel{Char}(0), t @test String(collect(ch)) == "hello" end -# errors inside @threads +# errors inside @threads :static function _atthreads_with_error(a, err) - Threads.@threads for i in eachindex(a) + Threads.@threads :static for i in eachindex(a) if err error("failed") end From 4f479046d16f1c716699a0972748137676ad671e Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Sat, 12 Feb 2022 12:36:29 +0900 Subject: [PATCH 3/3] Include a link to this PR --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 426a5b516d8c2..fd217eb7f954f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -75,7 +75,7 @@ Multi-threading changes * `Threads.@threads` now defaults to a new `:dynamic` schedule option which is similar to the previous behavior except that iterations will be scheduled dynamically to available worker threads rather than pinned to each thread. This - behavior is more composable with (possibly nested) `@spawn` and `@threads` loops ([#43919]) + behavior is more composable with (possibly nested) `@spawn` and `@threads` loops ([#43919], [#44136]) Build system changes --------------------