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