用另一个字节替换一个字节

Substitute a byte with another one

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

Given a packed 8 bits integer, substitute one byte with another if present.

例如,我想将 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,我没有更好的处理器。

你基本上需要做的是将你想要替换的所有字节(输入的)设置为零。然后将替换的所有其他字节设置为零并对结果进行或运算。您已经从 _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


N.B.: 如果替换值实际上是 0x01 你可以利用掩码(比较的结果)是 0x000xff(即 -0x01),即,您可以将要替换的值清零,然后减去掩码:

__m128i val = _mm_set1_epi8(0x06);
__m128i mask = _mm_cmpeq_epi8(inp, val);
return _mm_sub_epi8(_mm_andnot_si128(mask, inp), mask);

这可以避免从内存中加载 0x01 向量或为其浪费寄存器。根据您的体系结构,它的吞吐量可能会稍好一些。