Clang向量扩展和C ++中的相等运算符

时间:2015-04-06 03:28:24

标签: c++ clang simd

我使用Clang SIMD向量扩展编写了一个矢量类型。它工作得很好,除非我需要检查两个向量是否相等。 ==运算符似乎没有为Clang的矢量类型正确定义。尝试将两个向量与==进行比较奇怪地似乎评估了与被比较的两个向量相同的第三个向量,而不是bool。我觉得这很奇怪,因为应用其他操作如+-编译没有问题,并输出预期的结果。这是我的代码,用Clang 3.5(Xcode)编译:

// in vect.h 
template <typename NumericType>
using vec2 = NumericType __attribute__((ext_vector_type(2))) ;

//in main.cpp
#include "vect.h"

int main(int argc, const char ** argv) {

    vec2<int> v0 {0, 1} ;
    vec2<int> v1 {0, 1} ;

    vec2<int> sumVs = v0 + v1 ; //OK: evaluates to {0, 2} when run

    bool equal = (v0 == v1) ; /* Compiler error with message: "Cannot initialize
        a variable of type 'bool' with an rvalue of type 'int __attribute__((ext_vector_type(2)))'" */

    return 0;
}

是否有任何方法可以启用operator ==与Clang的矢量类型或此问题的任何其他解决方法?由于它们被认为是原始类型而不是类类型,我自己不会使比较运算符超载,并且编写全局equals()函数似乎很笨拙且不优雅。

更新:或者,如果没有人有我正在寻找的解决方案,或许有人可以在比较两个SIMD向量时解释==运算符的默认行为?

更新#2 :Hurkyl建议两个向量上的==进行矢量化比较。我更新了我的代码以测试这种可能性:

template <typename NumericType>
using vec3 = NumericType __attribute__((ext_vector_type(3))) ;

int main(int argc, const char ** argv) {

    vec3<int> v0 {1, 2, 3} ;
    vec3<int> v1 {3, 2, 1} ;

    auto compareVs = (v0 == v1) ;

    return 0;
}

LLDB将compareVs的值报告为{0,-1,0},如果发生了什么,这几乎是正确的,但true似乎很奇怪将为-1,false为0。

更新#3 :好的,感谢我收到的反馈,我现在可以更好地了解关系运算符和比较运算符如何应用于向量。但我的基本问题仍然存在。对于任何两个SIMD类型的向量v1v2,我需要一种简单而优雅的方法来检查它们是否相同。换句话说,我需要能够检查iv1中的每个索引v2v1[i] == v2[i],表示为单个布尔值(即< em> not 作为bool的向量/数组。如果唯一的答案确实是这样的函数:

template <typename NumericType>
bool equals(vec2<NumericType> v1, vec2<NumericType> v2) ...

...然后我接受了。但我希望有人可以提出一些不那么笨拙的事情。

2 个答案:

答案 0 :(得分:2)

使用false的按位补码作为真值并非异常(例如参见BASIC)。

如果你想用它来实现一个无分支的三元运算符,它在向量算法中特别有用:

r = (a == c)? b: d

变为

selector = (a == c)
r = (b & selector) | (d & ~selector)

答案 1 :(得分:2)

如果不是使用特定于编译器的语言扩展,而是使用instrinsics(例如,在xmmintrin.h中提供),那么您可以使用 _mm_movemask_ps(__m128)及其亲属。例如

__m128 a,b;
/* some code to fill a,b with integer elements */
bool a_equals_b = 15 == _mm_movemask_ps(_mm_cmpeq_epi32(a,b));

此代码的工作原理如下。首先,_mm_cmpeq_ps(a,b)生成另一个__m128,其中四个元素中的每一个都是位0或所有位1 - 我假设operator==编译器生成的向量扩展调用正是这个内在的) 。接下来,int _mm_movemask_ps(__m128)返回一个整数,其中 k 位设置为其参数的 k th元素的signbit。因此,如果所有元素都a==b,则_mm_movemask_ps(_mm_cmpeq_epi32(a,b))会返回1|2|4|8=15

我不知道编译器支持的语言扩展,但是如果你可以获得底层__m128(对于128位宽的向量),那么你可以使用这种方法(可能只调用{{1 }})。