使用 sse 内在函数时如何打破循环?
Posted
技术标签:
【中文标题】使用 sse 内在函数时如何打破循环?【英文标题】:how to break from a loop when using sse intrinsics? 【发布时间】:2013-01-25 14:17:51 【问题描述】:__m128* pSrc1 = (__m128*) string;
__m128 m0 = _mm_set_ps1(0); //null character
while(1)
__m128 result = __m128 _mm_cmpeq_ss(*pSrc1, m0);
//if character is \0 then break
//do some stuff here
pSrc1++;
我有一个长度可以是 16 的倍数的字符串。 如果 _mm_cmpeq_ss 返回相等,如何跳出循环?
【问题讨论】:
任何你不能做诸如 while(__m128 result != '\0') 之类的事情的原因 原谅我的无知,浮点运算不是 128 位 XMM 寄存器吗? @CHill60 因为您无法将 128 位向量与 char 进行相等性比较 @legends2k 不。XMM 寄存器上有许多整数运算,例如长度为 8、16、32 和 64 位的整数类型的按位和/或/加/子/多/掩码。跨度> @hirschhornsalz ...哎呀,当然羞愧地垂头丧气 【参考方案1】:如果您在第一次遇到\0
时尝试跳出循环,那么您需要执行以下操作:
__m128i* pSrc1 = (__m128i *)string; // init pointer to start of string
__m128i m0 = _mm_set1_epi8(0); // vector of 16 `\0` characters
while (1)
__m128i v0 = _mm_loadu_si128(pSrc1); // get 16 chars from string
__m128i v1 = _mm_cmpeq_epi8(v0, m0); // compare all 16 chars with '\0'
int vmask = _mm_movemask_epi8(v1); // get 16 comparison result bits
if (vmask != 0) // if any bit is 1
break; // we found a `\0`, break out of loop
pSrc1++; // next 16 characters...
如果您只想测试特定位置的\0
字符而忽略其他任何字符,则可以将if (vmask != 0)
测试更改为符合您特定要求的内容。
【讨论】:
只有 m 17th, 33rd ...字符可以是 \0 即我的字符串长度总是 16 的倍数 OK - 这个解决方案仍然有效(除非您想在其他地方忽略\0
字符?)。无论如何,我已经更新了答案以涵盖这种可能性。
movemask
是否优于 ptest xmm,same
,以测试全零? (当然,如果您已经在使用其他 SSE4.1 指令。)test / jcc
可以进行宏融合,因此它的 uops 数量相同(movemask + test&branch vs. ptest + branch),但指令更少。 movemask
在 Haswell 上有 3 个周期延迟(在 SnB/IvB 上有 2 个),而 ptest
即使在 Haswell 上也只有 2 个周期延迟。 movmsk
只能在 p0 上运行,而 ptest
可以在 p0/p5 (Haswell) 上运行,因此它使两个 FMA 端口都空闲。我认为在大多数情况下,PTEST
会是更好的选择。
哎呀,nvm,PTEST
在 Haswell 上是 2 微秒。因此,如果 uop 吞吐量是瓶颈,它可能比movmsk
+ test
慢。我想它仅在仅测试高位还不够的情况下才有用,或者您可以将它与 2 个不同的操作数一起使用(以获得免费的按位与)。根据 Agner Fog 的表格,PTEST
是 SnB 上的 1 个融合域 uop,但对于执行端口仍然是 2 个未融合的 uop。在 IvB/HSW 上,即使在融合域中也是 2 微秒。以上是关于使用 sse 内在函数时如何打破循环?的主要内容,如果未能解决你的问题,请参考以下文章