如何计算 __m128 变量。(另外 _mm_blendv_ps 是做什么的)?

0 c++ sse simd

所以我阅读了英特尔关于 _mm_blendv_ps 的文档,但不太明白该函数的真正作用。所以我写了下面的代码:

    __m128 a = { 18.0,4.0,19.0,21.0 };
    __m128 b = { 67.0,92.0,888.0,47.0 };
    __m128 mask = { 1.0,0.0,0.0,1.0 };

    __m128 result = _mm_blendv_ps(a, b, mask);
    cout << "Result is: " << result[0] << " " << result[1] << " " << result[2] << " " << result[4] << endl;
Run Code Online (Sandbox Code Playgroud)

但我收到错误“没有运算符 [] 与这些操作数匹配”。为什么我无法访问结果?结果不是32位浮点向量吗?

那么为什么我无法访问结果呢?我怎样才能访问它?cout 的结果是什么(blendv 做什么)?

rob*_*oke 5

Blendv 使用最高设置位在两个结果之间进行选择。它相当于这段代码:

__m128 _mm_blendv_ps(__m128 false_result, __m128 true_result, __m128 mask) {
   __m128 r;
   r[0] = (mask[0] & 0x80000000) ? true_result[0] : false_result[0];
   r[1] = (mask[1] & 0x80000000) ? true_result[1] : false_result[1];
   r[2] = (mask[2] & 0x80000000) ? true_result[2] : false_result[2];
   r[3] = (mask[3] & 0x80000000) ? true_result[3] : false_result[3];
   return r;
}
Run Code Online (Sandbox Code Playgroud)

我实际上倾向于包装它,因为参数顺序与标准有点不同if(cmp) { true } else { false };

__m128 select(__m128 mask, __m128 true_result, __m128 false_result) {
   return _mm_blendv_ps(false_result, true_result, mask);
}
Run Code Online (Sandbox Code Playgroud)

通常您会使用它来执行if(a < b) {} else {}类型操作,例如

// if (a < b) {return true_result;} else {return false_result;}
__m128 select_if_lt(__m128 a, __m128 b, __m128 true_result, __m128 false_result) {
   return select(_mm_cmplt_ps(a, b), true_result, false_result);
}

// if (a >= b) {return true_result;} else {return false_result;}
__m128 select_if_ge(__m128 a, __m128 b, __m128 true_result, __m128 false_result) {
   return select(_mm_cmpge_ps(a, b), true_result, false_result);
}
Run Code Online (Sandbox Code Playgroud)

在您上面发布的代码中:

    __m128 mask = { 1.0,0.0,0.0,1.0 };
Run Code Online (Sandbox Code Playgroud)

1.0 的最高位实际上是零,所以你需要一个负数来使掩码起作用,例如

    // it doesn't matter which negative number you use, 
    // it just requires the sign bit to be set. -0.0f works!
    __m128 mask = { -0.0f,0.0,0.0,-0.0f };
Run Code Online (Sandbox Code Playgroud)

仅查看符号位的好处是您可以执行某些 if/else 操作,而无需使用比较指令,例如

// if (a < 0) {return true_result;} else {return false_result;}
__m128 select_if_negative(__m128 a, __m128 true_result, __m128 false_result) {
    return select(a, true_result, false_result);
}
Run Code Online (Sandbox Code Playgroud)

但请注意,您将得到 -0.0f 的误报,这对您可能重要也可能不重要

至于访问 __m128 的内容,这通常不是跨平台的(一些编译器重载数组运算符,一些指定 .x/.y 等,一些具有内部联合成员变量)。因此,如果您想要一种跨平台访问内容的方法,您有两个选择:

  1. 正如彼得正确指出的那样,不要使用,而是与随机播放一起_mm_extract_ps使用。_mm_cvtss_f32
std::ostream& operator << (std::ostream& os, const __m128& v) {
   os << "(" << 
         _mm_cvtss_f32(v) << ", " << 
         _mm_cvtss_f32(_mm_shuffle_ps(b, b, _MM_SHUFFLE(1, 1, 1, 1))) << ", " << 
         _mm_cvtss_f32(_mm_unpackhi_ps(b, b)) << ", " << 
         _mm_cvtss_f32(_mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 3, 3, 3))) << ")"; 
    return os;
}
Run Code Online (Sandbox Code Playgroud)
  1. 使用_mm_store_ps
std::ostream& operator << (std::ostream& os, const __m128& v) {
   float f[4];
   _mm_storeu_ps(f, v);
   os << "(" << 
         f[0] << ", " << 
         f[1] << ", " << 
         f[2] << ", " << 
         f[3] << ")";
    return os;
}
Run Code Online (Sandbox Code Playgroud)

不管你怎么做,访问 XMM 寄存器的元素总是会产生成本(当然,除了 [0]),所以一般规则是尽量避免这样做!