Skip to content

Commit 378ed8a

Browse files
authored
updated fix for #19892 (FFTW threads initialization) (#21169)
* updated fix for #19892; initialize FFTW threads the first time the planner is called (#21127 incorrectly prevented threads from being used at all) * add test for #21163
1 parent 407995a commit 378ed8a

File tree

2 files changed

+38
-16
lines changed

2 files changed

+38
-16
lines changed

base/fft/FFTW.jl

+32-14
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,27 @@ alignment_of(A::FakeArray) = Int32(0)
8484

8585
## Julia wrappers around FFTW functions
8686

87+
# _init_() must be called before any FFTW planning routine.
88+
# -- Once FFTW is split into its own module, this can be called
89+
# in the module __init__(), but for now we must call it lazily
90+
# in every routine that might initialize the FFTW planner.
91+
# -- This initializes FFTW's threads support (defaulting to 1 thread).
92+
# If this isn't called before the FFTW planner is created, then
93+
# FFTW's threads algorithms won't be registered or used at all.
94+
# (Previously, we called fftw_cleanup, but this invalidated existing
95+
# plans, causing issue #19892.)
96+
const threads_initialized = Ref(false)
97+
function _init_()
98+
if !threads_initialized[]
99+
stat = ccall((:fftw_init_threads,libfftw), Int32, ())
100+
statf = ccall((:fftwf_init_threads,libfftwf), Int32, ())
101+
if stat == 0 || statf == 0
102+
error("could not initialize FFTW threads")
103+
end
104+
threads_initialized[] = true
105+
end
106+
end
107+
87108
# Wisdom
88109

89110
# Import and export wisdom to/from a single file for all precisions,
@@ -96,6 +117,7 @@ alignment_of(A::FakeArray) = Int32(0)
96117
# FFTW's api/import-wisdom-from-file.c file].
97118

98119
function export_wisdom(fname::AbstractString)
120+
_init_()
99121
f = ccall(:fopen, Ptr{Void}, (Cstring,Cstring), fname, :w)
100122
systemerror("could not open wisdom file $fname for writing", f == C_NULL)
101123
ccall((:fftw_export_wisdom_to_file,libfftw), Void, (Ptr{Void},), f)
@@ -105,6 +127,7 @@ function export_wisdom(fname::AbstractString)
105127
end
106128

107129
function import_wisdom(fname::AbstractString)
130+
_init_()
108131
f = ccall(:fopen, Ptr{Void}, (Cstring,Cstring), fname, :r)
109132
systemerror("could not open wisdom file $fname for reading", f == C_NULL)
110133
if ccall((:fftw_import_wisdom_from_file,libfftw),Int32,(Ptr{Void},),f)==0||
@@ -115,32 +138,23 @@ function import_wisdom(fname::AbstractString)
115138
end
116139

117140
function import_system_wisdom()
141+
_init_()
118142
if ccall((:fftw_import_system_wisdom,libfftw), Int32, ()) == 0 ||
119143
ccall((:fftwf_import_system_wisdom,libfftwf), Int32, ()) == 0
120144
error("failed to import system wisdom")
121145
end
122146
end
123147

124148
function forget_wisdom()
149+
_init_()
125150
ccall((:fftw_forget_wisdom,libfftw), Void, ())
126151
ccall((:fftwf_forget_wisdom,libfftwf), Void, ())
127152
end
128153

129154
# Threads
130155

131-
const threads_initialized = Ref(false)
132156
function set_num_threads(nthreads::Integer)
133-
if !threads_initialized[]
134-
# must forget wisdom if any FFTW routines have been called
135-
# (don't call fftw_cleanup, since that would invalidate existing plans)
136-
forget_wisdom()
137-
stat = ccall((:fftw_init_threads,libfftw), Int32, ())
138-
statf = ccall((:fftwf_init_threads,libfftwf), Int32, ())
139-
if stat == 0 || statf == 0
140-
error("could not initialize FFTW threads")
141-
end
142-
threads_initialized[] = true
143-
end
157+
_init_()
144158
ccall((:fftw_plan_with_nthreads,libfftw), Void, (Int32,), nthreads)
145159
ccall((:fftwf_plan_with_nthreads,libfftwf), Void, (Int32,), nthreads)
146160
end
@@ -154,11 +168,15 @@ const PlanPtr = Ptr{fftw_plan_struct}
154168

155169
const NO_TIMELIMIT = -1.0 # from fftw3.h
156170

157-
set_timelimit(precision::fftwTypeDouble,seconds) =
171+
function set_timelimit(precision::fftwTypeDouble,seconds)
172+
_init_()
158173
ccall((:fftw_set_timelimit,libfftw), Void, (Float64,), seconds)
174+
end
159175

160-
set_timelimit(precision::fftwTypeSingle,seconds) =
176+
function set_timelimit(precision::fftwTypeSingle,seconds)
177+
_init_()
161178
ccall((:fftwf_set_timelimit,libfftwf), Void, (Float64,), seconds)
179+
end
162180

163181
# Array alignment mod 16:
164182
# FFTW plans may depend on the alignment of the array mod 16 bytes,

test/fft.jl

+6-2
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@
22

33
# issue #19892
44
# (test this first to make sure it happens before set_num_threads)
5-
let a = randn(10^5,1), p1 = plan_rfft(a)
5+
let a = randn(10^5,1), p1 = plan_rfft(a, flags=FFTW.ESTIMATE)
66
FFTW.set_num_threads(2)
7-
p2 = plan_rfft(a)
7+
p2 = plan_rfft(a, flags=FFTW.ESTIMATE)
88
@test p1*a p2*a
9+
# make sure threads are actually being used for p2
10+
# (tests #21163).
11+
@test !contains(string(p1), "dft-thr")
12+
@test contains(string(p2), "dft-thr")
913
end
1014

1115
# fft

0 commit comments

Comments
 (0)