正确使用pyfftw加速numpy

时间:2015-01-30 00:34:39

标签: numpy pyfftw

我正在试图从Matlab跳跃到numpy,但我迫切需要速度在我的fft中。现在我知道pyfftw,但我不知道我正在使用它。我的方法类似于

import numpy as np
import pyfftw
import timeit

pyfftw.interfaces.cache.enable()

def wrapper(func, *args):
    def wrapped():
        return func(*args)
    return wrapped

def my_fft(v):
    global a
    global fft_object
    a[:] = v
    return fft_object()

def init_cond(X):
    return my_fft(2.*np.cosh(X)**(-2))

def init_cond_py(X):
    return np.fft.fft(2.*np.cosh(X)**(-2))

K = 2**16
Llx = 10.
KT = 2*K
dx = Llx/np.float64(K)
X = np.arange(-Llx,Llx,dx)

global a
global b
global fft_object
a = pyfftw.n_byte_align_empty(KT, 16, 'complex128')
b = pyfftw.n_byte_align_empty(KT, 16, 'complex128')
fft_object = pyfftw.FFTW(a,b)

wrapped = wrapper(init_cond, X)
print min(timeit.repeat(wrapped,repeat=100,number=1))

wrapped_two = wrapper(init_cond_py, X)
print min(timeit.repeat(wrapped_two,repeat=100,number=1))

我很欣赏有通过pyfftw进行scipy和numpy fft调用的构建器函数和标准接口。但这些都表现得非常缓慢。通过首先创建fft_object的一个实例然后全局使用它,我已经能够比numpy的fft调用更快或更快地获得速度。

话虽这么说,我的工作是假设智慧被隐含地存储。真的吗?我需要明确说明吗?如果是这样,最好的方法是什么?

另外,我认为timeit是完全不透明的。我使用得当吗?我称之为重复存储智慧吗?提前感谢您提供的任何帮助。

1 个答案:

答案 0 :(得分:3)

在交互式(ipython)会话中,我认为以下是你想要做的事情(ipython非常好地处理了timeit):

In [1]: import numpy as np

In [2]: import pyfftw

In [3]: K = 2**16

In [4]: Llx = 10.

In [5]: KT = 2*K

In [6]: dx = Llx/np.float64(K)

In [7]: X = np.arange(-Llx,Llx,dx)

In [8]: a = pyfftw.n_byte_align_empty(KT, 16, 'complex128')

In [9]: b = pyfftw.n_byte_align_empty(KT, 16, 'complex128')

In [10]: fft_object = pyfftw.FFTW(a,b)

In [11]: a[:] = 2.*np.cosh(X)**(-2)

In [12]: timeit np.fft.fft(a)
100 loops, best of 3: 4.96 ms per loop

In [13]: timeit fft_object(a)
100 loops, best of 3: 1.56 ms per loop

In [14]: np.allclose(fft_object(a), np.fft.fft(a))
Out[14]: True

您是否阅读过tutorial?你不明白什么?

我建议使用builders interface来构造FFTW对象。玩各种设置,最重要的是线程数。

默认情况下不存储智慧。您需要extract it yourself

您的所有globals都是不必要的 - 您想要更改的对象是可变的,因此您可以很好地处理它们。 fft_object总是指向同一个东西,所以没有问题就不是全局的。理想情况下,您只是不希望循环ii。我建议你如何构建数组,以便在一次调用中完成所有操作

编辑: [编辑:我写了下面的段落只是粗略地看了一下你的代码,显然它是一个递归更新,vectorising并不是一个明显的方法,没有一些严重的狡猾。我对你在底部的实现有一些评论,虽然] 我怀疑你的问题是如何最好地使用像Python(或Matlab)这样的语言进行数值处理的一个更基本的误解。核心原则是尽可能地进行矢量化。通过这个,我的意思是将你的python调用尽可能少。不幸的是,我不知道如何用你的例子做到这一点(虽然我只考虑了2分钟)。如果这仍然失败,请考虑cython - 尽管确保你真的想沿着那条路走下去(即你已经用尽了其他选择)。

关于全局:不要这样做。如果要创建一个具有状态的对象,请使用类(这就是它们的用途)或者在您的情况下使用闭包。全局几乎从来都不是你想要的(我想我在编写python的时候至少有一个模糊的合法用法,而且在pyfftw的缓存代码中)。我建议阅读this nice SO question。 Matlab是一种糟糕的语言 - 其中一个原因就是它的废话范围设施往往导致不良习惯。

如果您想全局修改参考,则只需要全局。我建议阅读更多关于Python scoping rules以及python中的变量really are的内容。

FFTW个对象带有您需要的所有阵列,因此您不需要单独传递它们。使用调用界面几乎不会产生任何开销(特别是如果您禁用标准化),无论是设置还是返回值 - 如果您处于该优化级别,我强烈怀疑您已达到限制(I&# 39;请注意,对于许多非常小的FFT,这可能不是相当,但此时你想重新考虑你的算法来向FFTW调用。如果你每次更新数组都会发现很大的开销(使用调用接口),这是一个错误,你应该这样提交它(我会非常惊讶)。

最重要的是,不要担心每次通话都会更新阵列。这几乎肯定不是你的瓶颈,但是如果你愿意的话,请确保你知道规范化并禁用它(与原始访问update_arrays()execute()相比,它可能会减慢速度方法)。

您的代码不使用缓存。缓存仅在您使用interfaces代码时使用,并减少了内部创建新FFTW对象的Python开销。由于您自己处理FFTW对象,因此没有理由进行缓存。

builders代码是获得FFTW对象的约束较少的接口。我现在几乎总是使用构建器(从头开始创建FFTW对象要方便得多)。您想直接创建FFTW对象的情况非常罕见,我有兴趣知道它们是什么。

关于算法实现的评论: 我不熟悉您正在实施的算法。但是,我对你目前的写作方式有一些评论。 您在每个循环中计算nl_eval(wp),但据我所知,它与前一循环中的nl_eval(w)相同,因此您不需要计算它两次(但有一点需要注意,当你到处都有全局变量时,很难看到它会发生什么,所以我可能会遗漏一些东西)。

不要在my_fftmy_ifft中烦扰副本。只需执行fft_object(u)(2.29毫秒与我的机器上1.67毫秒的前转情况)。内部数组更新例程使副本不必要。此外,正如您已经写过的那样,您需要复制两次:c[:]表示"复制到数组c",以及您正在复制的数组重新复制到cv.copy(),即v的副本(总共两份)。

更明智(也许是必要的)是将输出复制到保持数组中(因为这样可以避免在调用FFTW对象时破坏中间结果),但要确保保持数组正确对齐。我确定您已经注意到这很重要,但复制输出更容易理解。

您可以将所有缩放移动到一起。 wn计算中的3可以在my_fft中的nl_eval内移动。你也可以将它与ifft的标准化常量结合起来(并在pyfftw中将其关闭)。

查看numexpr的基本数组操作。它可以提供相对于香草numpy的相当多的加速。

无论如何,从所有这些中取出你的意愿。毫无疑问,我已经错过了某些内容或者说了一些不正确的内容,所以请尽量谦虚地接受它。值得花一点时间研究Python与Matlab相比如何(事实上,忘记后者)。