Numpy 维度如何影响pyfftw中的性能?

Numpy 维度如何影响pyfftw中的性能?,numpy,scipy,fft,fftw,pyfftw,Numpy,Scipy,Fft,Fftw,Pyfftw,我正在尝试使用FFT和pyfftw实现3d卷积。我在另一篇文章中使用了一个代码作为基础,因此: class CustomFFTConvolution(object): def __init__(self, A, B, threads=1): shape = (np.array(A.shape) + np.array(B.shape))-1 #shape=np.array(A.shape) - np.array(B.shape)+1 if np.iscomplexobj

我正在尝试使用FFT和pyfftw实现3d卷积。我在另一篇文章中使用了一个代码作为基础,因此:

class CustomFFTConvolution(object):

def __init__(self, A, B, threads=1):

    shape = (np.array(A.shape) + np.array(B.shape))-1
    #shape=np.array(A.shape) - np.array(B.shape)+1
    if np.iscomplexobj(A) and np.iscomplexobj(B):
        self.fft_A_obj = pyfftw.builders.fftn(
                A, s=shape, threads=threads)
        self.fft_B_obj = pyfftw.builders.fftn(
                B, s=shape, threads=threads)
        self.ifft_obj = pyfftw.builders.ifftn(
                self.fft_A_obj.get_output_array(), s=shape,
                threads=threads)

    else:
        self.fft_A_obj = pyfftw.builders.rfftn(
                A, s=shape, threads=threads)
        self.fft_B_obj = pyfftw.builders.rfftn(
                B, s=shape, threads=threads)
        self.ifft_obj = pyfftw.builders.irfftn(
                self.fft_A_obj.get_output_array(), s=shape,
                threads=threads)

def __call__(self, A, B):
    s1=np.array(A.shape)
    s2=np.array(B.shape)

    fft_padded_A = self.fft_A_obj(A)
    fft_padded_B = self.fft_B_obj(B)

    ret= self.ifft_obj(fft_padded_A * fft_padded_B)

    return self._centered(ret, s1 - s2 + 1)

def _centered(self,arr, newshape):
    # Return the center newshape portion of the array.
    newshape = np.asarray(newshape)
    currshape = np.array(arr.shape)
    startind = (currshape - newshape) // 2
    endind = startind + newshape
    myslice = [slice(startind[k], endind[k]) for k in range(len(endind))]
    return arr[tuple(myslice)]
我的数据A的形状是(931411806),我的过滤器B的形状是(32,32,32)。如果我在一台24核的机器上使用24个线程运行此代码,操作需要263秒。 现在,如果我在同一台机器上运行相同的实验,但是这次A的形状是(806411931),只是轴的交换,代码只需要16秒。这是什么原因? 是否有获得最佳性能的经验法则?也许填充其中一个维度?
谢谢

由于考虑了填充,填充的大小是否可以增加到偶数,或者小素数的倍数?选择偶数大小可以将挂钟时间除以3

根据尺寸,某些DFT算法可能不可用或效率不高。 例如,执行DFT的最有效算法之一是。它包括将复合大小N=N1*N2的信号的DFT划分为大小N2的N1 DTF。因此,它对通过乘以小的素因子(2、3、5、7)获得的复合尺寸更有效。FFTW中提供了专用的高效算法。从:

例如,标准FFTW分布对于大小可以分解为小素数(2、3、5和7)的数组最有效,否则它使用较慢的通用例程。如果您需要其他大小的有效转换,可以使用FFTW的代码生成器,它为您可能关心的任何特定数组大小生成快速C程序(“Codelet”)。例如,如果需要大小为513=19*33的变换,可以自定义FFTW以有效地支持因子19

您的填充尺寸具有较高的基本要素:

931=>962=2*13*37
411=>442=2*13*17
806=>837=3*3*3*31
填充可以扩展到更接近具有小素数的数字,例如980、448和864。然而,填充3D图像会导致内存占用的显著增加,以至于不可能总是这样

为什么更改维度顺序会改变计算时间? 差异可能是由于输入数组是实的。因此,对其中一个维度执行R2C DFT,然后对第二个维度和第三个维度执行C2C DFT,以计算3D DFT。如果要变换的第一个维度的大小为偶数,则R2C变换可以转化为大小为一半的复数DFT,如图所示。这个技巧不适用于奇数大小。因此,当962和837被翻转时,一些快速算法可能变得可用

下面是测试它的代码:

import pyfftw
import matplotlib.pyplot as plt
import multiprocessing
import numpy as np
from timeit import default_timer as timer

def listofgoodsizes():
    listt=[]
    p2=2
    for i2 in range(11):
        p3=1
        for i3 in range(7):
            p5=1
            for i5 in range(2):

                listt.append(p2*p3*p5)
                p5*=5
            p7=1
            for i7 in range(2):
                listt.append(p2*p3*p7)
                p7*=7

            p3*=3
        p2*=2
    listt.sort()
    return listt

def getgoodfftwsize(n,listt):
    for i in range(len(listt)):
        if listt[i]>=n:
            return listt[i]
    return n

def timea3DR2CDFT(n,m,p):
    bb = pyfftw.empty_aligned((n,m, p), dtype='float64')
    bf= pyfftw.empty_aligned((n,m, (p/2+1)), dtype='complex128')
    pyfftw.config.NUM_THREADS = 1 #multiprocessing.cpu_count()
    fft_object_b = pyfftw.FFTW(bb, bf,axes=(0,1,2))

    print n,m,p
    start = timer()
    fft_object_b(bb)
    end = timer()
    print end - start

#three prime numbers !      
n=3*37
m=241
p=5*19

timea3DR2CDFT(n,m,p)



# to even size :
neven=2*((n+1)/2)
meven=2*((m+1)/2)
peven=2*((p+1)/2)

timea3DR2CDFT(neven,meven,peven)


#to nearest multiple of prime
listt=listofgoodsizes()

ngood=getgoodfftwsize(n,listt)
mgood=getgoodfftwsize(m,listt)
pgood=getgoodfftwsize(p,listt)

timea3DR2CDFT(ngood,mgood,pgood)
在我的计算机上,它打印:

111 241 95
0.180601119995
112 242 96
0.0560319423676
112 252 96
0.0564918518066

您是否可以像以前那样使用交换的轴进行尝试,但将第一个维度缩短一个?也就是说,尝试使用
(805411931)
的形状,看看它是否再次变慢。我有点怀疑第一维度被
2**n
整除可能会加快速度,这取决于
n
的大小。形状绝对是速度的关键!试着在每个维度上使用2的幂,这会给你最大的速度。我想,如果选择一个接近您想要的快速大小,然后切掉输出,而不是选择一个奇怪的大小,您可能会是最快的。这部分毫无意义:
shape=(np.array(a.shape)+np.array(B.shape))-1
。您应该将较小的阵列填充到较大阵列的形状上。也不需要填充较大的阵列。作为奖励,您不需要削减结果。这就是说,如果您的FFT在24核上运行需要263秒,那么您确实做错了。我预计这样一个图像的FFT在单个核上运行最多需要几秒钟。