为什么在NumPy中填充FFT会使运行速度慢得多?

时间:2014-10-17 17:25:25

标签: python numpy fft

我使用NumPy的fft函数编写了一个脚本,我将输入数组填充到最接近的2的幂,以获得更快的FFT。

在对代码进行分析之后,我发现FFT调用花费的时间最长,所以我摆弄了参数,发现如果我没有填充输入数组, FFT的运行速度要快几倍。

这里有一个最简单的例子来说明我正在谈论的内容(我在IPython中运行它并使用%timeit魔术来计算执行时间。)

x     = np.arange(-4.*np.pi, 4.*np.pi, 1000)
dat1  = np.sin(x)

计时结果:

%timeit np.fft.fft(dat1)
100000 loops, best of 3: 12.3 µs per loop

%timeit np.fft.fft(dat1, n=1024)
10000 loops, best of 3: 61.5 µs per loop

将数组填充到2的幂会导致非常急剧的减速。

即使我创建了一个具有素数元素的数组(因此理论上最慢的FFT)

x2    = np.arange(-4.*np.pi, 4.*np.pi, 1009)
dat2  = np.sin(x2)

运行所需的时间仍然没有太大变化!

%timeit np.fft.fft(dat2)
100000 loops, best of 3: 12.2 µs per loop

我原以为填充数组将是一次操作,然后计算FFT应该更快。 我错过了什么吗?

编辑:我应该使用np.linspace而不是np.arange。以下是使用linspace

的时序结果
In [2]: import numpy as np

In [3]: x = np.linspace(-4*np.pi, 4*np.pi, 1000)

In [4]: x2 = np.linspace(-4*np.pi, 4*np.pi, 1024)

In [5]: dat1 = np.sin(x)

In [6]: dat2 = np.sin(x2)

In [7]: %timeit np.fft.fft(dat1)
10000 loops, best of 3: 55.1 µs per loop

In [8]: %timeit np.fft.fft(dat2)
10000 loops, best of 3: 49.4 µs per loop

In [9]: %timeit np.fft.fft(dat1, n=1024)
10000 loops, best of 3: 64.9 µs per loop

填充仍会导致速度减慢。这可能是一个本地问题吗?也就是说,由于我的NumPy设置中的一些怪癖它以这种方式表演?

2 个答案:

答案 0 :(得分:2)

像NumPy这样的FFT算法对于阵列大小来说很快,因为它们会分解成小素数的乘积,而不仅仅是2的幂。如果通过填充增加数组大小,计算工作量会增加。 FFT算法的速度也严重依赖于缓存使用。如果填充到创建低效缓存的数组大小,则效率会降低。真正快速的FFT算法,如FFTW和英特尔MKL,实际上将生成数组大小因子分解的计划,以获得最有效的计算。这包括启发式和实际测量。所以不,填充到最接近的2的幂只在入门教科书中有用,而不是在实践中有用。根据经验,如果数组大小计算为一个或多个非常大的素数,则通常会从填充中受益。

答案 1 :(得分:1)

当您想要使用np.arange

时,您正在使用np.linspace
In [2]: x     = np.arange(-4.*np.pi, 4.*np.pi, 1000)

In [3]: x
Out[3]: array([-12.56637061])

np.arange接受参数(开始,停止,步骤),而np.linspace是(start,stop,number_of_pts)。当您使用我怀疑您认为您正在使用的数据进行计算时,您会得到预期的行为:

In [4]: x = np.linspace(-4.*np.pi, 4.*np.pi, 1000)

In [5]: dat1 = np.sin(x)

In [6]: %timeit np.fft.fft(dat1)
1 loops, best of 3: 28.1 µs per loop

In [7]: %timeit np.fft.fft(dat1, n=1024)
10000 loops, best of 3: 26.7 µs per loop

In [8]: x = np.linspace(-4.*np.pi, 4.*np.pi, 1009)

In [9]: dat2 = np.sin(x)

In [10]: %timeit np.fft.fft(dat2)
10000 loops, best of 3: 53 µs per loop

In [11]: %timeit np.fft.fft(dat2, n=1024)
10000 loops, best of 3: 26.8 µs per loop