C 如何有效地结合SSE中的比较?
我正在尝试将以下代码转换为SSE/AVX: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 <
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
}
}