如何使用 SSE 加载 std::complex 数组的实部?

Posted

技术标签:

【中文标题】如何使用 SSE 加载 std::complex 数组的实部?【英文标题】:How can I load the real parts of an array of std::complex with SSE? 【发布时间】:2014-08-13 08:56:52 【问题描述】:

由于_mm_loadu_ps() Intrinsic 函数,我正在尝试将std::complex<float> 数组内容的实部加载到一个128 位寄存器中。

__m128 data_block;

complex<float> a[4];
a[0] = complex<float>(1.0, 2.0);
a[1] = complex<float>(3.0, 4.0);
a[2] = complex<float>(5.0, 6.0);
a[3] = complex<float>(7.0, 8.0);

data_block = _mm_loadu_ps(&a[0].real());

float b[4];
_mm_storeu_ps(b, data_block);

cout << b[0] << " " << b[1] << " " << b[2] << " " << b[3] << endl;

我得到了输出 1 2 3 4 而不是 1 3 5 7。 这是因为_mm_loadu_ps()函数加载了作为参数给出的地址之后的4个数据。

我知道我可以使用中间数组,但我的问题是: 是否有任何允许执行加载和存储操作的内部函数偏移量?所以我可以给sizeof(float)作为偏移量,只加载我数组的真实部分并在处理后存储它们。

提前谢谢你。

【问题讨论】:

【参考方案1】:

不 - 这是一个“收集”操作,仅由 AVX(不是 SSE)支持,而且效率也非常低。如果您对 SIMD 优化很认真,您应该更适当地组织您的数据。如果你真的必须坚持这种数据布局,那么最好的选择可能是加载两个连续的向量并打乱(置换)它们以将所有实部放在一个中(如果需要,将虚部放在另一个中),例如

__m128 v0 = _mm_loadu_ps(&a[0].real());
__m128 v1 = _mm_loadu_ps(&a[2].real());
__m128 v_real = _mm_shuffle_ps(v0, v1, _MM_SHUFFLE(2, 0, 2, 0));
__m128 v_imag = _mm_shuffle_ps(v0, v1, _MM_SHUFFLE(3, 1, 3, 1));

【讨论】:

感谢您的回答。事实上,我必须使用这种数据布局。我将使用您的选项,即使它会增加加载/存储操作的数量也应该没问题。 如果您也需要虚部,那么负载数量不会增加,但是是的,如果您只需要实部,那么会有 2 倍的惩罚。希望您之后进行足够的计算以吸收此成本。 早在 2010 年,我将我的 FFT 从 real,imagreal,imag 重写为 real,realimag,imag。结果:摆脱所有洗牌后,速度立即提高了 30%。整个 SSE3 addsub 的事情基本上是一个完整的笑话。 :)

以上是关于如何使用 SSE 加载 std::complex 数组的实部?的主要内容,如果未能解决你的问题,请参考以下文章

从 std::complex<MyType> 到 std::complex<double> 的类型转换

在 boost::asio::buffer 中使用类似 std::vector<std::complex<double>> 的参数

在 C++ 中使用 std::complex<T> 创建复无穷大

使用 AVX2 高效计算 std::complex<float> 向量的绝对值

如何使用 SSE2 加载 16 x 8 位整数

如何将无符号整数加载到 SIMD 中