Skip to content

Commit 2fe1568

Browse files
authored
Merge pull request #189 from JuliaMath/sf/preferences
Require Julia 1.6+, use Preferences, eliminate mutable state
2 parents 07eeabb + 2972104 commit 2fe1568

File tree

11 files changed

+146
-149
lines changed

11 files changed

+146
-149
lines changed

.drone.jsonnet

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ local Pipeline(os, arch, version) = {
1010
name: "build",
1111
image: "julia:"+version,
1212
commands: [
13-
"julia --project=. --check-bounds=yes --color=yes -e 'using InteractiveUtils; versioninfo(verbose=true); using Pkg; Pkg.build(); Pkg.test(coverage=true)'"
13+
"julia --project=. --check-bounds=yes --color=yes -e 'using InteractiveUtils; versioninfo(verbose=true); using Pkg; Pkg.test(coverage=true)'"
1414
]
1515
}
1616
],
@@ -20,7 +20,7 @@ local Pipeline(os, arch, version) = {
2020
};
2121

2222
[
23-
Pipeline("linux", "arm", "1.3"),
24-
Pipeline("linux", "arm64", "1.3"),
25-
Pipeline("linux", "arm64", "1.5")
23+
# Commenting this out because we don't have an official armv7l build yet
24+
#Pipeline("linux", "arm", "1.6"),
25+
Pipeline("linux", "arm64", "1.6")
2626
]

.drone.yml

Lines changed: 3 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,16 @@
11
---
22
kind: pipeline
3-
name: linux - arm - Julia 1.3
4-
5-
platform:
6-
os: linux
7-
arch: arm
8-
9-
steps:
10-
- name: build
11-
image: julia:1.3
12-
commands:
13-
- "julia --project=. --check-bounds=yes --color=yes -e 'using InteractiveUtils; versioninfo(verbose=true); using Pkg; Pkg.build(); Pkg.test(coverage=true)'"
14-
15-
trigger:
16-
branch:
17-
- master
18-
19-
---
20-
kind: pipeline
21-
name: linux - arm64 - Julia 1.3
22-
23-
platform:
24-
os: linux
25-
arch: arm64
26-
27-
steps:
28-
- name: build
29-
image: julia:1.3
30-
commands:
31-
- "julia --project=. --check-bounds=yes --color=yes -e 'using InteractiveUtils; versioninfo(verbose=true); using Pkg; Pkg.build(); Pkg.test(coverage=true)'"
32-
33-
trigger:
34-
branch:
35-
- master
36-
37-
---
38-
kind: pipeline
39-
name: linux - arm64 - Julia 1.5
3+
name: linux - arm64 - Julia 1.6
404

415
platform:
426
os: linux
437
arch: arm64
448

459
steps:
4610
- name: build
47-
image: julia:1.5
11+
image: julia:1.6
4812
commands:
49-
- "julia --project=. --check-bounds=yes --color=yes -e 'using InteractiveUtils; versioninfo(verbose=true); using Pkg; Pkg.build(); Pkg.test(coverage=true)'"
13+
- "julia --project=. --check-bounds=yes --color=yes -e 'using InteractiveUtils; versioninfo(verbose=true); using Pkg; Pkg.test(coverage=true)'"
5014

5115
trigger:
5216
branch:

.github/set_ci_preferences.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#!/usr/bin/env julia
2+
3+
open(joinpath(dirname(@__DIR__), "test", "Project.toml"), "a") do io
4+
println(io, """
5+
[preferences.FFTW]
6+
provider = "$(ARGS[1])"
7+
""")
8+
end

.github/workflows/CI.yml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,15 @@ jobs:
1313
name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - provider ${{ matrix.provider }} - ${{ matrix.threads }} thread(s)
1414
runs-on: ${{ matrix.os }}
1515
env:
16-
JULIA_FFTW_PROVIDER: ${{ matrix.provider }}
1716
JULIA_NUM_THREADS: ${{ matrix.threads }}
1817
strategy:
1918
fail-fast: false
2019
matrix:
2120
provider:
22-
- 'FFTW'
23-
- 'MKL'
21+
- 'fftw'
22+
- 'mkl'
2423
version:
25-
- '1.3'
24+
- '1.6'
2625
- 'nightly'
2726
os:
2827
- ubuntu-latest
@@ -40,9 +39,9 @@ jobs:
4039
arch: x86
4140
# 32-bit Linux binary for MKL isn't always available, let's ignore it
4241
- os: ubuntu-latest
43-
provider: 'MKL'
42+
provider: 'mkl'
4443
arch: x86
45-
- provider: 'MKL'
44+
- provider: 'mkl'
4645
threads: '2'
4746

4847
steps:
@@ -61,7 +60,8 @@ jobs:
6160
${{ runner.os }}-test-${{ env.cache-name }}-
6261
${{ runner.os }}-test-
6362
${{ runner.os }}-
64-
- uses: julia-actions/julia-buildpkg@v1
63+
- name: Set Preferences
64+
run: julia --project .github/set_ci_preferences.jl "${{ matrix.provider }}"
6565
- uses: julia-actions/julia-runtest@v1
6666
- uses: julia-actions/julia-processcoverage@v1
6767
- uses: codecov/codecov-action@v1

Project.toml

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,24 @@ name = "FFTW"
22
uuid = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341"
33
version = "1.3.2"
44

5+
[compat]
6+
AbstractFFTs = "1.0"
7+
FFTW_jll = "3.3"
8+
IntelOpenMP_jll = "2018.0.3"
9+
MKL_jll = "2019.0.117, 2020, 2021"
10+
Reexport = "0.2, 1.0"
11+
julia = "1.6"
12+
513
[deps]
614
AbstractFFTs = "621f4979-c628-5d54-868e-fcf4e3e8185c"
715
FFTW_jll = "f5851436-0d7a-5f13-b9de-f02708fd171a"
816
IntelOpenMP_jll = "1d5cc7b8-4909-519e-a0f8-d0f5ad9712d0"
917
Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb"
1018
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
1119
MKL_jll = "856f044c-d86e-5d09-b602-aeab76dc8ba7"
20+
Preferences = "21216c6a-2e73-6563-6e65-726566657250"
1221
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
1322

14-
[compat]
15-
AbstractFFTs = "1.0"
16-
FFTW_jll = "3.3"
17-
IntelOpenMP_jll = "2018.0.3"
18-
MKL_jll = "2019.0.117, 2020, 2021"
19-
Reexport = "0.2, 1.0"
20-
julia = "1.3"
2123

2224
[extras]
2325
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

deps/build.jl

Lines changed: 0 additions & 40 deletions
This file was deleted.

src/FFTW.jl

Lines changed: 12 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module FFTW
22

3-
using LinearAlgebra, Reexport
3+
using LinearAlgebra, Reexport, Preferences
44
import Libdl
55
@reexport using AbstractFFTs
66
using Base.Threads
@@ -15,47 +15,18 @@ import AbstractFFTs: Plan, ScaledPlan,
1515

1616
export dct, idct, dct!, idct!, plan_dct, plan_idct, plan_dct!, plan_idct!
1717

18-
const depsfile = joinpath(dirname(@__DIR__), "deps", "deps.jl")
19-
if isfile(depsfile)
20-
include(depsfile)
21-
else
22-
error("FFTW is not properly installed. Please run Pkg.build(\"FFTW\") ",
23-
"and restart Julia.")
24-
end
25-
26-
# MKL provides its own FFTW
27-
const fftw_vendor = occursin("mkl_rt", libfftw3) ? :mkl : :fftw
18+
include("providers.jl")
2819

29-
# Use Julia partr threading backend if present
30-
@static if fftw_vendor == :fftw
31-
# callback function that FFTW uses to launch `num` parallel
32-
# tasks (FFTW/fftw3#175):
33-
function spawnloop(f::Ptr{Cvoid}, fdata::Ptr{Cvoid}, elsize::Csize_t, num::Cint, callback_data::Ptr{Cvoid})
34-
@sync for i = 0:num-1
35-
Threads.@spawn ccall(f, Ptr{Cvoid}, (Ptr{Cvoid},), fdata + elsize*i)
36-
end
37-
end
38-
end
39-
40-
# If FFTW was built with threads, then they must be initialized before any FFTW planning routine.
41-
# -- This initializes FFTW's threads support (defaulting to 1 thread).
42-
# If this isn't called before the FFTW planner is created, then
43-
# FFTW's threads algorithms won't be registered or used at all.
44-
# (Previously, we called fftw_cleanup, but this invalidated existing
45-
# plans, causing Base Julia issue #19892.)
4620
function __init__()
47-
check_deps()
48-
stat = ccall((:fftw_init_threads, libfftw3), Int32, ())
49-
statf = ccall((:fftwf_init_threads, libfftw3f), Int32, ())
50-
if stat == 0 || statf == 0
51-
error("could not initialize FFTW threads")
21+
# If someone is trying to set the provider via the old environment variable, warn them that they
22+
# should instead use `set_provider!()` instead.
23+
if haskey(ENV, "JULIA_FFTW_PROVIDER")
24+
Base.depwarn("JULIA_FFTW_PROVIDER is deprecated; use FFTW.set_provider!() instead", :JULIA_FFTW_PROVIDER)
5225
end
53-
@static if fftw_vendor == :fftw
54-
if nthreads() > 1
55-
cspawnloop = @cfunction(spawnloop, Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t, Cint, Ptr{Cvoid}))
56-
ccall((:fftw_threads_set_callback, libfftw3), Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}), cspawnloop, C_NULL)
57-
ccall((:fftwf_threads_set_callback, libfftw3f), Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}), cspawnloop, C_NULL)
58-
end
26+
27+
# Hook FFTW threads up to our partr runtime
28+
@static if fftw_provider == "fftw"
29+
fftw_init_threads()
5930
end
6031
end
6132

@@ -91,9 +62,7 @@ end
9162
include("fft.jl")
9263
include("dct.jl")
9364

94-
if Base.VERSION >= v"1.4.2" # avoid potential segfaults, see https://github.com/JuliaLang/julia/pull/35378
95-
include("precompile.jl")
96-
_precompile_()
97-
end
65+
include("precompile.jl")
66+
_precompile_()
9867

9968
end # module

src/fft.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ unsafe_set_timelimit(precision::fftwTypeSingle,seconds) =
203203
# function will be documented in FFTW 3.3.4.
204204

205205

206-
@static if fftw_vendor == :mkl
206+
@static if fftw_provider == "mkl"
207207
alignment_of(A::StridedArray{<:fftwDouble}) =
208208
convert(Int32, convert(Int64, pointer(A)) % 16)
209209
alignment_of(A::StridedArray{<:fftwSingle}) =
@@ -366,7 +366,7 @@ end
366366

367367
# The sprint_plan function was released in FFTW 3.3.4, but MKL versions
368368
# claiming to be FFTW 3.3.4 still don't seem to have this function.
369-
const has_sprint_plan = version >= v"3.3.4" && fftw_vendor == :fftw
369+
const has_sprint_plan = version >= v"3.3.4" && fftw_provider == "fftw"
370370

371371
@static if has_sprint_plan
372372
sprint_plan_(plan::FFTWPlan{<:fftwDouble}) =

src/providers.jl

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
2+
function get_provider()
3+
# Note: we CANNOT do something like have the `default` value be `get(ENV, "JULIA_FFTW_PROVIDER", "fftw")` here.
4+
# This is because the only way the Julia knows that a default has changed is if the values on-disk change; so
5+
# if your "default" value can be changed from the outside, you quickly run into cache invalidation issues.
6+
# So the default here _must_ be a constant.
7+
default_provider = "fftw"
8+
9+
# Load the preference
10+
provider = @load_preference("provider", default_provider)
11+
12+
# Ensure the provider matches one of the ones we support
13+
if provider ("fftw", "mkl")
14+
@error("Invalid provider setting \"$(provider)\"; valid settings include [\"fftw\", \"mkl\"], defaulting to \"fftw\"")
15+
provider = default_provider
16+
end
17+
return provider
18+
end
19+
20+
# Read in preferences, see if any users have requested a particular backend
21+
const fftw_provider = get_provider()
22+
23+
"""
24+
set_provider!(provider; export_prefs::Bool = false)
25+
26+
Convenience wrapper for setting the FFT provider. Valid values include `"fftw"`, `"mkl"`.
27+
Also supports `Preferences` sentinel values `nothing` and `missing`; see the docstring for
28+
`Preferences.set_preferences!()` for more information on what these values mean.
29+
"""
30+
function set_provider!(provider; export_prefs::Bool = false)
31+
if provider !== nothing && provider !== missing && provider ("fftw", "mkl")
32+
throw(ArgumentError("Invalid provider value '$(provider)'"))
33+
end
34+
set_preferences!(@__MODULE__, "provider" => provider; export_prefs)
35+
if provider != fftw_provider
36+
# Re-fetch to get default values in the event that `nothing` or `missing` was passed in.
37+
provider = get_provider()
38+
@info("FFTW provider changed; restart Julia for this change to take effect", provider)
39+
end
40+
end
41+
42+
# If we're using fftw_jll, load it in
43+
@static if fftw_provider == "fftw"
44+
using FFTW_jll
45+
46+
# callback function that FFTW uses to launch `num` parallel
47+
# tasks (FFTW/fftw3#175):
48+
function spawnloop(f::Ptr{Cvoid}, fdata::Ptr{Cvoid}, elsize::Csize_t, num::Cint, callback_data::Ptr{Cvoid})
49+
@sync for i = 0:num-1
50+
Threads.@spawn ccall(f, Ptr{Cvoid}, (Ptr{Cvoid},), fdata + elsize*i)
51+
end
52+
end
53+
54+
# If FFTW was built with threads, then they must be initialized before any FFTW planning routine.
55+
# -- This initializes FFTW's threads support (defaulting to 1 thread).
56+
# If this isn't called before the FFTW planner is created, then
57+
# FFTW's threads algorithms won't be registered or used at all.
58+
# (Previously, we called fftw_cleanup, but this invalidated existing
59+
# plans, causing Base Julia issue #19892.)
60+
function fftw_init_threads()
61+
stat = ccall((:fftw_init_threads, libfftw3), Int32, ())
62+
statf = ccall((:fftwf_init_threads, libfftw3f), Int32, ())
63+
if stat == 0 || statf == 0
64+
error("could not initialize FFTW threads")
65+
end
66+
67+
if nthreads() > 1
68+
cspawnloop = @cfunction(spawnloop, Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t, Cint, Ptr{Cvoid}))
69+
ccall((:fftw_threads_set_callback, libfftw3), Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}), cspawnloop, C_NULL)
70+
ccall((:fftwf_threads_set_callback, libfftw3f), Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}), cspawnloop, C_NULL)
71+
end
72+
end
73+
end
74+
75+
# If we're using MKL, load it in and set library paths appropriately.
76+
@static if fftw_provider == "mkl"
77+
using IntelOpenMP_jll, MKL_jll
78+
const libfftw3 = MKL_jll.libmkl_rt_path
79+
const libfftw3f = libfftw3
80+
end

test/Project.toml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# A bug in Julia 1.6.0's Pkg causes Preferences to be dropped during `Pkg.test()`, so we work around
2+
# it by explicitly creating a `test/Project.toml` which will correctly communicate any preferences
3+
# through to the child Julia process. X-ref: https://github.com/JuliaLang/Pkg.jl/issues/2500
4+
5+
[deps]
6+
AbstractFFTs = "621f4979-c628-5d54-868e-fcf4e3e8185c"
7+
FFTW_jll = "f5851436-0d7a-5f13-b9de-f02708fd171a"
8+
IntelOpenMP_jll = "1d5cc7b8-4909-519e-a0f8-d0f5ad9712d0"
9+
Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb"
10+
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
11+
MKL_jll = "856f044c-d86e-5d09-b602-aeab76dc8ba7"
12+
Preferences = "21216c6a-2e73-6563-6e65-726566657250"
13+
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
14+
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

0 commit comments

Comments
 (0)