【问题标题】:Why does padding an FFT in NumPy make it run much slower?为什么在 NumPy 中填充 FFT 会使其运行速度变慢?
【发布时间】:2014-12-13 08:21:27
【问题描述】:

我使用 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 设置中的一些怪癖,它的行为方式是这样的?

【问题讨论】:

  • 用更大的数组(例如 32768 附近)尝试相同的测试。
  • @WarrenWeckesser - 当我将其填充到 2 的幂(即 32768)时,性能现在有了 巨大 的提升。所以我猜在性能提升开始显现的地方有一个截止点。
  • 对于小型阵列,开销(额外复制,其他?)淹没了使用 2 的幂的性能增益。使用 n=1024 的开销数量令人惊讶,但我没有看过进入代码查找原因。

标签: python numpy fft


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    当你想使用np.linspace时,你正在使用np.arange

    In [2]: x     = np.arange(-4.*np.pi, 4.*np.pi, 1000)
    
    In [3]: x
    Out[3]: array([-12.56637061])
    

    np.arange 接受参数 (start, stop, step),而 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
    

    【讨论】:

    • 啊,该死的。在就寝时间之前,我不应该进行“智能”观察。感谢您指出愚蠢!
    • 我刚刚用linspace 尝试过,我发现与原始帖子相似的内容。我已经对其进行了编辑以反映这一点。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-15
    • 2018-10-12
    • 2019-07-19
    相关资源
    最近更新 更多