numpy fft对于长度小的素数而言很快,但有多小?

时间:2017-09-22 05:42:26

标签: python performance numpy fft primes

我已经看到几个例子表明,如果输入长度是2,3,5,7等的乘积,那么numpy的fft实现很快。但是,这里仍然被认为是“小”的最大素数是多少?

2 个答案:

答案 0 :(得分:5)

请注意,scipy的FFT具有2,3,4和5(reference)的基数。我假设numpy可能有类似的实现,这将使5成为FFT长度中最大的有效素数因子。

根据经验,最大的素数我会考虑"小"出于FFT性能的目的是11.但是对于实际目的而言,任何小于约30的输入长度都将非常快。 Python的执行开销肯定会使任何算法性能提升相形见绌。对于更高的输入长度,事情变得越来越有趣。

以下是小型FFT的一些性能结果(中位执行时间超过500批1000个FFT):

enter image description here

我用红色标记了素数n,用绿色标记了两个幂。

标记以下观察结果:

  • 一般来说,FFT对素数来说很慢,但对于两倍的幂来说很快。这非常期待并验证结果。

  • n <=11的性能差异无法衡量。这可能是由于FFT实现或执行开销造成的。

  • 31(可能是29)及更高的素数明显慢于其他附近值。

  • 有一些非二次幂值也能提供良好的性能。这可能是高度复合的数字。

测量结果如下:

import numpy as np
import matplotlib.pyplot as plt
from time import time


N = np.arange(2, 65)
times = np.empty((500, N.size))
for i, n in enumerate(N):
    for r in range(times.shape[0]):
        x = np.random.randn(1000, n)
        t = time()
        y = np.fft.fft(x, axis=-1)
        t = time() - t
        times[r, i] = t


med = np.median(times, axis=0)
plt.plot(N, med, 'k')

primes = np.array([2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61])
plt.plot(primes, med[primes-2]+0.0005, 'rx', label='n = prime')

ptwos = np.array([2, 4, 8, 16, 32, 64])
plt.plot(ptwos, med[ptwos-2]-0.0005, 'gx', label='n = 2**k')

plt.legend(loc='best')
plt.xlabel('n')
plt.ylabel('time')
plt.grid()
plt.show()

答案 1 :(得分:2)

numpy.fft对于复合数字来说速度很快,但对于素数而言并不快。使用pyFFTW获得最高性能的DFT。

<强>解释

According to an old numpy issue,对于素数长度的数组,Bluestein算法 实现了DFT。维基百科指出,该算法的性能特征等同于应用于长度为零填充的输入的高性能算法:

  

关键点在于这些FFT的长度N不同:这样的卷积只能通过将其填充到大于或等于2N-1的长度来精确地从FFT计算出来。特别地,可以填充到两个或一些其他高度复合尺寸的功率,对于该功率,可以通过例如以下方式有效地执行FFT。 O(N log N)时间内的Cooley-Tukey算法。因此,Bluestein的算法提供了一种O(N log N)方法来计算素数大小的DFT,尽管比Cooley-Tukey算法的复合尺寸慢几倍。

我建议一般不要使用numpy的实现来解决这些退化情况。请改用https://pypi.python.org/pypi/pyFFTW。我的直觉是性能差异将是恒定的(即快一半),直到填充长度的数组不再适合您的处理器缓存 - 然后它将慢10-100倍。