Skip to content

Commit b10667a

Browse files
stevengjtkelman
authored andcommitted
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 (cherry picked from commit 378ed8a)
1 parent eb7e796 commit b10667a

File tree

2 files changed

+38
-16
lines changed

2 files changed

+38
-16
lines changed

base/fft/FFTW.jl

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,27 @@ alignment_of(A::FakeArray) = Int32(0)
8989

9090
## Julia wrappers around FFTW functions
9191

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

94115
# Import and export wisdom to/from a single file for all precisions,
@@ -101,6 +122,7 @@ alignment_of(A::FakeArray) = Int32(0)
101122
# FFTW's api/import-wisdom-from-file.c file].
102123

103124
function export_wisdom(fname::AbstractString)
125+
_init_()
104126
f = ccall(:fopen, Ptr{Void}, (Cstring,Cstring), fname, :w)
105127
systemerror("could not open wisdom file $fname for writing", f == C_NULL)
106128
ccall((:fftw_export_wisdom_to_file,libfftw), Void, (Ptr{Void},), f)
@@ -110,6 +132,7 @@ function export_wisdom(fname::AbstractString)
110132
end
111133

112134
function import_wisdom(fname::AbstractString)
135+
_init_()
113136
f = ccall(:fopen, Ptr{Void}, (Cstring,Cstring), fname, :r)
114137
systemerror("could not open wisdom file $fname for reading", f == C_NULL)
115138
if ccall((:fftw_import_wisdom_from_file,libfftw),Int32,(Ptr{Void},),f)==0||
@@ -120,32 +143,23 @@ function import_wisdom(fname::AbstractString)
120143
end
121144

122145
function import_system_wisdom()
146+
_init_()
123147
if ccall((:fftw_import_system_wisdom,libfftw), Int32, ()) == 0 ||
124148
ccall((:fftwf_import_system_wisdom,libfftwf), Int32, ()) == 0
125149
error("failed to import system wisdom")
126150
end
127151
end
128152

129153
function forget_wisdom()
154+
_init_()
130155
ccall((:fftw_forget_wisdom,libfftw), Void, ())
131156
ccall((:fftwf_forget_wisdom,libfftwf), Void, ())
132157
end
133158

134159
# Threads
135160

136-
const threads_initialized = Ref(false)
137161
function set_num_threads(nthreads::Integer)
138-
if !threads_initialized[]
139-
# must forget wisdom if any FFTW routines have been called
140-
# (don't call fftw_cleanup, since that would invalidate existing plans)
141-
forget_wisdom()
142-
stat = ccall((:fftw_init_threads,libfftw), Int32, ())
143-
statf = ccall((:fftwf_init_threads,libfftwf), Int32, ())
144-
if stat == 0 || statf == 0
145-
error("could not initialize FFTW threads")
146-
end
147-
threads_initialized[] = true
148-
end
162+
_init_()
149163
ccall((:fftw_plan_with_nthreads,libfftw), Void, (Int32,), nthreads)
150164
ccall((:fftwf_plan_with_nthreads,libfftwf), Void, (Int32,), nthreads)
151165
end
@@ -159,11 +173,15 @@ typealias PlanPtr Ptr{fftw_plan_struct}
159173

160174
const NO_TIMELIMIT = -1.0 # from fftw3.h
161175

162-
set_timelimit(precision::fftwTypeDouble,seconds) =
176+
function set_timelimit(precision::fftwTypeDouble,seconds)
177+
_init_()
163178
ccall((:fftw_set_timelimit,libfftw), Void, (Float64,), seconds)
179+
end
164180

165-
set_timelimit(precision::fftwTypeSingle,seconds) =
181+
function set_timelimit(precision::fftwTypeSingle,seconds)
182+
_init_()
166183
ccall((:fftwf_set_timelimit,libfftwf), Void, (Float64,), seconds)
184+
end
167185

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

test/fft.jl

Lines changed: 6 additions & 2 deletions
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)