Skip to content

Revert back to the standard form for ccall which supports LazyLibrary improvements #318

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
name = "FFTW"
uuid = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341"
version = "1.9.0"
version = "1.10.0"

[deps]
AbstractFFTs = "621f4979-c628-5d54-868e-fcf4e3e8185c"
FFTW_jll = "f5851436-0d7a-5f13-b9de-f02708fd171a"
Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
MKL_jll = "856f044c-d86e-5d09-b602-aeab76dc8ba7"
Preferences = "21216c6a-2e73-6563-6e65-726566657250"
Expand All @@ -13,6 +14,7 @@ Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
[compat]
AbstractFFTs = "1.5"
FFTW_jll = "3.3.9"
Libdl = "1.6"
LinearAlgebra = "<0.0.1, 1"
MKL_jll = "2019.0.117, 2020, 2021, 2022, 2023, 2024, 2025"
Preferences = "1.2"
Expand Down
82 changes: 52 additions & 30 deletions src/FFTW.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,48 +16,70 @@ export dct, idct, dct!, idct!, plan_dct, plan_idct, plan_dct!, plan_idct!

include("providers.jl")

function initialize_library_paths()
# If someone is trying to set the provider via the old environment variable, warn them that they
# should instead use `set_provider!()` instead.
function check_env()
if haskey(ENV, "JULIA_FFTW_PROVIDER")
Base.depwarn("JULIA_FFTW_PROVIDER is deprecated; use FFTW.set_provider!() instead", :JULIA_FFTW_PROVIDER)
end
end

# Hook FFTW threads up to our partr runtime, and re-assign the
# libfftw3{,f} refs at runtime, since we may have relocated and
# changed the path to the library since the last time we precompiled.
if VERSION >= v"1.11.0"
# This can be be deleted once FFTW_jll is upgraded to the real lazy jll code, to get the real benefits of this mess
mutable struct FakeLazyLibrary
reallibrary::Symbol
on_load_callback
@atomic h::Ptr{Cvoid}
end
import Libdl: LazyLibrary, dlopen
function dlopen(lib::FakeLazyLibrary)
h = @atomic :monotonic lib.h
h != C_NULL && return h
@lock fftwlock begin
h = @atomic :monotonic lib.h
h != C_NULL && return h
h = dlopen(getglobal(FFTW, lib.reallibrary))
lib.on_load_callback()
@atomic :release lib.h = h
end
return h
end
function fftw_init_check()
check_env()
@static if fftw_provider == "fftw"
libfftw3_path[] = FFTW_jll.libfftw3_path
libfftw3f_path[] = FFTW_jll.libfftw3f_path
fftw_init_threads()
end
@static if fftw_provider == "mkl"
libfftw3_path[] = MKL_jll.libmkl_rt_path
libfftw3f_path[] = MKL_jll.libmkl_rt_path
end
return nothing
end

if VERSION >= v"1.12.0-beta1.29"
const initialize_library_paths_once = OncePerProcess{Nothing}() do
initialize_library_paths()
return
end
function libfftw3()
initialize_library_paths_once()
return libfftw3_path[]
end
function libfftw3f()
initialize_library_paths_once()
return libfftw3f_path[]
end
@static if fftw_provider == "fftw"
import FFTW_jll: libfftw3 as libfftw3_no_init,
libfftw3f as libfftw3f_no_init
elseif fftw_provider == "mkl"
import MKL_jll: libmkl_rt as libfftw3_no_init,
libmkl_rt as libfftw3f_no_init
end
const libfftw3 = FakeLazyLibrary(:libfftw3_no_init, fftw_init_check, C_NULL)
const libfftw3f = FakeLazyLibrary(:libfftw3f_no_init, fftw_init_check, C_NULL)

else
function __init__()
initialize_library_paths()
function __init__()
# If someone is trying to set the provider via the old environment variable, warn them that they
# should instead use `set_provider!()` instead.
check_env()

global libfftw3
global libfftw3f
# Hook FFTW threads up to our partr runtime
@static if fftw_provider == "fftw"
libfftw3 = FFTW_jll.libfftw3_path
libfftw3f = FFTW_jll.libfftw3f_path
fftw_init_threads()
end
@static if fftw_provider == "mkl"
libfftw3 = MKL_jll.libmkl_rt_path
libfftw3f = MKL_jll.libmkl_rt_path
end
libfftw3() = libfftw3_path[]
libfftw3f() = libfftw3f_path[]
end
end


# most FFTW calls other than fftw_execute should be protected by a lock to be thread-safe
const fftwlock = ReentrantLock()
Expand Down
83 changes: 43 additions & 40 deletions src/fft.jl
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,11 @@ function plan_r2r end

## FFT: Implement fft by calling fftw.

const version = VersionNumber(split(unsafe_string(cglobal(
(:fftw_version,libfftw3()), UInt8)), ['-', ' '])[2])
# TODO: this is dangerous since it captures runtime data from the compile machine
# n.b. write this as a function to avoid Julia bugs with the runtime cglobal implementation
get_version() = VersionNumber(split(unsafe_string(cglobal(
(:fftw_version,libfftw3_no_init), UInt8)), ['-', ' '])[2])
const version = get_version()

## Direction of FFT

Expand Down Expand Up @@ -141,32 +144,32 @@ alignment_of(A::FakeArray) = Int32(0)
@exclusive function export_wisdom(fname::AbstractString)
f = ccall(:fopen, Ptr{Cvoid}, (Cstring,Cstring), fname, :w)
systemerror("could not open wisdom file $fname for writing", f == C_NULL)
ccall((:fftw_export_wisdom_to_file,libfftw3()), Cvoid, (Ptr{Cvoid},), f)
ccall((:fftw_export_wisdom_to_file,libfftw3), Cvoid, (Ptr{Cvoid},), f)
ccall(:fputs, Int32, (Ptr{UInt8},Ptr{Cvoid}), " "^256, f) # no NUL, hence no Cstring
ccall((:fftwf_export_wisdom_to_file,libfftw3f()), Cvoid, (Ptr{Cvoid},), f)
ccall((:fftwf_export_wisdom_to_file,libfftw3f), Cvoid, (Ptr{Cvoid},), f)
ccall(:fclose, Cvoid, (Ptr{Cvoid},), f)
end

@exclusive function import_wisdom(fname::AbstractString)
f = ccall(:fopen, Ptr{Cvoid}, (Cstring,Cstring), fname, :r)
systemerror("could not open wisdom file $fname for reading", f == C_NULL)
if ccall((:fftw_import_wisdom_from_file,libfftw3()),Int32,(Ptr{Cvoid},),f)==0||
ccall((:fftwf_import_wisdom_from_file,libfftw3f()),Int32,(Ptr{Cvoid},),f)==0
if ccall((:fftw_import_wisdom_from_file,libfftw3),Int32,(Ptr{Cvoid},),f)==0||
ccall((:fftwf_import_wisdom_from_file,libfftw3f),Int32,(Ptr{Cvoid},),f)==0
error("failed to import wisdom from $fname")
end
ccall(:fclose, Cvoid, (Ptr{Cvoid},), f)
end

@exclusive function import_system_wisdom()
if ccall((:fftw_import_system_wisdom,libfftw3()), Int32, ()) == 0 ||
ccall((:fftwf_import_system_wisdom,libfftw3f()), Int32, ()) == 0
if ccall((:fftw_import_system_wisdom,libfftw3), Int32, ()) == 0 ||
ccall((:fftwf_import_system_wisdom,libfftw3f), Int32, ()) == 0
error("failed to import system wisdom")
end
end

@exclusive function forget_wisdom()
ccall((:fftw_forget_wisdom,libfftw3()), Cvoid, ())
ccall((:fftwf_forget_wisdom,libfftw3f()), Cvoid, ())
ccall((:fftw_forget_wisdom,libfftw3), Cvoid, ())
ccall((:fftwf_forget_wisdom,libfftw3f), Cvoid, ())
end

# Threads
Expand All @@ -176,15 +179,15 @@ function _set_num_threads(num_threads::Integer)
@static if fftw_provider == "mkl"
_last_num_threads[] = num_threads
end
ccall((:fftw_plan_with_nthreads,libfftw3()), Cvoid, (Int32,), num_threads)
ccall((:fftwf_plan_with_nthreads,libfftw3f()), Cvoid, (Int32,), num_threads)
ccall((:fftw_plan_with_nthreads,libfftw3), Cvoid, (Int32,), num_threads)
ccall((:fftwf_plan_with_nthreads,libfftw3f), Cvoid, (Int32,), num_threads)
end

@exclusive set_num_threads(num_threads::Integer) = _set_num_threads(num_threads)

function get_num_threads()
@static if fftw_provider == "fftw"
ccall((:fftw_planner_nthreads,libfftw3()), Cint, ())
ccall((:fftw_planner_nthreads,libfftw3), Cint, ())
else
_last_num_threads[]
end
Expand All @@ -211,9 +214,9 @@ const NO_TIMELIMIT = -1.0 # from fftw3.h

# only call these when fftwlock is held:
unsafe_set_timelimit(precision::fftwTypeDouble,seconds) =
ccall((:fftw_set_timelimit,libfftw3()), Cvoid, (Float64,), seconds)
ccall((:fftw_set_timelimit,libfftw3), Cvoid, (Float64,), seconds)
unsafe_set_timelimit(precision::fftwTypeSingle,seconds) =
ccall((:fftwf_set_timelimit,libfftw3f()), Cvoid, (Float64,), seconds)
ccall((:fftwf_set_timelimit,libfftw3f), Cvoid, (Float64,), seconds)
@exclusive set_timelimit(precision, seconds) = unsafe_set_timelimit(precision, seconds)

# Array alignment mod 16:
Expand All @@ -234,9 +237,9 @@ unsafe_set_timelimit(precision::fftwTypeSingle,seconds) =
convert(Int32, convert(Int64, pointer(A)) % 16)
else
alignment_of(A::StridedArray{T}) where {T<:fftwDouble} =
ccall((:fftw_alignment_of, libfftw3()), Int32, (Ptr{T},), A)
ccall((:fftw_alignment_of, libfftw3), Int32, (Ptr{T},), A)
alignment_of(A::StridedArray{T}) where {T<:fftwSingle} =
ccall((:fftwf_alignment_of, libfftw3f()), Int32, (Ptr{T},), A)
ccall((:fftwf_alignment_of, libfftw3f), Int32, (Ptr{T},), A)
end

# FFTWPlan (low-level)
Expand Down Expand Up @@ -320,9 +323,9 @@ unsafe_convert(::Type{PlanPtr}, p::FFTWPlan) = p.plan

# these functions should only be called while the fftwlock is held
unsafe_destroy_plan(@nospecialize(plan::FFTWPlan{<:fftwDouble})) =
ccall((:fftw_destroy_plan,libfftw3()), Cvoid, (PlanPtr,), plan)
ccall((:fftw_destroy_plan,libfftw3), Cvoid, (PlanPtr,), plan)
unsafe_destroy_plan(@nospecialize(plan::FFTWPlan{<:fftwSingle})) =
ccall((:fftwf_destroy_plan,libfftw3f()), Cvoid, (PlanPtr,), plan)
ccall((:fftwf_destroy_plan,libfftw3f), Cvoid, (PlanPtr,), plan)

const deferred_destroy_lock = ReentrantLock() # lock protecting the deferred_destroy_plans list
const deferred_destroy_plans = FFTWPlan[]
Expand Down Expand Up @@ -388,19 +391,19 @@ end
#################################################################################################

cost(plan::FFTWPlan{<:fftwDouble}) =
ccall((:fftw_cost,libfftw3()), Float64, (PlanPtr,), plan)
ccall((:fftw_cost,libfftw3), Float64, (PlanPtr,), plan)
cost(plan::FFTWPlan{<:fftwSingle}) =
ccall((:fftwf_cost,libfftw3f()), Float64, (PlanPtr,), plan)
ccall((:fftwf_cost,libfftw3f), Float64, (PlanPtr,), plan)

@exclusive function arithmetic_ops(plan::FFTWPlan{<:fftwDouble})
add, mul, fma = Ref(0.0), Ref(0.0), Ref(0.0)
ccall((:fftw_flops,libfftw3()), Cvoid,
ccall((:fftw_flops,libfftw3), Cvoid,
(PlanPtr,Ref{Float64},Ref{Float64},Ref{Float64}), plan, add, mul, fma)
return (round(Int64, add[]), round(Int64, mul[]), round(Int64, fma[]))
end
@exclusive function arithmetic_ops(plan::FFTWPlan{<:fftwSingle})
add, mul, fma = Ref(0.0), Ref(0.0), Ref(0.0)
ccall((:fftwf_flops,libfftw3f()), Cvoid,
ccall((:fftwf_flops,libfftw3f), Cvoid,
(PlanPtr,Ref{Float64},Ref{Float64},Ref{Float64}), plan, add, mul, fma)
return (round(Int64, add[]), round(Int64, mul[]), round(Int64, fma[]))
end
Expand Down Expand Up @@ -431,9 +434,9 @@ const has_sprint_plan = version >= v"3.3.4" && fftw_provider == "fftw"

@static if has_sprint_plan
sprint_plan_(plan::FFTWPlan{<:fftwDouble}) =
ccall((:fftw_sprint_plan,libfftw3()), Ptr{UInt8}, (PlanPtr,), plan)
ccall((:fftw_sprint_plan,libfftw3), Ptr{UInt8}, (PlanPtr,), plan)
sprint_plan_(plan::FFTWPlan{<:fftwSingle}) =
ccall((:fftwf_sprint_plan,libfftw3f()), Ptr{UInt8}, (PlanPtr,), plan)
ccall((:fftwf_sprint_plan,libfftw3f), Ptr{UInt8}, (PlanPtr,), plan)
function sprint_plan(plan::FFTWPlan)
p = sprint_plan_(plan)
str = unsafe_string(p)
Expand Down Expand Up @@ -515,49 +518,49 @@ _colmajorstrides(p) = ()
# Execute

unsafe_execute!(plan::FFTWPlan{<:fftwDouble}) =
ccall((:fftw_execute,libfftw3()), Cvoid, (PlanPtr,), plan)
ccall((:fftw_execute,libfftw3), Cvoid, (PlanPtr,), plan)

unsafe_execute!(plan::FFTWPlan{<:fftwSingle}) =
ccall((:fftwf_execute,libfftw3f()), Cvoid, (PlanPtr,), plan)
ccall((:fftwf_execute,libfftw3f), Cvoid, (PlanPtr,), plan)

unsafe_execute!(plan::cFFTWPlan{T},
X::StridedArray{T}, Y::StridedArray{T}) where {T<:fftwDouble} =
ccall((:fftw_execute_dft,libfftw3()), Cvoid,
ccall((:fftw_execute_dft,libfftw3), Cvoid,
(PlanPtr,Ptr{T},Ptr{T}), plan, X, Y)

unsafe_execute!(plan::cFFTWPlan{T},
X::StridedArray{T}, Y::StridedArray{T}) where {T<:fftwSingle} =
ccall((:fftwf_execute_dft,libfftw3f()), Cvoid,
ccall((:fftwf_execute_dft,libfftw3f), Cvoid,
(PlanPtr,Ptr{T},Ptr{T}), plan, X, Y)

unsafe_execute!(plan::rFFTWPlan{Float64,FORWARD},
X::StridedArray{Float64}, Y::StridedArray{Complex{Float64}}) =
ccall((:fftw_execute_dft_r2c,libfftw3()), Cvoid,
ccall((:fftw_execute_dft_r2c,libfftw3), Cvoid,
(PlanPtr,Ptr{Float64},Ptr{Complex{Float64}}), plan, X, Y)

unsafe_execute!(plan::rFFTWPlan{Float32,FORWARD},
X::StridedArray{Float32}, Y::StridedArray{Complex{Float32}}) =
ccall((:fftwf_execute_dft_r2c,libfftw3f()), Cvoid,
ccall((:fftwf_execute_dft_r2c,libfftw3f), Cvoid,
(PlanPtr,Ptr{Float32},Ptr{Complex{Float32}}), plan, X, Y)

unsafe_execute!(plan::rFFTWPlan{Complex{Float64},BACKWARD},
X::StridedArray{Complex{Float64}}, Y::StridedArray{Float64}) =
ccall((:fftw_execute_dft_c2r,libfftw3()), Cvoid,
ccall((:fftw_execute_dft_c2r,libfftw3), Cvoid,
(PlanPtr,Ptr{Complex{Float64}},Ptr{Float64}), plan, X, Y)

unsafe_execute!(plan::rFFTWPlan{Complex{Float32},BACKWARD},
X::StridedArray{Complex{Float32}}, Y::StridedArray{Float32}) =
ccall((:fftwf_execute_dft_c2r,libfftw3f()), Cvoid,
ccall((:fftwf_execute_dft_c2r,libfftw3f), Cvoid,
(PlanPtr,Ptr{Complex{Float32}},Ptr{Float32}), plan, X, Y)

unsafe_execute!(plan::r2rFFTWPlan{T},
X::StridedArray{T}, Y::StridedArray{T}) where {T<:fftwDouble} =
ccall((:fftw_execute_r2r,libfftw3()), Cvoid,
ccall((:fftw_execute_r2r,libfftw3), Cvoid,
(PlanPtr,Ptr{T},Ptr{T}), plan, X, Y)

unsafe_execute!(plan::r2rFFTWPlan{T},
X::StridedArray{T}, Y::StridedArray{T}) where {T<:fftwSingle} =
ccall((:fftwf_execute_r2r,libfftw3f()), Cvoid,
ccall((:fftwf_execute_r2r,libfftw3f), Cvoid,
(PlanPtr,Ptr{T},Ptr{T}), plan, X, Y)

# NOTE ON GC (garbage collection):
Expand Down Expand Up @@ -654,7 +657,7 @@ for (Tr,Tc,fftw,lib) in ((:Float64,:(Complex{Float64}),"fftw",:libfftw3),
unsafe_set_timelimit($Tr, timelimit)
R = isa(region, Tuple) ? region : copy(region)
dims, howmany = dims_howmany(X, Y, size(X), R)
plan = ccall(($(string(fftw,"_plan_guru64_dft")),$lib()),
plan = ccall(($(string(fftw,"_plan_guru64_dft")),$lib),
PlanPtr,
(Int32, Ptr{Int}, Int32, Ptr{Int},
Ptr{$Tc}, Ptr{$Tc}, Int32, UInt32),
Expand All @@ -674,7 +677,7 @@ for (Tr,Tc,fftw,lib) in ((:Float64,:(Complex{Float64}),"fftw",:libfftw3),
regionshft = _circshiftmin1(region) # FFTW halves last dim
unsafe_set_timelimit($Tr, timelimit)
dims, howmany = dims_howmany(X, Y, size(X), regionshft)
plan = ccall(($(string(fftw,"_plan_guru64_dft_r2c")),$lib()),
plan = ccall(($(string(fftw,"_plan_guru64_dft_r2c")),$lib),
PlanPtr,
(Int32, Ptr{Int}, Int32, Ptr{Int},
Ptr{$Tr}, Ptr{$Tc}, UInt32),
Expand All @@ -694,7 +697,7 @@ for (Tr,Tc,fftw,lib) in ((:Float64,:(Complex{Float64}),"fftw",:libfftw3),
regionshft = _circshiftmin1(region) # FFTW halves last dim
unsafe_set_timelimit($Tr, timelimit)
dims, howmany = dims_howmany(X, Y, size(Y), regionshft)
plan = ccall(($(string(fftw,"_plan_guru64_dft_c2r")),$lib()),
plan = ccall(($(string(fftw,"_plan_guru64_dft_c2r")),$lib),
PlanPtr,
(Int32, Ptr{Int}, Int32, Ptr{Int},
Ptr{$Tc}, Ptr{$Tr}, UInt32),
Expand All @@ -716,7 +719,7 @@ for (Tr,Tc,fftw,lib) in ((:Float64,:(Complex{Float64}),"fftw",:libfftw3),
knd = fix_kinds(region, kinds)
unsafe_set_timelimit($Tr, timelimit)
dims, howmany = dims_howmany(X, Y, size(X), region)
plan = ccall(($(string(fftw,"_plan_guru64_r2r")),$lib()),
plan = ccall(($(string(fftw,"_plan_guru64_r2r")),$lib),
PlanPtr,
(Int32, Ptr{Int}, Int32, Ptr{Int},
Ptr{$Tr}, Ptr{$Tr}, Ptr{Int32}, UInt32),
Expand Down Expand Up @@ -744,7 +747,7 @@ for (Tr,Tc,fftw,lib) in ((:Float64,:(Complex{Float64}),"fftw",:libfftw3),
howmany[2:3, :] .*= 2
end
howmany = [howmany [2,1,1]] # append loop over real/imag parts
plan = ccall(($(string(fftw,"_plan_guru64_r2r")),$lib()),
plan = ccall(($(string(fftw,"_plan_guru64_r2r")),$lib),
PlanPtr,
(Int32, Ptr{Int}, Int32, Ptr{Int},
Ptr{$Tc}, Ptr{$Tc}, Ptr{Int32}, UInt32),
Expand Down
Loading
Loading