【问题标题】:Is there any way to compute 1D FFT of 2D FFT in another dimension without transposition using intel mkl?有没有办法在另一个维度上计算 2D FFT 的 1D FFT 而无需使用 intel mkl 进行转置?
【发布时间】:2019-03-07 03:33:34
【问题描述】:

我想使用 mkl 计算存储为一维数组的二维数组的一维 FFT。 例如,

for (int j=0; j<NJ; j++) //rows
{
  for (int i=0; i<NI; i++) //columns
   {
     Pre_2D_array[i+j*NI].x=1.0;
     Pre_2D_array[i+j*NI].y=2.0;
   }
}

我想在行维度上计算 Pre_2D_array 的一维 FFT。我能想到的唯一方法是重塑数组并像这样进行 FFT,

   for (int i=0; i<NI; i++) //columns
    {
      for (int j=0; j<NJ; j++) //rows
       {
         2D_array[j+i*NJ]=Pre_2D_array[i+j*NI];
       }
    }

DFTI_DESCRIPTOR_HANDLE desc_x = 0;
DftiCreateDescriptor(&desc_x, DFTI_PREC, DFTI_COMPLEX, 1, NJ);
DftiSetValue(desc_x, DFTI_NUMBER_OF_TRANSFORMS, NI);
DftiSetValue(desc_x, DFTI_INPUT_DISTANCE,  NJ);
DftiCommitDescriptor(desc_x);

DftiComputeForward(desc_x, 2D_array);

尽管这样可以得到正确的答案。但是当数组很大时,对原始数组进行转置(重塑)会浪费太多时间。 有没有办法在不重塑阵列的情况下进行 FFT?或者有什么快速的方法来尽可能快地重塑数组?

cpuinfo 是:

processor   : 0
vendor_id   : GenuineIntel
cpu family  : 6
model       : 79
model name  : Intel(R) Xeon(R) CPU E5-2648L v4 @ 1.80GHz
stepping    : 1
microcode   : 0xb000022
cpu MHz     : 1795.882
cache size  : 35840 KB
physical id : 0
siblings    : 14
core id     : 0
cpu cores   : 14
apicid      : 0
initial apicid  : 0
fpu     : yes
fpu_exception   : yes
cpuid level : 20
wp      : yes
flags       : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch arat epb xsaveopt pln pts dtherm tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm rdseed adx smap
bogomips    : 3591.76
clflush size    : 64
cache_alignment : 64
address sizes   : 46 bits physical, 48 bits virtual
power management:

【问题讨论】:

    标签: c arrays fft intel-mkl


    【解决方案1】:

    FFTW 库在 fftw_plan_many_dft() 等函数中引入了参数 istrideostride 以避免转置数组。该页面上的最后一个示例是第二维上的 DFT。

    同样,英特尔数学内核库引入了data layout configuration parameters,例如DFTI_INPUT_STRIDES and DFTI_OUTPUT_STRIDESDFTI_NUMBER_OF_TRANSFORMS

    第二维上的 DFT 可能看起来像(我没有测试过):

    DftiCreateDescriptor(&desc_x, DFTI_PREC, DFTI_COMPLEX, 1, NJ);
    DftiSetValue(desc_x, DFTI_NUMBER_OF_TRANSFORMS, NI);
    DftiSetValue(desc_x, DFTI_INPUT_STRIDES, &NI);
    DftiSetValue(desc_x, DFTI_OUTPUT_STRIDES, &NI);
    DftiSetValue(desc_x, DFTI_INPUT_DISTANCE,  1);
    DftiSetValue(desc_x, DFTI_OUTPUT_DISTANCE,  1);
    DftiCommitDescriptor(desc_x);
    

    DFTI_OUTPUT_STRIDES 对于就地转换 (DFTI_PLACEMENT=DFTI_INPLACE) 会被忽略。

    【讨论】:

    • 如果我根据您的代码设置 desc_x,它将返回“Segment fault”。而且我找不到问题。
    • 查看文档,我只是认为DFTI_INPUT_STRIDES 应该是一个整数数组。你能试试DftiSetValue(desc_x, DFTI_INPUT_STRIDES, &amp;NI);DftiSetValue(desc_x, DFTI_OUTPUT_STRIDES, &amp;NI);吗?
    • 哦,我忘了!你的回答对我真的很有效!谢谢你。 @弗朗西斯
    【解决方案2】:

    据我所知,英特尔 MKL 不提供对数据元素之间具有跨度的数据执行 FFT 的能力。

    但是,FFTW 可以。每4.4.1 Advanced Complex DFTs of the FFTW documentation

    fftw_plan fftw_plan_many_dft(int rank, const int *n, int howmany,
                                 fftw_complex *in, const int *inembed,
                                 int istride, int idist,
                                 fftw_complex *out, const int *onembed,
                                 int ostride, int odist,
                                 int sign, unsigned flags);
    

    此例程计划多个多维复 DFT,并且它 扩展 fftw_plan_dft 例程(请参阅复杂 DFT)以计算 多少个变换,每个变换都有等级和大小n。此外, 变换数据不必是连续的,但它可以布置在 具有任意步幅的内存。考虑到这些可能性, fftw_plan_many_dft 添加新参数 howmany, {i,o}nembed, {i,o}stride{i,o}dist。 FFTW 基本接口(参见复杂 DFTs) 提供了专门用于等级 1、2 和 3 的例程,但是 高级界面只处理一般等级的情况。

    howmany 是要计算的(非负)变换数。这 结果计划计算howmany 变换,其中 第 k 次变换位于in+k*idist 位置(在 C 指针算法中), 其输出位于out+k*odist 位置。在此获得的计划 方式通常比多次调用 FFTW 更快 个别变换。基本fftw_plan_dft接口对应 到howmany=1(在这种情况下, dist 参数被忽略)。

    每个howmany 变换都有等级等级和大小n,如 基本界面。此外,高级界面允许输入 并且每个变换的输出数组是 更大的秩数组,由inembedonembed 描述 参数,分别。 {i,o}nembed 必须是长度数组 rankn 应按元素小于或等于 {i,o}nembed。为 nembed 参数传递 NULL 是等效的 传递n(即相同的物理和逻辑维度,如 基本界面。)

    stride 参数表示输入的j-th 元素或 输出数组分别位于j*istridej*ostride。 (对于多维数组,j 是普通的行主索引。) 当在howmany 循环中与k-th 转换结合使用时,从 上面,这意味着第 (j,k) 个元素位于j*stride+k*dist。 (基本的fftw_plan_dft接口对应步幅为1。)

    对于就地转换,输入和输出步幅和距离 参数应该相同;否则,规划者可能会返回 NULL.

    该页面方便地提供了一个(有些令人困惑的)对二维数组的列执行一维 FFT 的示例:

    将二维数组的每一列转换为 10 行 3 列:

       int rank = 1; /* not 2: we are computing 1d transforms */
       int n[] = {10}; /* 1d transforms of length 10 */
       int howmany = 3;
       int idist = odist = 1;
       int istride = ostride = 3; /* distance between two elements in 
                                     the same column */
       int *inembed = n, *onembed = n;
    

    有关更多示例,另请参阅How do I use fftw_plan_many_dft on a transposed array of data?

    【讨论】:

    • 感谢您提供另一种解决方法。但是现在只想用MKL来解决。 @francis 表明,英特尔 MKL 还提供了在数据中执行 FFT 的功能,数据元素之间有一个跨步。
    【解决方案3】:

    您无法沿数据的更高维度进行一维 FFT。您需要先进行转置,以使 FT 维度成为数据在 RAM 中连续的维度。

    然而,它并没有你想象的那么糟糕。在多核机器上,您可以轻松设置一些线程,其唯一工作是预先/后排列 FT 数据。

    【讨论】:

    • 感谢您的回答。是否有任何函数可以进行转置而不是使用“for(int i=0;...)”来进行循环。
    • 你的矩阵有多大?
    • 我的矩阵大小是 4096*2048。我想在 2048 上做一维 FFT,但矩阵首先按 4096 维存储。
    • 太棒了,cat /proc/cpuinfo 说了什么?
    • CPU 为 E5-2648L v4 14 核。但是我只能在一个核心上使用一个线程来计算它。因为其他核心也有同样的使命。
    猜你喜欢
    • 2012-07-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-18
    • 2021-12-18
    • 1970-01-01
    相关资源
    最近更新 更多