在多线程的OOP中使用FFTW



考虑一个二维复杂值数组的容器

#include <vector>
#include <array>
struct Array2D {
typedef std::array<double, 2> complex;
int X, Y;
std::vector<complex> values;
};

和一个使用FFTW计算傅里叶变换的函数:

#include <fftw3.h>
void FourierTransform(const Array2D& source, Array2D *dest) {
fftw_plan p = fftw_plan_dft_2d(source.X, source.Y, (fftw_complex*)source.values.data(), (fftw_complex*)dest->values.data(), FFTW_FORWARD, FFTW_ESTIMATE);
fftw_execute(p);
fftw_destroy_plan(p);
}

这不是一个最佳的实现,因为每次计算傅里叶变换时它都会创建和破坏一个计划,但它很方便且易于使用。然而,创建和销毁FFTW计划不是线程安全的(见这里),所以这个函数不应该从不同的线程同时调用。

问题:

  • 我的应用程序中有许多不同的数组,我需要计算傅里叶变换和
  • #pragma omp parallel for的主循环并行化和代码中需要计算傅里叶变换的地方之间有几个函数调用。

#pragma omp parallel for之前初始化所有这些数组以及fftw计划,然后将它们作为参数传递给后续函数,将使代码变得很多不清晰的。是否有另一种方法可以让我封装和隐藏FFTW函数调用在线程安全的方式?

到FFTW文档的链接给出了一个答案,即确保序列化对FFTW的不安全调用。它建议这样做,使用信号量,但因为你是在OpenMP你可以做到与#pragma omp critical,像这样的东西

void FourierTransform(const Array2D& source, Array2D *dest) {
fftw_plan p;
#pragma omp critical (FFTW)
{
p = fftw_plan_dft_2d(source.X, source.Y, 
(fftw_complex*)source.values.data(),
(fftw_complex*)dest->values.data(), 
FFTW_FORWARD, FFTW_ESTIMATE);
}
fftw_execute(p);
#pragma omp critical (FFTW)
{
fftw_destroy_plan(p);
}
}

我在这里使用了一个命名的临界区(命名为FFTW),这样这些临界区不会减少代码中任何其他关键区域的并行性,而是相互保护。

更一般地说,提供相关函数的包装的、线程安全的版本并调用它们,而不是调用未包装的版本,可能会更好。比如

static fftw_plan_p TS_fftw_plan_dft_2d(... arguments ... ) {
fftw_plan p;
#pragma omp critical (FFTW)
{
p = fftw_plan_dft_2d(... arguments ...);
}
return p;
}

然后您只需调用TS_fftw_foo而不是fftw_foo在您的代码的其余部分。

如果你想更极端一些,你可以把所有这些包装器放在一个头文件中(也把它们标记为内联的),然后用宏把相关的FFTW函数扩展成TS_fftw函数。然后加上你的标题就可以了。然而,这是相当模糊和极端的。只包装你需要的函数可能会更清楚发生了什么。

显然,所有这些代码都是未经测试和编译的,但我希望你能理解。

最新更新