【问题标题】:How could I make FFTW Hilbert transform calculate faster?我怎样才能使 FFTW 希尔伯特变换计算得更快?
【发布时间】:2020-12-22 21:11:40
【问题描述】:

我正在使用来自 FFTW 源的希尔伯特变换函数。 因为我打算在数据流模式下在我的 DAQ 中使用它。 该函数工作正常,但计算速度慢,会导致 FIFO 溢出。 我听说将fftw_plan()hilbert() 移到外部以重用plan 可能很有用,但是,一旦我这样做,就会出错,在fftw_destroy_plan(plan); 上说Exception thrown at 0x0000000070769240 (libfftw3-3.dll) in CppFFTW.exe: 0xC0000005: Access violation reading location 0x0000000000000000.。有没有人有类似的经验或更好的解决方案来提高hilbert() 的计算?

这是我尝试过的(2020 年 12 月 26 日已编辑):

#include <iostream>
#include <fftw3.h>
#include <time.h>


using namespace std;

//macros for real and imaginary parts
#define REAL 0
#define IMAG 1
//length of complex array
#define N 8

fftw_complex* out;
fftw_plan plan;


void hilbert(const double* in, fftw_complex* out)
{
    // copy the data to the complex array
    for (int i = 0; i < N; ++i) {
        out[i][REAL] = in[i];
        out[i][IMAG] = 0;
    }
    // creat a DFT plan and execute it
    //fftw_plan plan = fftw_plan_dft_1d(N, out, out, FFTW_FORWARD, FFTW_ESTIMATE);
    fftw_execute(plan);
    // destroy a plan to prevent memory leak
    //fftw_destroy_plan(plan);
    int hN = N >> 1; // half of the length (N/2)
    int numRem = hN; // the number of remaining elements
    // multiply the appropriate value by 2
    //(those should multiplied by 1 are left intact because they wouldn't change)
    for (int i = 1; i < hN; ++i) {
        out[i][REAL] *= 2;
        out[i][IMAG] *= 2;
    }
    // if the length is even, the number of the remaining elements decrease by 1
    if (N % 2 == 0)
        numRem--;
    else if (N > 1) {
        out[hN][REAL] *= 2;
        out[hN][IMAG] *= 2;
    }
    // set the remaining value to 0
    // (multiplying by 0 gives 0, so we don't care about the multiplicands)
    memset(&out[hN + 1][REAL], 0, numRem * sizeof(fftw_complex));
    // creat a IDFT plan and execute it
    //plan = fftw_plan_dft_1d(N, out, out, FFTW_BACKWARD, FFTW_ESTIMATE);
    fftw_execute(plan);
    // do some cleaning
    //fftw_destroy_plan(plan);
    //fftw_cleanup();
    // scale the IDFT output
    for (int i = 0; i < N; ++i) {
        out[i][REAL] /= N;
        out[i][IMAG] /= N;
    }



   




}
/* Displays complex numbers in the form a +/- bi. */
void displayComplex(fftw_complex* y)
{
    for (int i = 0; i < N; i++) {
        if (y[i][IMAG] < 0)
            cout << y[i][REAL] << "-" << abs(y[i][IMAG]) << "i" << endl;
        else
            cout << y[i][REAL] << "+" << y[i][IMAG] << "i" << endl;
    }
}



/* Test */
int main()
{
    fftw_plan plan = fftw_plan_dft_1d(N, out, out, FFTW_FORWARD, FFTW_ESTIMATE);
    plan = fftw_plan_dft_1d(N, out, out, FFTW_BACKWARD, FFTW_ESTIMATE);
    

    
    // input array
    double x[N];
    // output array
    fftw_complex y[N];
    // fill the first of some numbers
    for (int i = 0; i < N; ++i) {
        x[i] = i + 1;  // i.e.{1 2 3 4 5 6 7 8}
    }
    //clock_t begin = clock();
    // compute the FFT of x and store the result in y.
    hilbert(x, y);
    // display the result
    cout << "Hilbert =" << endl;
    displayComplex(y);
    fftw_destroy_plan(plan);
    fftw_destroy_plan(plan);
}

这里是原始代码:

#include <iostream>
#include <fftw3.h>


using namespace std;

//macros for real and imaginary parts
#define REAL 0
#define IMAG 1
//length of complex array
#define N 8

void hilbert(const double* in, fftw_complex* out)
{
    // copy the data to the complex array
    for (int i = 0; i < N; ++i) {
        out[i][REAL] = in[i];
        out[i][IMAG] = 0;
    }
    // creat a DFT plan and execute it
    fftw_plan plan = fftw_plan_dft_1d(N, out, out, FFTW_FORWARD, FFTW_ESTIMATE);
    fftw_execute(plan);
    // destroy a plan to prevent memory leak
    fftw_destroy_plan(plan);
    int hN = N >> 1; // half of the length (N/2)
    int numRem = hN; // the number of remaining elements
    // multiply the appropriate value by 2
    //(those should multiplied by 1 are left intact because they wouldn't change)
    for (int i = 1; i < hN; ++i) {
        out[i][REAL] *= 2;
        out[i][IMAG] *= 2;
    }
    // if the length is even, the number of the remaining elements decrease by 1
    if (N % 2 == 0)
        numRem--;
    else if (N > 1) {
        out[hN][REAL] *= 2;
        out[hN][IMAG] *= 2;
    }
    // set the remaining value to 0
    // (multiplying by 0 gives 0, so we don't care about the multiplicands)
    memset(&out[hN + 1][REAL], 0, numRem * sizeof(fftw_complex));
    // creat a IDFT plan and execute it
    plan = fftw_plan_dft_1d(N, out, out, FFTW_BACKWARD, FFTW_ESTIMATE);
    fftw_execute(plan);
    // do some cleaning
    fftw_destroy_plan(plan);
    fftw_cleanup();
    // scale the IDFT output
    for (int i = 0; i < N; ++i) {
        out[i][REAL] /= N;
        out[i][IMAG] /= N;
    }



   




}
/* Displays complex numbers in the form a +/- bi. */
void displayComplex(fftw_complex* y)
{
    for (int i = 0; i < N; i++) {
        if (y[i][IMAG] < 0)
            cout << y[i][REAL] << "-" << abs(y[i][IMAG]) << "i" << endl;
        else
            cout << y[i][REAL] << "+" << y[i][IMAG] << "i" << endl;
    }
}



/* Test */
int main()
{
    
 
    // input array
    double x[N];
    // output array
    fftw_complex y[N];
    // fill the first of some numbers
    for (int i = 0; i < N; ++i) {
        x[i] = i + 1;  // i.e.{1 2 3 4 5 6 7 8}
    }
    // compute the FFT of x and store the result in y.
    hilbert(x, y);
    // display the result
    cout << "Hilbert =" << endl;
    displayComplex(y);
 
}

准确的输出

Hilbert =
1+3.82843i
2-1i
3-1i
4-1.82843i
5-1.82843i
6-1i
7-1i
8+3.82843i

【问题讨论】:

  • 也许您可以通过在您的陈述中将问题重新集中在这一部分上来回答这个问题:“但是,一旦我这样做了,它就会出现问题。”。 FFTW 的文档确切地说要重新使用 fftw_plan。那么,您尝试过什么?您看到的“错误”效果是什么?
  • 已编辑,之所以这么说是因为我不确定我的方法是否正确。
  • 为什么要覆盖一个干净的结构?代替out[i][REAL],执行:out[i].re。我什至不确定它们是否等效 [yours might be UB] 并且 .re 是结构的定义方式和 lib 的期望。同样适用于out[i].im
  • 改几个字母有用吗...?
  • out 是一个指针,因此通过out[i][anything] 访问它是错误的。我很惊讶它编译了,因为没有办法告诉编译器行数。它 is UB 并且您正在访问 超出 数组的末尾。当你这样做时:out[i][1]设置out[i].im 部分。你在破坏out[i + 1]。最终,这会导致您在 [stack] 数组的末尾乱涂乱画——这就是 UB [未定义的行为]。

标签: c++ signal-processing fftw


【解决方案1】:

为了速度,如果可能,您肯定希望重用 FFTW 计划。在多次调用 hilbert 重用计划时,请删除 hilbert 中的 fftw_destroy_plan(plan) 行,否则该计划将无法在下一次调用中使用。相反,在程序结束时破坏计划。

编辑:我之前错过了您使用两个计划,一个用于向前转换,另一个用于向后转换。在hilbert() 中,删除对fftw_plan_dft_1dfftw_destroy_planfftw_cleanup 的所有调用;唯一的 FFTW 呼叫应该是 fftw_execute。相反,在程序开始时预先创建两个计划,并在程序结束时销毁它们。

【讨论】:

  • 谢谢!你的意思是我必须删除 2 行 fftw_destroy_plan(plan)
  • 糟糕,我之前错过了您使用两个 FFTW 计划。你是对的,两条破坏线都应该被删除。请参阅我的帖子上的编辑。
  • 你的意思是plan = fftw_plan_dft_1d(N, out, out, FFTW_BACKWARD, FFTW_ESTIMATE); 吗?为了更好地理解,你能给我一个编辑过的脚本来显示哪些部分需要删除,哪些部分需要移动到hilbert()之外,哪些部分还需要留在函数中?非常感谢!
  • 请看我的帖子,我试过你说的,但还是出现类似的错误,你能指出我哪里做错了吗?谢谢!
  • 看你更新的代码,计划创建和销毁命令已经不在hilbert;那挺好的。现在缺少的是需要在开始时创建这些计划或main。当然,尝试执行计划而不初始化它是行不通的。要创建计划,请将两个fftw_plan 变量声明为全局变量(两个计划:一个用于前向,另一个用于后向)并使用fftw_plan_dft_1d 来初始化它们中的每一个。然后在main结束时,对他们每个人调用fftw_destroy_plan进行清理。
【解决方案2】:

经过多次尝试,这里是成功的FFTWhilbert()改进。 移出两个fftw_plan,让它们在main()中初始化,另外fftw_destroy_plan也被移走了。

#include <iostream>
#include <fftw3.h>
#include <time.h>


using namespace std;

//macros for real and imaginary parts
#define REAL 0
#define IMAG 1
//length of complex array
#define N 8
fftw_complex y[N];



void hilbert(const double* in, fftw_complex* out, fftw_plan plan_forward, fftw_plan plan_backward)
{
    // copy the data to the complex array
    for (int i = 0; i < N; ++i) {
        out[i][REAL] = in[i];
        out[i][IMAG] = 0;
    }
    // creat a DFT plan and execute it
    //fftw_plan plan = fftw_plan_dft_1d(N, out, out, FFTW_FORWARD, FFTW_ESTIMATE);
    fftw_execute(plan_forward);
    // destroy a plan to prevent memory leak
    //fftw_destroy_plan(plan_forward);
    int hN = N >> 1; // half of the length (N/2)
    int numRem = hN; // the number of remaining elements
    // multiply the appropriate value by 2
    //(those should multiplied by 1 are left intact because they wouldn't change)
    for (int i = 1; i < hN; ++i) {
        out[i][REAL] *= 2;
        out[i][IMAG] *= 2;
    }
    // if the length is even, the number of the remaining elements decrease by 1
    if (N % 2 == 0)
        numRem--;
    else if (N > 1) {
        out[hN][REAL] *= 2;
        out[hN][IMAG] *= 2;
    }
    // set the remaining value to 0
    // (multiplying by 0 gives 0, so we don't care about the multiplicands)
    memset(&out[hN + 1][REAL], 0, numRem * sizeof(fftw_complex));
    // creat a IDFT plan and execute it
    //plan = fftw_plan_dft_1d(N, out, out, FFTW_BACKWARD, FFTW_ESTIMATE);
    fftw_execute(plan_backward);
    // do some cleaning
    //fftw_destroy_plan(plan_backward);
    //fftw_cleanup();
    // scale the IDFT output
    for (int i = 0; i < N; ++i) {
        out[i][REAL] /= N;
        out[i][IMAG] /= N;
    }








}
/* Displays complex numbers in the form a +/- bi. */
void displayComplex(fftw_complex* y)
{
    for (int i = 0; i < N; i++) {
        if (y[i][IMAG] < 0)
            cout << y[i][REAL] << "-" << abs(y[i][IMAG]) << "i" << endl;
        else
            cout << y[i][REAL] << "+" << y[i][IMAG] << "i" << endl;
    }
}



/* Test */
int main()
{
    // input array
    double x[N];
    // output array
    //fftw_complex y[N];
    fftw_plan plan_forward = fftw_plan_dft_1d(N, y, y, FFTW_FORWARD, FFTW_ESTIMATE);
    fftw_plan plan_backward = fftw_plan_dft_1d(N, y, y, FFTW_BACKWARD, FFTW_ESTIMATE);
    



   
    // fill the first of some numbers
    for (int i = 0; i < N; ++i) {
        x[i] = i + 1;  // i.e.{1 2 3 4 5 6 7 8}
    }
    // compute the FFT of x and store the result in y.
    hilbert(x, y, plan_forward, plan_backward);
    
    // display the result
    cout << "Hilbert =" << endl;
    
    displayComplex(y);
    fftw_destroy_plan(plan_forward);
    fftw_destroy_plan(plan_backward);
   
}

【讨论】:

    【解决方案3】:

    由我的顶级 cmets 介绍 ...

    好吧……经过几次错误的开始……

    您的第二个版本有一些问题。

    值得注意的是,您没有分配out

    另外,为了安全起见,我相信您需要两个 fftw_plan 结构,一个用于前进,一个用于后退。

    这些应该在main 中分配/初始化一次

    并且,删除hilbert 中的所有分配/销毁调用。这样,只需更改out中的数据即可多次调用。

    清理代码可以到main底部。

    我已经为您的第二个版本创建了一个清理后的工作版本。我用cpp 条件展示了旧代码和新代码的区别:

    #if 0
    // old code ...
    #else
    // new code ...
    #endif
    

    所以,这里是:

    #include <iostream>
    #include <cstring>
    #include <fftw3.h>
    #include <time.h>
    
    using namespace std;
    
    //macros for real and imaginary parts
    #define REAL 0
    #define IMAG 1
    
    //length of complex array
    #define N 8
    
    fftw_plan plan_fwd;
    fftw_plan plan_bwd;
    fftw_complex *out;
    
    void
    hilbert(const double *in, fftw_complex *out)
    {
        // copy the data to the complex array
        for (int i = 0; i < N; ++i) {
            out[i][REAL] = in[i];
            out[i][IMAG] = 0;
        }
    
        // creat a DFT plan and execute it
        // fftw_plan plan = fftw_plan_dft_1d(N, out, out, FFTW_FORWARD, FFTW_ESTIMATE); // This line has been moved to the main function
    
        fftw_execute(plan_fwd);
    
        // destroy a plan to prevent memory leak
    #if 0
        fftw_destroy_plan(plan_fwd);
    #endif
        int hN = N >> 1;                    // half of the length (N/2)
        int numRem = hN;                    // the number of remaining elements
    
        // multiply the appropriate value by 2
        // (those should multiplied by 1 are left intact because they wouldn't change)
        for (int i = 1; i < hN; ++i) {
            out[i][REAL] *= 2;
            out[i][IMAG] *= 2;
        }
    
        // if the length is even, the number of the remaining elements decrease by 1
        if (N % 2 == 0)
            numRem--;
        else if (N > 1) {
            out[hN][REAL] *= 2;
            out[hN][IMAG] *= 2;
        }
    
        // set the remaining value to 0
        // (multiplying by 0 gives 0, so we don't care about the multiplicands)
        memset(&out[hN + 1][REAL], 0, numRem * sizeof(fftw_complex));
        // creat a IDFT plan and execute it
    #if 0
        plan_bwd = fftw_plan_dft_1d(N, out, out, FFTW_BACKWARD, FFTW_ESTIMATE);
    #endif
        fftw_execute(plan_bwd);
        // do some cleaning
    #if 0
        fftw_destroy_plan(plan_bwd);
        fftw_cleanup();
    #endif
    
        // scale the IDFT output
        for (int i = 0; i < N; ++i) {
            out[i][REAL] /= N;
            out[i][IMAG] /= N;
        }
    }
    
    /* Displays complex numbers in the form a +/- bi. */
    void
    displayComplex(fftw_complex * y)
    {
        for (int i = 0; i < N; i++) {
            if (y[i][IMAG] < 0)
                cout << y[i][REAL] << "-" << abs(y[i][IMAG]) << "i" << endl;
            else
                cout << y[i][REAL] << "+" << y[i][IMAG] << "i" << endl;
        }
    }
    
    /* Test */
    int
    main()
    {
    
    #if 1
        out = (fftw_complex *) malloc(sizeof(fftw_complex) * N);
    #endif
    
        plan_fwd = fftw_plan_dft_1d(N, out, out, FFTW_FORWARD, FFTW_ESTIMATE);
        plan_bwd = fftw_plan_dft_1d(N, out, out, FFTW_BACKWARD, FFTW_ESTIMATE);
    
        // input array
        double x[N];
    
        // output array
        fftw_complex y[N];
    
        // fill the first of some numbers
        for (int i = 0; i < N; ++i) {
            x[i] = i + 1;                   // i.e.{1 2 3 4 5 6 7 8}
        }
        clock_t begin = clock();
    
        // compute the FFT of x and store the result in y.
        hilbert(x, y);
        // display the result
        cout << "Hilbert =" << endl;
        displayComplex(y);
        clock_t end = clock();
        double time_spent = (double) (end - begin) / CLOCKS_PER_SEC;
    
        printf("%f", time_spent);
    
        fftw_destroy_plan(plan_fwd);
        fftw_destroy_plan(plan_bwd);
        fftw_cleanup();
    }
    

    这是程序输出:

    Hilbert =
    0.125+0i
    0.5+0i
    0.75+0i
    1+0i
    0.625+0i
    0+0i
    0+0i
    0+0i
    0.000171
    

    【讨论】:

    • 感谢 Craig,但输出的答案不正确
    • 抱歉迟到了评论,我认为您提出的内容几乎符合概念,但是输出答案是错误的。但我认为我们可以根据您的代码不断改进。顺便说一句,我不是投反对票的人;)
    • 我更新了问题部分的实际输出值,请看一下。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-11
    • 2019-10-16
    • 1970-01-01
    • 2020-04-06
    • 2011-06-14
    • 2018-11-26
    相关资源
    最近更新 更多