Skip to content

Commit 53b809c

Browse files
committed
explicit warmup call
1 parent 8eee4c5 commit 53b809c

4 files changed

Lines changed: 107 additions & 0 deletions

File tree

benchmarks/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,17 @@ regardless of their total core count. Physical cores are detected via
2828
`psutil.cpu_count(logical=False)` — hyperthreads are excluded per MKL
2929
recommendation.
3030

31+
## Notes on Measurement
32+
33+
### DFTI descriptor warmup
34+
35+
MKL creates a DFTI descriptor on the first FFT call for a given (size, dtype,
36+
strides) combination and reuses it on subsequent calls. To avoid charging
37+
that one-time cost to the first measured iteration, each benchmark's `setup`
38+
performs an explicit warmup call after preparing the input array. ASV's
39+
default `warmup_time` (0.1s) already amortizes this for sub-millisecond
40+
transforms, but the explicit warmup makes the intent visible.
41+
3142
## Running Benchmarks
3243

3344
Prerequisites:

benchmarks/benchmarks/bench_fft1d.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,15 @@ class BenchFFT1D(BenchC2C):
1919
params = [_SIZES_POW2, _DTYPES_ALL]
2020
param_names = ["n", "dtype"]
2121

22+
def setup(self, n, dtype):
23+
super().setup(n, dtype)
24+
# Prime the MKL DFTI descriptor cache so the first measured
25+
# iteration doesn't pay the one-time descriptor-creation cost.
26+
# ASV's warmup_time (default 0.1s) would normally cover this,
27+
# but doing it explicitly removes the dependency on that default.
28+
mkl_fft.fft(self.x)
29+
mkl_fft.ifft(self.x)
30+
2231
def time_fft(self, n, dtype):
2332
mkl_fft.fft(self.x)
2433

@@ -37,6 +46,12 @@ class BenchRFFT1D(BenchR2C):
3746
params = [_SIZES_POW2, _DTYPES_REAL]
3847
param_names = ["n", "dtype"]
3948

49+
def setup(self, n, dtype):
50+
super().setup(n, dtype)
51+
# Prime the DFTI descriptor cache (see BenchFFT1D.setup).
52+
mkl_fft.rfft(self.x_real)
53+
mkl_fft.irfft(self.x_complex, n=n)
54+
4055
def time_rfft(self, n, dtype):
4156
mkl_fft.rfft(self.x_real)
4257

@@ -59,6 +74,12 @@ class BenchFFT1DNonPow2(BenchC2C):
5974
params = [_SIZES_NONPOW2, _DTYPES_ALL]
6075
param_names = ["n", "dtype"]
6176

77+
def setup(self, n, dtype):
78+
super().setup(n, dtype)
79+
# Prime the DFTI descriptor cache (see BenchFFT1D.setup).
80+
mkl_fft.fft(self.x)
81+
mkl_fft.ifft(self.x)
82+
6283
def time_fft(self, n, dtype):
6384
mkl_fft.fft(self.x)
6485

@@ -77,6 +98,12 @@ class BenchRFFT1DNonPow2(BenchR2C):
7798
params = [_SIZES_NONPOW2, _DTYPES_REAL]
7899
param_names = ["n", "dtype"]
79100

101+
def setup(self, n, dtype):
102+
super().setup(n, dtype)
103+
# Prime the DFTI descriptor cache (see BenchFFT1D.setup).
104+
mkl_fft.rfft(self.x_real)
105+
mkl_fft.irfft(self.x_complex, n=n)
106+
80107
def time_rfft(self, n, dtype):
81108
mkl_fft.rfft(self.x_real)
82109

benchmarks/benchmarks/bench_fftnd.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,15 @@ class BenchFFT2D(BenchC2C):
2626
]
2727
param_names = ["shape", "dtype"]
2828

29+
def setup(self, shape, dtype):
30+
super().setup(shape, dtype)
31+
# Prime the MKL DFTI descriptor cache so the first measured
32+
# iteration doesn't pay the one-time descriptor-creation cost.
33+
# ASV's warmup_time (default 0.1s) would normally cover this,
34+
# but doing it explicitly removes the dependency on that default.
35+
mkl_fft.fft2(self.x)
36+
mkl_fft.ifft2(self.x)
37+
2938
def time_fft2(self, shape, dtype):
3039
mkl_fft.fft2(self.x)
3140

@@ -44,6 +53,12 @@ class BenchRFFT2D(BenchR2C):
4453
params = [_SHAPES_2D, _DTYPES_REAL]
4554
param_names = ["shape", "dtype"]
4655

56+
def setup(self, shape, dtype):
57+
super().setup(shape, dtype)
58+
# Prime the DFTI descriptor cache (see BenchFFT2D.setup).
59+
mkl_fft.rfft2(self.x_real)
60+
mkl_fft.irfft2(self.x_complex, s=shape)
61+
4762
def time_rfft2(self, shape, dtype):
4863
mkl_fft.rfft2(self.x_real)
4964

@@ -71,6 +86,12 @@ class BenchFFT2DNonPow2(BenchC2C):
7186
]
7287
param_names = ["shape", "dtype"]
7388

89+
def setup(self, shape, dtype):
90+
super().setup(shape, dtype)
91+
# Prime the DFTI descriptor cache (see BenchFFT2D.setup).
92+
mkl_fft.fft2(self.x)
93+
mkl_fft.ifft2(self.x)
94+
7495
def time_fft2(self, shape, dtype):
7596
mkl_fft.fft2(self.x)
7697

@@ -92,6 +113,12 @@ class BenchFFTnD(BenchC2C):
92113
]
93114
param_names = ["shape", "dtype"]
94115

116+
def setup(self, shape, dtype):
117+
super().setup(shape, dtype)
118+
# Prime the DFTI descriptor cache (see BenchFFT2D.setup).
119+
mkl_fft.fftn(self.x)
120+
mkl_fft.ifftn(self.x)
121+
95122
def time_fftn(self, shape, dtype):
96123
mkl_fft.fftn(self.x)
97124

@@ -110,6 +137,12 @@ class BenchRFFTnD(BenchR2C):
110137
params = [_SHAPES_3D, _DTYPES_REAL]
111138
param_names = ["shape", "dtype"]
112139

140+
def setup(self, shape, dtype):
141+
super().setup(shape, dtype)
142+
# Prime the DFTI descriptor cache (see BenchFFT2D.setup).
143+
mkl_fft.rfftn(self.x_real)
144+
mkl_fft.irfftn(self.x_complex, s=shape)
145+
113146
def time_rfftn(self, shape, dtype):
114147
mkl_fft.rfftn(self.x_real)
115148

@@ -136,6 +169,12 @@ class BenchFFTnDNonPow2(BenchC2C):
136169
]
137170
param_names = ["shape", "dtype"]
138171

172+
def setup(self, shape, dtype):
173+
super().setup(shape, dtype)
174+
# Prime the DFTI descriptor cache (see BenchFFT2D.setup).
175+
mkl_fft.fftn(self.x)
176+
mkl_fft.ifftn(self.x)
177+
139178
def time_fftn(self, shape, dtype):
140179
mkl_fft.fftn(self.x)
141180

benchmarks/benchmarks/bench_interfaces.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ def setup(self, n, dtype, module):
4949
mod = _MODULE_MAP[module]
5050
self.fft = mod.fft
5151
self.ifft = mod.ifft
52+
# Prime the MKL DFTI descriptor cache so the first measured
53+
# iteration doesn't pay the one-time descriptor-creation cost.
54+
# ASV's warmup_time (default 0.1s) would normally cover this,
55+
# but doing it explicitly removes the dependency on that default.
56+
self.fft(self.x)
57+
self.ifft(self.x)
5258

5359
def time_fft(self, n, dtype, module):
5460
self.fft(self.x)
@@ -73,6 +79,9 @@ def setup(self, n, dtype, module):
7379
mod = _MODULE_MAP[module]
7480
self.rfft = mod.rfft
7581
self.irfft = mod.irfft
82+
# Prime the DFTI descriptor cache (see BenchC2C1D.setup).
83+
self.rfft(self.x_real)
84+
self.irfft(self.x_complex, n=n)
7685

7786
def time_rfft(self, n, dtype, module):
7887
self.rfft(self.x_real)
@@ -103,6 +112,9 @@ def setup(self, n, dtype, module):
103112
mod = _MODULE_MAP[module]
104113
self.hfft = mod.hfft
105114
self.ihfft = mod.ihfft
115+
# Prime the DFTI descriptor cache (see BenchC2C1D.setup).
116+
self.hfft(self.x_complex, n=n)
117+
self.ihfft(self.x_real)
106118

107119
def time_hfft(self, n, dtype, module):
108120
self.hfft(self.x_complex, n=n)
@@ -127,6 +139,9 @@ def setup(self, shape, dtype, module):
127139
mod = _MODULE_MAP[module]
128140
self.fft2 = mod.fft2
129141
self.ifft2 = mod.ifft2
142+
# Prime the DFTI descriptor cache (see BenchC2C1D.setup).
143+
self.fft2(self.x)
144+
self.ifft2(self.x)
130145

131146
def time_fft2(self, shape, dtype, module):
132147
self.fft2(self.x)
@@ -151,6 +166,9 @@ def setup(self, shape, dtype, module):
151166
mod = _MODULE_MAP[module]
152167
self.rfft2 = mod.rfft2
153168
self.irfft2 = mod.irfft2
169+
# Prime the DFTI descriptor cache (see BenchC2C1D.setup).
170+
self.rfft2(self.x_real)
171+
self.irfft2(self.x_complex, s=shape)
154172

155173
def time_rfft2(self, shape, dtype, module):
156174
self.rfft2(self.x_real)
@@ -180,6 +198,9 @@ def setup(self, shape, dtype, module):
180198
mod = _MODULE_MAP[module]
181199
self.hfft2 = mod.hfft2
182200
self.ihfft2 = mod.ihfft2
201+
# Prime the DFTI descriptor cache (see BenchC2C1D.setup).
202+
self.hfft2(self.x_complex, s=shape)
203+
self.ihfft2(self.x_real)
183204

184205
def time_hfft2(self, shape, dtype, module):
185206
self.hfft2(self.x_complex, s=shape)
@@ -204,6 +225,9 @@ def setup(self, shape, dtype, module):
204225
mod = _MODULE_MAP[module]
205226
self.fftn = mod.fftn
206227
self.ifftn = mod.ifftn
228+
# Prime the DFTI descriptor cache (see BenchC2C1D.setup).
229+
self.fftn(self.x)
230+
self.ifftn(self.x)
207231

208232
def time_fftn(self, shape, dtype, module):
209233
self.fftn(self.x)
@@ -228,6 +252,9 @@ def setup(self, shape, dtype, module):
228252
mod = _MODULE_MAP[module]
229253
self.rfftn = mod.rfftn
230254
self.irfftn = mod.irfftn
255+
# Prime the DFTI descriptor cache (see BenchC2C1D.setup).
256+
self.rfftn(self.x_real)
257+
self.irfftn(self.x_complex, s=shape)
231258

232259
def time_rfftn(self, shape, dtype, module):
233260
self.rfftn(self.x_real)
@@ -257,6 +284,9 @@ def setup(self, shape, dtype, module):
257284
mod = _MODULE_MAP[module]
258285
self.hfftn = mod.hfftn
259286
self.ihfftn = mod.ihfftn
287+
# Prime the DFTI descriptor cache (see BenchC2C1D.setup).
288+
self.hfftn(self.x_complex, s=shape)
289+
self.ihfftn(self.x_real)
260290

261291
def time_hfftn(self, shape, dtype, module):
262292
self.hfftn(self.x_complex, s=shape)

0 commit comments

Comments
 (0)