From a67ea5a10c15816c2284efb3a8cc3cb637525d03 Mon Sep 17 00:00:00 2001 From: Michael Abbott Date: Wed, 1 Jul 2020 15:06:20 +0200 Subject: [PATCH 1/5] add get_num_threads --- Project.toml | 2 +- README.md | 3 ++ src/Compat.jl | 99 +++++++++++++++++++++++++++++++++++++++++++++++- test/runtests.jl | 31 +++++++++++++++ 4 files changed, 133 insertions(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index 3884a49e2..122e90554 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "Compat" uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "3.12.0" +version = "3.13.0" [deps] Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" diff --git a/README.md b/README.md index 3faaee579..d1d7b4fb3 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,8 @@ changes in `julia`. ## Supported features +* `Compat.get_num_threads()` adds the functionality of `LinearAlgebra.BLAS.get_num_threads()` ([#36360]). (since Compat 3.13.0) + * `@inferred [AllowedType] f(x)` is defined ([#27516]). (since Compat 3.12.0) * Search a character in a string with `findfirst`, `findnext`, `findlast` and `findprev` ([#31664]). (since Compat 3.12.0) @@ -171,3 +173,4 @@ Note that you should specify the correct minimum version for `Compat` in the [#34251]: https://github.com/JuliaLang/julia/pull/34251 [#35577]: https://github.com/JuliaLang/julia/pull/35577 [#27516]: https://github.com/JuliaLang/julia/pull/27516 +[#36360]: https://github.com/JuliaLang/julia/pull/36360 diff --git a/src/Compat.jl b/src/Compat.jl index f379c44e3..d7b5892cb 100644 --- a/src/Compat.jl +++ b/src/Compat.jl @@ -1,7 +1,7 @@ module Compat import LinearAlgebra -using LinearAlgebra: Adjoint, Diagonal, Transpose, UniformScaling, RealHermSymComplexHerm +using LinearAlgebra: Adjoint, Diagonal, Transpose, UniformScaling, RealHermSymComplexHerm, BLAS include("compatmacro.jl") @@ -464,6 +464,103 @@ if VERSION < v"1.2.0-DEV.77" #export @inferred end +# https://github.com/JuliaLang/julia/pull/36360 +if VERSION < v"1.6.0-DEV.322" # b8110f8d1ec6349bee77efb5022621fdf50bd4a5 + + function guess_vendor() + # like determine_vendor, but guesses blas in some cases + # where determine_vendor returns :unknown + ret = BLAS.vendor() + if Sys.isapple() && (ret == :unknown) + ret = :osxblas + end + ret + end + + """ + Compat.set_num_threads(n) + + Set the number of threads the BLAS library should use. + + Also accepts `nothing`, in which case julia tries to guess the default number of threads. + Passing `nothing` is discouraged and mainly exists because, + on exotic variants of BLAS, `nothing` may be returned by `get_num_threads()`. + Thus the following pattern may fail to set the number of threads, but will not error: + ```julia + old = get_num_threads() + set_num_threads(1) + @threads for i in 1:10 + # single-threaded BLAS calls + end + set_num_threads(old) + ``` + """ + set_num_threads(n)::Nothing = _set_num_threads(n) + + function _set_num_threads(n::Integer; _blas = guess_vendor()) + if _blas === :openblas || _blas == :openblas64 + return ccall((BLAS.@blasfunc(openblas_set_num_threads), BLAS.libblas), Cvoid, (Cint,), n) + elseif _blas === :mkl + # MKL may let us set the number of threads in several ways + return ccall((:MKL_Set_Num_Threads, BLAS.libblas), Cvoid, (Cint,), n) + elseif _blas === :osxblas + # OSX BLAS looks at an environment variable + ENV["VECLIB_MAXIMUM_THREADS"] = n + else + @assert _blas === :unknown + @warn "Failed to set number of BLAS threads." maxlog=1 + end + return nothing + end + _tryparse_env_int(key) = tryparse(Int, get(ENV, key, "")) + + function _set_num_threads(::Nothing; _blas = guess_vendor()) + n = something( + _tryparse_env_int("OPENBLAS_NUM_THREADS"), + _tryparse_env_int("OMP_NUM_THREADS"), + max(1, Sys.CPU_THREADS ÷ 2), + ) + _set_num_threads(n; _blas) + end + + """ + Compat.get_num_threads() + + Get the number of threads the BLAS library is using. + + On exotic variants of `BLAS` this function can fail, + which is indicated by returning `nothing`. + + In Julia 1.6 this is `LinearAlgebra.BLAS.get_num_threads()` + """ + get_num_threads(;_blas=guess_vendor())::Union{Int, Nothing} = _get_num_threads() + + function _get_num_threads(; _blas = guess_vendor())::Union{Int, Nothing} + if _blas === :openblas || _blas === :openblas64 + return Int(ccall((BLAS.@blasfunc(openblas_get_num_threads), BLAS.libblas), Cint, ())) + elseif _blas === :mkl + return Int(ccall((:mkl_get_max_threads, BLAS.libblas), Cint, ())) + elseif _blas === :osxblas + key = "VECLIB_MAXIMUM_THREADS" + nt = _tryparse_env_int(key) + if nt === nothing + @warn "Failed to read environment variable $key" maxlog=1 + else + return nt + end + else + @assert _blas === :unknown + end + @warn "Could not get number of BLAS threads. Returning `nothing` instead." maxlog=1 + return nothing + end + +else + # Ensure that these can still be accessed as Compat.get_num_threads() etc: + using LinearAlgebra + import BLAS: set_num_threads, get_num_threads +end + include("deprecated.jl") end # module Compat diff --git a/test/runtests.jl b/test/runtests.jl index fd08efe16..25a43d8e5 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -464,4 +464,35 @@ end @inferred Missing g(10) end +# https://github.com/JuliaLang/julia/pull/36360 +@testset "get_set_num_threads" begin + default = Compat.get_num_threads() + @test default isa Int # seems dodgy, could be nothing! + @test default > 0 + Compat.set_num_threads(1) + @test Compat.get_num_threads() === 1 + Compat.set_num_threads(default) + @test Compat.get_num_threads() === default + + if VERSION < v"1.6.0-DEV.322" + # These tests from PR rely on internal functions which would be BLAS. not Compat. + @test_logs (:warn,) match_mode=:any Compat._set_num_threads(1, _blas=:unknown) + if Compat.guess_vendor() !== :osxblas + # test osxblas which is not covered by CI + withenv("VECLIB_MAXIMUM_THREADS" => nothing) do + @test @test_logs( + (:warn,), + (:warn,), + match_mode=:any, + Compat._get_num_threads(_blas=:osxblas), + ) === nothing + @test_logs Compat._set_num_threads(1, _blas=:osxblas) + @test @test_logs(Compat._get_num_threads(_blas=:osxblas)) === 1 + @test_logs Compat._set_num_threads(2, _blas=:osxblas) + @test @test_logs(Compat._get_num_threads(_blas=:osxblas)) === 2 + end + end + end +end + nothing From ab887908e226a11f76dc4aae171e7f6d93c4db6b Mon Sep 17 00:00:00 2001 From: Michael Abbott Date: Wed, 1 Jul 2020 15:35:57 +0200 Subject: [PATCH 2/5] keyword --- src/Compat.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compat.jl b/src/Compat.jl index d7b5892cb..d39e118a4 100644 --- a/src/Compat.jl +++ b/src/Compat.jl @@ -520,7 +520,7 @@ if VERSION < v"1.6.0-DEV.322" # b8110f8d1ec6349bee77efb5022621fdf50bd4a5 _tryparse_env_int("OMP_NUM_THREADS"), max(1, Sys.CPU_THREADS ÷ 2), ) - _set_num_threads(n; _blas) + _set_num_threads(n; _blas = _blas) end """ From e9e2c170fdd2c841853ed4eeeac478e30f692690 Mon Sep 17 00:00:00 2001 From: Michael Abbott Date: Wed, 1 Jul 2020 15:39:08 +0200 Subject: [PATCH 3/5] nightly --- src/Compat.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Compat.jl b/src/Compat.jl index d39e118a4..d529d90fc 100644 --- a/src/Compat.jl +++ b/src/Compat.jl @@ -557,8 +557,7 @@ if VERSION < v"1.6.0-DEV.322" # b8110f8d1ec6349bee77efb5022621fdf50bd4a5 else # Ensure that these can still be accessed as Compat.get_num_threads() etc: - using LinearAlgebra - import BLAS: set_num_threads, get_num_threads + import LinearAlgebra.BLAS: set_num_threads, get_num_threads end include("deprecated.jl") From 69238d353408f5bc3ecb9e86fd2051ccb4c2c04d Mon Sep 17 00:00:00 2001 From: Michael Abbott Date: Thu, 2 Jul 2020 12:08:33 +0200 Subject: [PATCH 4/5] tweaks --- README.md | 2 +- test/runtests.jl | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d1d7b4fb3..b5ba05df5 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ changes in `julia`. ## Supported features -* `Compat.get_num_threads()` adds the functionality of `LinearAlgebra.BLAS.get_num_threads()` ([#36360]). (since Compat 3.13.0) +* `Compat.get_num_threads()` adds the functionality of `LinearAlgebra.BLAS.get_num_threads()`, and has matching `Compat.set_num_threads(n)` ([#36360]). (since Compat 3.13.0) * `@inferred [AllowedType] f(x)` is defined ([#27516]). (since Compat 3.12.0) diff --git a/test/runtests.jl b/test/runtests.jl index 25a43d8e5..08afc0178 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -474,6 +474,10 @@ end Compat.set_num_threads(default) @test Compat.get_num_threads() === default + # Run the ::Nothing method, to check no error: + Compat.set_num_threads(nothing) + Compat.set_num_threads(default) + if VERSION < v"1.6.0-DEV.322" # These tests from PR rely on internal functions which would be BLAS. not Compat. @test_logs (:warn,) match_mode=:any Compat._set_num_threads(1, _blas=:unknown) From ca0c45ddcc52cb9198d61bb5fd3d6b5884f9f145 Mon Sep 17 00:00:00 2001 From: Michael Abbott <32575566+mcabbott@users.noreply.github.com> Date: Thu, 2 Jul 2020 13:36:27 +0200 Subject: [PATCH 5/5] Apply suggestions from code review Co-authored-by: Martin Holters --- src/Compat.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Compat.jl b/src/Compat.jl index d529d90fc..f575376e1 100644 --- a/src/Compat.jl +++ b/src/Compat.jl @@ -471,7 +471,7 @@ if VERSION < v"1.6.0-DEV.322" # b8110f8d1ec6349bee77efb5022621fdf50bd4a5 # like determine_vendor, but guesses blas in some cases # where determine_vendor returns :unknown ret = BLAS.vendor() - if Sys.isapple() && (ret == :unknown) + if Base.Sys.isapple() && (ret == :unknown) ret = :osxblas end ret @@ -518,7 +518,7 @@ if VERSION < v"1.6.0-DEV.322" # b8110f8d1ec6349bee77efb5022621fdf50bd4a5 n = something( _tryparse_env_int("OPENBLAS_NUM_THREADS"), _tryparse_env_int("OMP_NUM_THREADS"), - max(1, Sys.CPU_THREADS ÷ 2), + max(1, Base.Sys.CPU_THREADS ÷ 2), ) _set_num_threads(n; _blas = _blas) end