CFFI-based wrapper of Apple's Accelerate (vDSP) FFT routines. It implements multiple-signal version of 1D ffts (real and complex) and 2D ffts (real and complex).
Note that vDSP's FFT only work for input data of length that is a power of 2, e.g. ... 256, 512, 1024 ...
This module has a very primitive multi-threading support. Useful for large-size multi-signal 2d FFT and multi-signal 1d FFT. For multi-threaded calculation, input data has to have at least three dimesnions for 2d transform and at least two dimensions for 1d transform.
For Intel-based macs use mkl_fft, which is one of the fastest FFT libraries out there. However, the Accelerate implementation can be faster than MKL on Apple Silicon macs. See benchmarks below.
- cffi
- numpy
Install with pip:
$ pip install accelerate_fft
or clone the latest development version and run:
$ python setup.py install
Use it like numpy fft:
>>> import numpy as np >>> import accelerate_fft as fft >>> a = np.random.randn(4,8) >>> np.allclose(fft.fft(a), np.fft.fft(a)) True
Note that real transforms in accelerate have a wierd format, and the outputs are not the same as numpy's rfft and rfft2. Use :func:`unpack` or :func:`unpack2` to convert to numpy-like format. Note also the scaling factor of 2:
>>> np.allclose(fft.unpack(fft.rfft(a))/2, np.fft.rfft(a)) True >>> np.allclose(fft.unpack2(fft.rfft2(a))/2, np.fft.rfft2(a)) True
The inverse transforms are also different from numpy's. You should scale the results to get the proper inverse like:
>>> np.allclose(fft.ifft(fft.fft(a))/8, a) True >>> np.allclose(fft.ifft2(fft.fft2(a))/8/4, a) True >>> np.allclose(fft.irfft(fft.rfft(a))/8/2, a) True >>> np.allclose(fft.irfft2(fft.rfft2(a))/8/4/2, a) True
For multi-dimensional data (ndim >= 3), you can use threading to speed up computation of 2d transforms:
>>> a = np.random.randn(32,512,512) + 0j >>> fft.set_nthreads(4) 1 >>> out = fft.fft2(a)
Note that for small arrays, because of the python thread-creation overhead, this may be slower than:
>>> fft.set_nthreads(1) 4
Because vDSP's FFT work in split-complex data format. You may prepare and retrieve the data in this format directly. This is controlled by the split_in and split_out arguments, e.g.:
>>> real, imag = fft.fft2(a, split_out = True) >>> real, imag = fft.ifft2((real, imag), split_in = True, split_out = True, overwrite_x = True)
Here, the overwrite_x argument defines that the transform is done inplace and it overwrites the input data.
Testing was done using fft2 on input array of shape (8,2048,2048) and of "complex64" dtype.
| MAC Mini (2020) ,M1, 8GB | MBP (Early 2015), 2.7 GHz Intel i5, 8GB | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| library | accelerate_fft |
mkl_fft |
accelerate_fft |
mkl_fft |
||||||||
| N threads | 1 | 2 | 4 | 1 | 2 | 4 | 1 | 2 | 4 | 1 | 2 | 4 |
| t (ms) normal | 195 | 116 | 85 | 204 | 108 | 66 | 522 | 324 | 298 | 294 | 179 | 180 |
| t (ms) inplace | 147 | 97 | 59 | 200 | 100 | 59 | 334 | 188 | 171 | 248 | 154 | 153 |
The inplace version (overwrite_x = True) was done on split data format (split_in = True and split_out = True) for the accelerate_fft. Results show that on intel-based MACs, mkl_fft performs better, however, on Apple Silicon, accelerate_fft (single-core computation) can be slightly faster. Note that for mkl_fft you have to call:
>>> [mkl_fft.fft2(x, overwrite_x = True) for x in a]
whereas
>>> mkl_fft.fft2(a, overwrite_x = True)was much slower to compute.
A bugfix release. Makes 1D transform work on 2D data.
Improves multi-threaded fft calculation on smaller arrays and in 1D transforms. Implements 'axis' and 'axes' arguments.
accelerate_fft is released under MIT license so you can use it freely.