正确使用pyfftw在numpy上加速

正确使用pyfftw在numpy上加速,numpy,pyfftw,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

我正在尝试从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_对象的实例,然后全局使用它,我能够获得与numpy的fft调用一样快或稍快的速度

话虽如此,我工作的前提是,智慧被隐含地储存着。这是真的吗?我需要把它说清楚吗?如果是,最好的方法是什么

而且,我认为它是完全不透明的。我用得对吗?它是否像我所说的那样储存智慧?提前感谢您可能提供的任何帮助

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

你看过报纸了吗?你还不明白什么

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

智慧不是默认存储的。你需要这样做

所有的
globals
都是不必要的-您想要更改的对象是可变的,因此您可以很好地处理它们<代码>fft_对象始终指向同一个对象,因此如果不是全局对象,就没有问题。理想情况下,您只是不希望该循环覆盖
ii
。我建议研究一下如何构造数组,以便在一次调用中完成所有操作

编辑: [编辑:我只是粗略地看了一下你的代码,写了下面的一段,很明显,由于它是一个递归更新,向量化不是一个明显的方法,没有一些严重的技巧。不过,我在底部对你的实现有一些评论] 我怀疑您的问题是对如何最好地使用Python(或者实际上是Matlab)之类的语言进行数值处理的更根本的误解。其核心宗旨是尽可能多地矢量化。我的意思是,将python调用汇总为尽可能少的调用。不幸的是,我看不出如何使用您的示例实现这一点(尽管我只考虑了2分钟)。如果这仍然是失败的,想一想——尽管要确保你真的想走这条路(也就是说,你已经用尽了其他的选择)

关于全球人:不要那样做。如果您想创建一个状态为的对象,在您的情况下可以使用类(这就是它们的用途)或者闭包。全局代码几乎从来都不是你想要的(我想在我所有的python写作中,我至少有一个合法的用法,那就是在pyfftw的缓存代码中)。我建议你读书。Matlab是一种蹩脚的语言——其众多原因之一是其蹩脚的范围界定工具,这往往会导致坏习惯

仅当要全局修改引用时才需要全局。我建议阅读更多关于python中的变量以及哪些变量的内容

FFTW
对象带有您需要的所有数组,因此您无需单独传递它们。无论是设置还是返回值,使用调用接口几乎不会带来任何开销(特别是在禁用规范化的情况下)——如果您处于该优化级别,我强烈怀疑您已经达到了极限(我要警告的是,对于许多非常小的FFT来说,这可能并不完全正确,但在这一点上,您需要重新考虑将对FFTW的调用矢量化的算法)。如果您发现每次更新数组(使用调用接口)都会产生大量开销,这是一个bug,您应该提交它(我会非常惊讶)

总之,不要担心每次调用时都会更新数组。这几乎肯定不是您的瓶颈,但请确保您知道规范化,并在需要时禁用它(与原始访问
update\u arrays()
execute()
方法相比,它可能会稍微降低速度)

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

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

关于算法实现的评论: 我不熟悉您正在实现的算法。但是,我对您目前编写的算法有一些评论。 你在每个循环中都在计算
nl_eval(wp)
,但据我所知,这与上一个循环中的
nl_eval(w)
是一样的,所以你不需要计算两次(但这附带一个警告,当你到处都有全局变量时,很难看到发生了什么,所以我可能遗漏了一些东西)

不要为
my\u fft
my\u ifft
中的副本操心。只需执行
fft\u对象(u)
(在我的机器上,正向情况下为2.29毫秒,而在我的机器上为1.67毫秒)。内部数组更新例程使副本变得不必要。此外,正如您编写的那样,您复制了两次:
c[:]
意味着“复制到数组中
c
”,而要复制到
c
中的数组是
v.copy()
,即
v
的一个副本(总共两个副本)

更明智的做法(可能是必要的)是将输出复制到保留数组中(因为这样可以避免在调用FFTW对象时破坏临时结果),不过要确保保留数组
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