使用OpenMP创建FFTW计划

7

我正在尝试并行执行多个傅里叶变换(FFT)。我正在使用FFTW和OpenMP。每个FFT都不同,因此我不依赖于FFTW的内置多线程(我知道它使用OpenMP)。

int m;

// assume:
// int numberOfColumns = 100;
// int numberOfRows = 100;

#pragma omp parallel for default(none) private(m) shared(numberOfColumns, numberOfRows)//  num_threads(4)
    for(m = 0; m < 36; m++){

        // create pointers
        double          *inputTest;
        fftw_complex    *outputTest;
        fftw_plan       testPlan;

        // preallocate vectors for FFTW
         outputTest = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*numberOfRows*numberOfColumns);
         inputTest  = (double *)fftw_malloc(sizeof(double)*numberOfRows*numberOfColumns);

         // confirm that preallocation worked
         if (inputTest == NULL || outputTest == NULL){
             logger_.log_error("\t\t FFTW memory not allocated on m = %i", m);
         }

         // EDIT: insert data into inputTest
         inputTest = someDataSpecificToThisIteration(m); // same size for all m

        // create FFTW plan
        #pragma omp critical (make_plan)
        {
            testPlan = fftw_plan_dft_r2c_2d(numberOfRows, numberOfColumns, inputTest, outputTest, FFTW_ESTIMATE);
        }

         // confirm that plan was created correctly
         if (testPlan == NULL){
             logger_.log_error("\t\t failed to create plan on m = %i", m);
         }

        // execute plan
         fftw_execute(testPlan);

        // clean up
         fftw_free(inputTest);
         fftw_free(outputTest);
         fftw_destroy_plan(testPlan);

    }// end parallelized for loop

所有的都很好,但是如果我从计划创建周围移除关键结构(fftw_plan_dft_r2c_2d),我的代码将失败。有人能解释一下为什么吗?fftw_plan_dft_r2c_2d不是一个“孤儿”,对吧?是因为两个线程可能同时尝试命中numberOfRowsnumberOfColumns内存位置吗?


你没有使用fftw的多线程功能。 实际上,你正在同时进行36个单线程转换。 - nat chouf
我知道。我在最初的问题中说过:“每个FFT都不同,所以我不依赖FFTW的内置多线程”。我想要并行地进行36个单线程转换。 - tir38
抱歉,我的错误,我读成了完全相反的意思;-) - nat chouf
1个回答

7

关于线程安全,FFTW文档中已经详细说明:

...但是需要注意一些问题,因为规划程序在调用和规划之间共享数据(例如智能数据和三角表)。

总之,FFTW中唯一线程安全(可重入)的例程是 fftw_execute (以及其新数组变体)。所有其他例程(例如规划器)只应从一个线程调用。因此,例如,您可以在对规划程序的任何调用周围包装一个信号量锁定;更简单的是,您可以只从一个线程创建所有计划。我们认为这不应该是一个重要的限制(FFTW是为实际执行变换的性能敏感代码设计的),而且计划之间共享数据的好处很大。

在典型的FFT应用程序中,计划很少被构建,因此如果必须同步它们的创建,那也无所谓。在您的情况下,除非数据的维数发生变化,否则不需要在每次迭代中创建新计划。您应该采取以下措施:

#pragma omp parallel default(none) private(m) shared(numberOfColumns, numberOfRows)
{
   // create pointers
   double          *inputTest;
   fftw_complex    *outputTest;
   fftw_plan       testPlan;

   // preallocate vectors for FFTW
   outputTest = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*numberOfRows*numberOfColumns);
   inputTest  = (double *)fftw_malloc(sizeof(double)*numberOfRows*numberOfColumns);

   // confirm that preallocation worked
   if (inputTest == NULL || outputTest == NULL){
      logger_.log_error("\t\t FFTW memory not allocated on m = %i", m);
   }

   // create FFTW plan
   #pragma omp critical (make_plan)
   testPlan = fftw_plan_dft_r2c_2d(numberOfRows, numberOfColumns, inputTest, outputTest, FFTW_ESTIMATE);

   #pragma omp for
   for (m = 0; m < 36; m++) {
      // execute plan
      fftw_execute(testPlan);
   }

   // clean up
   fftw_free(inputTest);
   fftw_free(outputTest);
   fftw_destroy_plan(testPlan);
}

现在计划只会在每个线程中创建一次,而串行化开销会随着每次执行 fftw_execute() 的减少而减少。如果在 NUMA 系统上运行 (例如多插槽的 AMD64 或 Intel (post-)Nehalem 系统),则应启用线程绑定以实现最大性能。


我刚刚读了一下手册的那部分……回来回答我的问题时看到了你的问题。你得到了这个点赞。你说“除非数据的维度发生变化”,但如果维度相同但值不同怎么办?我更新了我的原始问题以反映这一点。 - tir38
@tir38,这就是为什么你要多次执行计划的原因,不是吗?只要重复使用输入和输出数组,单个计划就可以了。只是不要像那样给inputTest赋值,因为它是一个指针。你最好有类似someDataSpecificToThisIteration(m, inputData)的东西,并将函数的输出放入inputData中。 - Hristo Iliev
抱歉,我再次强调一下。我指的是someDataSpecificToThisIteration[m]。这不是一个方法调用,而是从某个通用数组中提取数据。因此,我只需要将inputData指向该数据即可。由于我实际上有36个指针指向36个数组条目,所以我需要36个计划,对吗? - tir38
1
@tir38,不需要。你只需要稍微不同地执行每个计划即可。请参见这里 - Hristo Iliev
你可以使用 fftw_execute_dft_r2c。在这里,你可以给输入和输出提供新的指针。它也是线程安全的。 - Jens Munk
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接