比较慢的python numpy 3D傅里叶变换

时间:2016-10-15 16:08:55

标签: python performance numpy fft

对于我的工作,我需要在大图像上执行离散傅里叶变换(DFT)。在当前示例中,我需要用于1921 x 512 x 512图像的3D FT(以及512 x 512图像的2D FFT)。现在,我正在使用numpy包和相关函数np.fft.fftn()。下面的代码片段以下列方式示例性地显示了相同尺寸/略小的2D / 3D随机数生成网格上的2D和3D FFT时间:

import sys
import numpy as np
import time

tas = time.time()
a = np.random.rand(512, 512)
tab = time.time()
b = np.random.rand(100, 512, 512)

tbfa = time.time()

fa = np.fft.fft2(a)
tfafb = time.time()
fb = np.fft.fftn(b)
tfbe = time.time()

print "initializing 512 x 512 grid:", tab - tas
print "initializing 100 x 512 x 512 grid:", tbfa - tab
print "2D FFT on 512 x 512 grid:", tfafb - tbfa
print "3D FFT on 100 x 512 x 512 grid:", tfbe - tfafb

输出:

initializing 512 x 512 grid: 0.00305700302124
initializing 100 x 512 x 512 grid: 0.301637887955
2D FFT on 512 x 512 grid: 0.0122730731964
3D FFT on 100 x 512 x 512 grid: 3.88418793678

我遇到的问题是我经常需要这个过程,所以每张图像花费的时间应该很短。在我自己的计算机上测试时(中段笔记本电脑,分配给虚拟机的2GB RAM( - >因此更小的测试网格)),你可以看到3D FFT需要~5 s(数量级)。现在,在工作中,机器更好,集群/网格架构系统和FFT更快。在这两种情况下,2D瞬间完成。

然而,对于1921x512x512,np.fft.fftn()需要约5分钟。因为我猜scipy的实现并不快得多,并且考虑到在相同大小的网格上MATLAB的FFT在~5秒内完成,我的问题是是否有一种方法可以将过程加速到或几乎达到MATLAB时间。我对FFT的了解有限,但显然MATLAB使用的是FFTW算法,而python则没有。有一些合理的机会,有一些pyFFTW包我得到相似的时间?此外,1921年似乎是一个不吉利的选择,只有2个素数因子(17,113),所以我认为这也起了作用。另一方面,512是非常适合的2的幂。如果可能的话,是否可以实现类似MATLAB的时间,而不用零填充到2048?

我在问,因为我必须经常使用FFT(对于这种差异影响巨大的数量!),如果没有可能减少python中的计算时间,我必须切换到其他更快的实现。

2 个答案:

答案 0 :(得分:2)

是的,与pyfftwnumpy.fft相比,通过接口scipy.fftpack使用FFTW可能会缩短您的计算时间。这些DFT算法实现的性能可以在this one等基准测试中进行比较:Improving FFT performance in Python中报告了一些有趣的结果

我建议使用以下代码进行测试:

import pyfftw
import numpy
import time
import scipy

f = pyfftw.n_byte_align_empty((127,512,512),16, dtype='complex128')
#f = pyfftw.empty_aligned((33,128,128), dtype='complex128', n=16)
f[:] = numpy.random.randn(*f.shape)

# first call requires more time for plan creation
# by default, pyfftw use FFTW_MEASURE for the plan creation, which means that many 3D dft are computed so as to choose the fastest algorithm.
fftf=pyfftw.interfaces.numpy_fft.fftn(f)

#help(pyfftw.interfaces)
tas = time.time()
fftf=pyfftw.interfaces.numpy_fft.fftn(f) # here the plan is applied, nothing else.
tas = time.time()-tas
print "3D FFT, pyfftw:", tas

f = pyfftw.n_byte_align_empty((127,512,512),16, dtype='complex128')
#f = pyfftw.empty_aligned((33,128,128), dtype='complex128', n=16)
f[:] = numpy.random.randn(*f.shape)


tas = time.time()
fftf=numpy.fft.fftn(f)
tas = time.time()-tas
print "3D FFT, numpy:", tas

tas = time.time()
fftf=scipy.fftpack.fftn(f)
tas = time.time()-tas
print "3D FFT, scipy/fftpack:", tas

# first call requires more time for plan creation
# by default, pyfftw use FFTW_MEASURE for the plan creation, which means that many 3D dft are computed so as to choose the fastest algorithm.
f = pyfftw.n_byte_align_empty((128,512,512),16, dtype='complex128')
fftf=pyfftw.interfaces.numpy_fft.fftn(f)

tas = time.time()
fftf=pyfftw.interfaces.numpy_fft.fftn(f) # here the plan is applied, nothing else.
tas = time.time()-tas
print "3D padded FFT, pyfftw:", tas

对于127 * 512 * 512的大小,在我的小型计算机上,我得到了:

3D FFT, pyfftw: 3.94130897522
3D FFT, numpy: 16.0487070084
3D FFT, scipy/fftpack: 19.001199007
3D padded FFT, pyfftw: 2.55221295357

因此pyfftw明显快于numpy.fftscipy.fftpack。使用填充更快,但计算的东西是不同的。

最后,pyfftw在第一次运行时可能看起来较慢,因为它根据documentation使用了标记FFTW_MEASURE。当且仅当连续计算了许多相同大小的DFT时,这是一件好事。

答案 1 :(得分:0)

您可以尝试从英特尔MKL(数学内核库)进行FFT,该速度比FFTW高faster。 英特尔为Python提供了mkl-fft,它取代了numpy.fft。您需要做的就是键入:

pip install mkl-fft

,然后再次运行程序,无需进行任何更改。

另外,numpy 1.17(即将发布)将具有FFT的新实现:

  

Pocketfft库替换了基于fftpack的FFT模块

     

两个实现都具有相同的祖先(Paul的Fortran77 FFTPACK   N. Swarztrauber),但pocketfft包含其他修改   在某些情况下可以提高准确性和性能。对于   FFT长度包含较大的素因数,pocketfft使用Bluestein的   算法,它维持O(N log N)的运行时复杂度,而不是   素数长度朝O(N * N)方向恶化。另外,对于   具有接近素数长度的实值FFT已得到改进,并且可以与之媲美   带有复数值FFT。