用另一个替换一个字节

时间:2019-01-15 15:32:19

标签: sse simd avx

我发现很难为这个看似简单的问题创建代码。

  

给出一个压缩的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,没有更好的处理器。

1 个答案:

答案 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,则可以利用以下事实:掩码(比较结果)是0x000xff(即{{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); 向量或浪费寄存器的时间。而且,根据您的体系结构,它的吞吐量可能会稍微好一些。