(如何)我可以使用openmp矢量化"std::complex<double>"吗?

(How) Can I vectorize `std::complex<double>` using openmp?

本文关键字:complex lt double gt std 我可以 如何 可以使 矢量化 openmp      更新时间:2023-10-16

我想使用矢量化来优化我的应用程序。更具体地说,我想对std::complex<double>类型的数学运算进行矢量化。然而,这似乎相当困难。请考虑以下示例:

#define TEST_LEN 100
#include <algorithm>
#include <complex>
typedef std::complex<double> cmplx;
using namespace std::complex_literals;
#pragma omp declare simd
cmplx add(cmplx a, cmplx b)
{
return a + b;
}
#pragma omp declare simd
cmplx mult(cmplx a, cmplx b)
{
return a * b;
}
void k(cmplx *x, cmplx *&y, int i0, int N)
{
#pragma omp for simd
for (int i = i0; i < N; i++)
y[i] = add(mult(-(1i + 1.0), x[i]), 1i);
}
int main(int argc, char **argv)
{
cmplx *x = new cmplx[TEST_LEN];
cmplx *y = new cmplx[TEST_LEN];
for (int i = 0; i < TEST_LEN; i++)
x[i] = 0;
for (int i = 0; i < TEST_LEN; i++)
{
int N = std::min(4, TEST_LEN - i);
k(x, y, i, N);
}
delete[] x;
delete[] y;
return 1;
}

我正在使用 g++ 编译器。对于此代码,编译器会发出以下警告:

警告:不支持的 SIMD 返回类型"cmplx"{aka 'std::complex'}

对于包含multadd函数的行。 似乎不可能像这样对std::complex<double>类型进行矢量化。

有没有其他方法

可以存档?

不容易。当接下来的 N 个步骤中的值具有相同的行为方式时,SIMD 可以很好地工作。因此,例如考虑一个 2D 向量数组:

X Y X Y X Y X Y

如果我们在这里做一个向量加法运算,

X Y X Y X Y X Y
+ + + + + + + +
X Y X Y X Y X Y

编译器将很好地矢量化该操作。但是,如果我们想对 X 和 Y 值执行不同的操作,则内存布局对于 SIMD 来说就会出现问题:

X Y X Y X Y X Y
+ / + / + / + /
X Y X Y X Y X Y

例如,如果您考虑乘法情况:

(a + bi) (c + di) = (ac - bd)  (ad + bc)i

突然之间,操作在 SIMD 通道之间跳跃,这几乎会扼杀任何像样的矢量化。

快速浏览一下这个神霹雳:https://godbolt.org/z/rnVVgl 加法归结为一些 vaddps 指令(一次处理 8 个浮子)。 乘法最终使用 vfmadd231ss 和 vmulss(它们一次只能处理 1 个浮点数)。

自动矢量化复杂代码的唯一简单方法是将实部和虚部分成 2 个数组:

struct ComplexArray {
float* real;
float* imaginary;
};

在这个 godbolt 中,您可以看到编译器现在正在使用 vfmadd213ps 指令(因此再次回到一次处理 8 个浮点数)。

https://godbolt.org/z/Ostaax