load1 和广播内在函数之间的区别
difference between load1 and broadcast intrinsics
_mm_broadcast_ss()
和_mm_load_ps1()
有什么区别?
void example(){
__declspec(align(32)) const float num = 20;
__m128 a1 = _mm_broadcast_ss(&num);
__declspec(align(32)) float f1[4];
_mm_store_ps (f1, a1);
std::cout << f1[0] << " " << f1[1] <<" " << f1[2] << " " << f1[3] << "\n";
__m128 a2 = _mm_load_ps1(&num);
__declspec(align(32)) float f2[4];
_mm_store_ps (f2, a2);
std::cout << f2[0] << " " << f2[1] <<" " << f2[2] << " " << f2[3] << "\n";
}
我在两种方式中得到了相同的输出,那么为什么它们都存在?
_mm_broadcast_ss
仅为 AVX 目标编译。
_mm_load1_ps
/ _mm_load_ps1
在为不支持 AVX 的目标编译时将编译为多条指令 (movss
/ shufps
)。当您正在 为 AVX 目标编译时,任何好的编译器都会使用 vbroadcastss
来实现它们。
load1
/ set1
和其他便利函数很早就引入了,因为让编译器选择移动数据的最佳策略通常很好。
_mm_broadcast_*
内在函数是作为 vbroadcastss
/ vbroadcastsd
指令的直接包装器引入的。 (AVX2有整数vpbroadcast...
,reg-reg形式vbroadcastss
。AVX1只有vbroadcastss x/ymm, [mem]
。)
AFAICT,仅使用 _mm_load1_ps
或 _mm_set1_ps
.
没有任何缺点
它对代码没有影响,并允许为非 AVX 目标构建相同的源代码。
选择可能会对 -O0
处的 asm 输出产生影响,但 IDK。如果您关心未优化构建中的 asm 输出,那么 1:这很奇怪,并且 2:您必须查看编译器的功能。
正如您从 godbolt 上的 asm 输出中看到的(对于 gcc):
Without AVX (-mno-avx
)
bcast: compile error so I #ifdef it out
__m128 load1(const float*p) { return _mm_load1_ps(p); }
movss xmm0, DWORD PTR [rdi]
shufps xmm0, xmm0, 0
ret
With AVX (-mavx
)
__m128 bcast(const float*p) { return _mm_broadcast_ss(p); }
vbroadcastss xmm0, DWORD PTR [rdi]
ret
__m128 load1(const float*p) { return _mm_load1_ps(p); }
vbroadcastss xmm0, DWORD PTR [rdi]
ret
_mm_broadcast_ss()
和_mm_load_ps1()
有什么区别?
void example(){
__declspec(align(32)) const float num = 20;
__m128 a1 = _mm_broadcast_ss(&num);
__declspec(align(32)) float f1[4];
_mm_store_ps (f1, a1);
std::cout << f1[0] << " " << f1[1] <<" " << f1[2] << " " << f1[3] << "\n";
__m128 a2 = _mm_load_ps1(&num);
__declspec(align(32)) float f2[4];
_mm_store_ps (f2, a2);
std::cout << f2[0] << " " << f2[1] <<" " << f2[2] << " " << f2[3] << "\n";
}
我在两种方式中得到了相同的输出,那么为什么它们都存在?
_mm_broadcast_ss
仅为 AVX 目标编译。
_mm_load1_ps
/ _mm_load_ps1
在为不支持 AVX 的目标编译时将编译为多条指令 (movss
/ shufps
)。当您正在 为 AVX 目标编译时,任何好的编译器都会使用 vbroadcastss
来实现它们。
load1
/ set1
和其他便利函数很早就引入了,因为让编译器选择移动数据的最佳策略通常很好。
_mm_broadcast_*
内在函数是作为 vbroadcastss
/ vbroadcastsd
指令的直接包装器引入的。 (AVX2有整数vpbroadcast...
,reg-reg形式vbroadcastss
。AVX1只有vbroadcastss x/ymm, [mem]
。)
AFAICT,仅使用 _mm_load1_ps
或 _mm_set1_ps
.
没有任何缺点
它对代码没有影响,并允许为非 AVX 目标构建相同的源代码。
选择可能会对 -O0
处的 asm 输出产生影响,但 IDK。如果您关心未优化构建中的 asm 输出,那么 1:这很奇怪,并且 2:您必须查看编译器的功能。
正如您从 godbolt 上的 asm 输出中看到的(对于 gcc):
Without AVX (-mno-avx
)
bcast: compile error so I #ifdef it out
__m128 load1(const float*p) { return _mm_load1_ps(p); }
movss xmm0, DWORD PTR [rdi]
shufps xmm0, xmm0, 0
ret
With AVX (-mavx
)
__m128 bcast(const float*p) { return _mm_broadcast_ss(p); }
vbroadcastss xmm0, DWORD PTR [rdi]
ret
__m128 load1(const float*p) { return _mm_load1_ps(p); }
vbroadcastss xmm0, DWORD PTR [rdi]
ret