我正在试图从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是完全不透明的。我使用得当吗?我称之为重复存储智慧吗?提前感谢您提供的任何帮助。
答案 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_fft
或my_ifft
中烦扰副本。只需执行fft_object(u)
(2.29毫秒与我的机器上1.67毫秒的前转情况)。内部数组更新例程使副本不必要。此外,正如您已经写过的那样,您需要复制两次:c[:]
表示"复制到数组c
",以及您正在复制的数组重新复制到c
是v.copy()
,即v
的副本(总共两份)。
更明智(也许是必要的)是将输出复制到保持数组中(因为这样可以避免在调用FFTW对象时破坏中间结果),但要确保保持数组正确对齐。我确定您已经注意到这很重要,但复制输出更容易理解。
您可以将所有缩放移动到一起。 wn
计算中的3可以在my_fft
中的nl_eval
内移动。你也可以将它与ifft的标准化常量结合起来(并在pyfftw中将其关闭)。
查看numexpr的基本数组操作。它可以提供相对于香草numpy的相当多的加速。
无论如何,从所有这些中取出你的意愿。毫无疑问,我已经错过了某些内容或者说了一些不正确的内容,所以请尽量谦虚地接受它。值得花一点时间研究Python与Matlab相比如何(事实上,忘记后者)。