C 如何有效地结合SSE中的比较?

C 如何有效地结合SSE中的比较?,c,optimization,assembly,sse,avx,C,Optimization,Assembly,Sse,Avx,我正在尝试将以下代码转换为SSE/AVX: float x1, x2, x3; float a1[], a2[], a3[], b1[], b2[], b3[]; for (i=0; i < N; i++) { if (x1 > a1[i] && x2 > a2[i] && x3 > a3[i] && x1 < b1[i] && x2 < b2[i] && x3 <

我正在尝试将以下代码转换为SSE/AVX:

float x1, x2, x3;
float a1[], a2[], a3[], b1[], b2[], b3[];
for (i=0; i < N; i++)
{
    if (x1 > a1[i] && x2 > a2[i] && x3 > a3[i] && x1 < b1[i] && x2 < b2[i] && x3 < b3[i])
    {
        // do something with i
    }
}
这同样有效,而且速度很快。对此进行基准测试(英特尔i7-2630QM、Windows 7、cygwin 1.7、cygwin gcc 4.5.3或mingw x86_64 gcc 4.5.3,N=8)表明,64位上的速度与上述代码相同(小于0.1%)。两个版本的内部循环都在缓存中的数据上平均运行6.8个时钟,并且比较结果始终为false

有趣的是,在32位上,_mm_测试版本的运行速度要慢10%。事实证明,编译器在循环展开后溢出掩码,必须重新读取它们;这可能是不必要的,在手工编码的装配中可以避免


选择哪种方法?似乎没有令人信服的理由选择
VPTEST
而不是
VMOVMSKPS
。实际上,选择
VMOVMSKPS
有一点原因,即它释放了一个xmm寄存器,否则该寄存器将被掩码占用

如果使用浮点数,通常需要使用
MOVMSKPS
(以及相应的AVX指令
VMOVMSKPS
)而不是
pmovskb


除此之外,是的,这是一种标准的方法;您还可以使用
PTEST
VPTEST
)根据SSE或AVX和/或AND NOT的结果直接更新条件标志。

针对您编辑的版本:

如果要直接根据
PTEST
的结果进行分支,使用它比将
MOVMSKPS
转换为GP reg更快,然后对其执行
测试来设置分支指令的标志。在AMD CPU上,在矢量域和整数域之间移动数据非常慢(5到10个周期的延迟,取决于CPU型号)

对于
PTEST
,您通常不需要额外的寄存器。您可以使用与两个参数相同的值,就像常规的非向量
TEST
指令一样。(测试
foo&foo
与测试
foo
相同)

在您的例子中,您确实需要检查是否设置了所有向量元素。如果你颠倒比较,然后或者将结果放在一起(所以你测试的是
!(x1
),你需要测试所有零的向量,而不是所有零的向量。但处理低元素仍然是个问题。如果需要保存寄存器以避免
PTEST
/
VTESTPS
需要向量掩码,则可以在执行
PTEST
之前将向量右移4个字节,并将其分支为零


AVX引入了
VTESTPS
,我想这可以避免可能的浮点->整数旁路延迟。但是,如果您使用任何int-domain指令为测试生成输入,那么最好使用
(V)PTEST
。(我知道您使用的是内部语言,但与记忆法相比,输入和查看内部语言是一件痛苦的事情。)

谢谢!你能解释一下如何使用PTEST吗?在什么情况下,这种方法比MOVMSKPS更有效?@AlexI:我正要写一个例子,但你似乎已经自己弄明白了。不过需要注意的是:如果你可以选择a[i]和b[i]第四条通道中的内容,并将其设为+/-无穷大(或者任何+/-值,因为x[3]为零),那么你可以消除遮罩,直接对比较结果进行PTEST。
__m128 x; // x1, x2, x3, 0
__m128 a[N]; // packed a1[i], a2[i], a3[i], 0 
__m128 b[N]; // packed b1[i], b2[i], b3[i], 0

for (int i = 0; i < N; i++)
{
    __m128 gt_mask = _mm_cmpgt_ps(x, a[i]);
    __m128 lt_mask = _mm_cmplt_ps(x, b[i]);
    __m128 mask = _mm_and_ps(gt_mask, lt_mask);
    if (_mm_movemask_epi8 (_mm_castps_si128(mask)) == 0xfff0)
    {
        // do something with i
    }
}
__m128 x; // x1, x2, x3, 0
__m128 a[N]; // packed a1[i], a2[i], a3[i], 0
__m128 b[N]; // packed b1[i], b2[i], b3[i], 0
__m128i ref_mask = _mm_set_epi32(0xffff, 0xffff, 0xffff, 0x0000);

for (int i = 0; i < N; i++)
{
    __m128 gt_mask = _mm_cmpgt_ps(x, a[i]);
    __m128 lt_mask = _mm_cmplt_ps(x, b[i]);
    __m128 mask = _mm_and_ps(gt_mask, lt_mask);
    if (_mm_testc_si128(_mm_castps_si128(mask), ref_mask))
    {
        // do stuff with i
    }
}