为何GCC不为含SIMD向量的多元素联合使用向量寄存器?
GCC中SIMD联合的参数传递差异问题
问题现象
当GCC处理包含__m128这类SIMD向量类型与同尺寸、同对齐非向量类型的联合时,生成的代码效率远低于仅包含SIMD类型的联合或直接使用SIMD类型的场景:
- 混合类型联合的参数会通过两个XMM寄存器传递,随后被加载到栈上,才能供
addps指令使用; - 纯SIMD类型的参数/返回值则直接通过XMM寄存器传递,无需栈操作。
原始混合类型联合代码
#include <immintrin.h> #include <array> union simd { __m128 vec; alignas(__m128) std::array<float, 4> values; }; simd add(simd a, simd b) noexcept { simd ret; ret.vec = _mm_add_ps(a.vec, b.vec); return ret; }
生成的低效汇编
add(simd, simd): movq QWORD PTR [rsp-40], xmm0 movq QWORD PTR [rsp-32], xmm1 movq QWORD PTR [rsp-24], xmm2 movq QWORD PTR [rsp-16], xmm3 movaps xmm4, XMMWORD PTR [rsp-24] addps xmm4, XMMWORD PTR [rsp-40] movaps XMMWORD PTR [rsp-40], xmm4 movq xmm1, QWORD PTR [rsp-32] movq xmm0, QWORD PTR [rsp-40] ret
纯SIMD类型代码(对比)
__m128 add(__m128 a, __m128 b) noexcept { return _mm_add_ps(a, b); }
生成的高效汇编
add(float __vector(4), float __vector(4)): addps xmm0, xmm1 ret
差异原因
这是x86-64平台System V AMD64 ABI(调用约定)的规则导致的:
- 若联合/结构体的所有成员都是向量类型,整个复合类型会被归类为向量类型,遵循向量的寄存器传递规则;
- 一旦联合中存在非向量类型成员(哪怕尺寸、对齐与向量完全匹配),ABI会将其归类为普通聚合类型。对于16字节的聚合类型,ABI要求拆分到两个寄存器传递,函数内部必须将这些拆分部分重新组合到栈上才能访问完整的向量成员,从而产生额外的栈操作开销。
解决方法
方法1:用属性强制向量归类
给联合添加__attribute__((vector_size(16)))属性,强制GCC将其视为向量类型,直接遵循向量的参数传递规则:
#include <immintrin.h> #include <array> union simd __attribute__((vector_size(16))) { __m128 vec; alignas(__m128) std::array<float, 4> values; }; simd add(simd a, simd b) noexcept { simd ret; ret.vec = _mm_add_ps(a.vec, b.vec); return ret; }
修改后生成的汇编会和纯SIMD类型的版本完全一致,没有额外栈操作。
方法2:拆分接口与内部实现
对外提供纯SIMD类型的高效接口,内部需要使用联合时再进行类型转换,兼顾效率与灵活性:
#include <immintrin.h> #include <array> union simd { __m128 vec; alignas(__m128) std::array<float, 4> values; }; // 高效的核心接口 __m128 add_core(__m128 a, __m128 b) noexcept { return _mm_add_ps(a, b); } // 对外的联合接口 simd add(simd a, simd b) noexcept { simd ret; ret.vec = add_core(a.vec, b.vec); return ret; }
内容的提问来源于stack exchange,提问作者JustClaire
相关产品推荐
相关产品推荐

