用另一个字节替换一个字节
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
你可以利用掩码(比较的结果)是 0x00
或 0xff
(即 -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
向量或为其浪费寄存器。根据您的体系结构,它的吞吐量可能会稍好一些。
我发现很难为这个看似简单的问题创建代码。
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
你可以利用掩码(比较的结果)是 0x00
或 0xff
(即 -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
向量或为其浪费寄存器。根据您的体系结构,它的吞吐量可能会稍好一些。