【问题标题】:numpy fft is fast for lengths that are products of small primes, but how small?numpy fft 对于小素数乘积的长度很快,但有多小?
【发布时间】:2018-03-03 14:41:52
【问题描述】:

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

【问题讨论】:

  • 您是否尝试对此进行基准测试?

标签: python performance numpy fft primes


【解决方案1】:

请注意,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()

【讨论】:

  • 我认为 N
  • 是的。我修改了您的基准测试代码以使用更长的输入长度(最多 10^4),并根据它们的主要因素限制输入长度。对于限制为 2、3 和 5 的素数因子,FFT 时间几乎与输入长度呈线性关系,并且在 10^4 处几乎达到相同的最大值。引入 7 几乎使峰值翻了一番。所以看起来你的答案,因为它专注于简短的输入,可能过于乐观了?
  • @TavinCole 啊,看来我误读了你的问题。我以为您对素值 FFT 而不是复合物感兴趣。事实上,对于复合材料,我们应该研究更广泛的数字。我不确定 FFT 是如何实际实现的——也许看看素数的幂就足以了解它们在复合材料中的表现?
【解决方案2】:

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 倍。

【讨论】:

猜你喜欢
  • 2021-12-29
  • 1970-01-01
  • 1970-01-01
  • 2016-06-03
  • 2015-11-04
  • 1970-01-01
  • 1970-01-01
  • 2021-03-30
相关资源
最近更新 更多