我发现很难为这个看似简单的问题创建代码。
给出一个压缩的8位整数,如果存在则用另一个字节替换。
例如,我想用0x06
代替0x01
,所以我可以用res
作为输入来查找0x06
:
// Bytes to be manipulated
res = _mm_set_epi8(0x00, 0x03, 0x02, 0x06, 0x0F, 0x02, 0x02, 0x06, 0x0A, 0x03, 0x02, 0x06, 0x00, 0x00, 0x02, 0x06);
// Target value and substitution
val = _mm_set1_epi8(0x06);
sub = _mm_set1_epi8(0x01);
// Find the target
sse = _mm_cmpeq_epi8(res, val);
// Isolate target
sse = _mm_and_si128(res, sse);
// Isolate remaining bytes
adj = _mm_andnot_si128(sse, res);
现在,我不知道如何继续or
这两个部分,我需要删除目标并将其替换为替换的字节。
我在这里缺少什么SIMD指令?
和其他问题一样,我仅限于AVX,没有更好的处理器。
答案 0 :(得分:6)
您基本上需要做的是将要替换的所有字节(输入的字节)设置为零。然后将替换的所有其他字节设置为零,然后对结果进行或运算。您已经从_mm_cmpeq_epi8
得到了一个掩码来执行此操作。总体来说,可以这样完成:
__m128i mask = _mm_cmpeq_epi8(inp, val);
return _mm_or_si128(_mm_and_si128(mask, sub), _mm_andnot_si128(mask, inp));
由于and / andnot / or的最后一种组合很常见,因此SSE4.1引入了一条指令(基本上)将它们组合为一个指令:
__m128i mask = _mm_cmpeq_epi8(inp, val);
return _mm_blendv_epi8(inp, sub, mask);
实际上,当使用优化进行编译时,clang5.0及更高版本足以将第一个变体替换为第二个变体:https://godbolt.org/z/P-tcik
NB:如果替换值实际上是0x01
,则可以利用以下事实:掩码(比较结果)是0x00
或0xff
(即{{1 }}),也就是说,您可以将要替换的值归零,然后减去掩码:
-0x01
这可以节省从内存中加载__m128i val = _mm_set1_epi8(0x06);
__m128i mask = _mm_cmpeq_epi8(inp, val);
return _mm_sub_epi8(_mm_andnot_si128(mask, inp), mask);
向量或浪费寄存器的时间。而且,根据您的体系结构,它的吞吐量可能会稍微好一些。