【发布时间】:2018-03-03 14:41:52
【问题描述】:
我见过几个例子表明如果输入长度是 2、3、5、7 等的乘积,那么 numpy 的 fft 实现很快。但是在这里仍然被认为是“小”的最大素数是多少?
【问题讨论】:
-
您是否尝试对此进行基准测试?
标签: python performance numpy fft primes
我见过几个例子表明如果输入长度是 2、3、5、7 等的乘积,那么 numpy 的 fft 实现很快。但是在这里仍然被认为是“小”的最大素数是多少?
【问题讨论】:
标签: python performance numpy fft primes
请注意,scipy 的 FFT 的基数为 2、3、4 和 5 (reference)。我假设 numpy 可能有类似的实现,这将使 5 成为 FFT 长度中最大的有效素数。
根据经验,就 FFT 性能而言,我认为“小”的最大素数是 11。但任何小于 30 的输入长度在实际应用中都会非常快。 Python 的执行开销肯定会使任何算法性能提升相形见绌。输入长度越长,事情就越有趣。
以下是小型 FFT 的一些性能结果(平均执行时间超过 500 批,每批 1000 个 FFT):
我已将素值 n 标记为红色,将二次幂标记为绿色。
标记以下观察结果:
一般来说,FFT 对于素数来说很慢,但对于二的幂来说很快。这是非常符合预期的,并且验证了结果。
n <=11 没有可测量的性能差异。这可能是由于 FFT 实现或执行开销造成的。
31(可能是 29)和更高的素数显然比附近的其他值慢。
有一些非二次方值也可以提供良好的性能。这可能是高度复合的数字。
测量是这样进行的:
import numpy as np
import matplotlib.pyplot as plt
from time import perf_counter as 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()
【讨论】:
numpy.fft 对合数来说很快,但对素数来说不是很快。使用 pyFFTW 获得 Python 的最高性能 DFT。
说明:
According to an old numpy issue,Bluestein 算法未针对素数长度数组上的 DFT 实现。维基百科指出,该算法的性能特征等同于应用于长度已被零填充的输入的高性能算法:
关键是这些 FFT 的长度 N 不同:只有通过将 FFT 补零到大于或等于 2N–1 的长度,才能从 FFT 精确计算出这样的卷积。特别是,可以填充到 2 的幂或一些其他高度复合大小,为此 FFT 可以通过例如时间为 O(N log N) 的 Cooley–Tukey 算法。因此,Bluestein 的算法提供了一种 O(N log N) 的方法来计算素数大小的 DFT,尽管它比 Cooley-Tukey 算法在复合大小上要慢几倍。
对于这些退化的情况,我建议避免使用numpy 的实现。请改用https://pypi.python.org/pypi/pyFFTW。我的直觉是性能差异将保持不变(即,速度减半),直到填充长度数组不再适合您的处理器的缓存 - 然后它将慢 10-100 倍。
【讨论】: