计算 128 位 avx 向量中唯一值的数量,或检测所有元素是否相等?
count number of unique values in a 128bit avx vector, or detecting if all elements are equal?
我正在优化我的代码库中的热路径,并且我已转向矢量化。请记住,我对所有这些 SIMD 东西还是很陌生。这是我要解决的问题,使用非 SIMD
实现
inline int count_unique(int c1, int c2, int c3, int c4)
{
return 4 - (c2 == c1)
- ((c3 == c1) || (c3 == c2))
- ((c4 == c1) || (c4 == c2) || (c4 == c3));
}
用-O3
编译后的汇编输出:
count_unique:
xor eax, eax
cmp esi, edi
mov r8d, edx
setne al
add eax, 3
cmp edi, edx
sete dl
cmp esi, r8d
sete r9b
or edx, r9d
movzx edx, dl
sub eax, edx
cmp edi, ecx
sete dl
cmp r8d, ecx
sete dil
or edx, edi
cmp esi, ecx
sete cl
or edx, ecx
movzx edx, dl
sub eax, edx
ret
将 c1,c2,c3,c4 存储为 16 字节整数向量时,如何完成这样的操作?
好的,我已经“简化”了问题,因为当我使用唯一计数时,唯一的情况是它是否为 1,但这与检查所有元素是否相同相同,可以是通过将输入与自身进行比较来完成,但使用 _mm_alignr_epi8
函数移动了一个元素(4 个字节)。
inline int is_same_val(__m128i v1) {
__m128i v2 = _mm_alignr_epi8(v1, v1, 4);
__m128i vcmp = _mm_cmpeq_epi32(v1, v2);
return ((uint16_t)_mm_movemask_epi8(vcmp) == 0xffff);
}
对于您的简化问题(测试所有 4 条车道是否相等),我会稍微不同地做,方法如下。这样只需要3条指令就可以完成测试。
// True when the input vector has the same value in all 32-bit lanes
inline bool isSameValue( __m128i v )
{
// Rotate vector by 4 bytes
__m128i v2 = _mm_shuffle_epi32( v, _MM_SHUFFLE( 0, 3, 2, 1 ) );
// The XOR outputs zero for equal bits, 1 for different bits
__m128i xx = _mm_xor_si128( v, v2 );
// Use PTEST instruction from SSE 4.1 set to test the complete vector for all zeros
return (bool)_mm_testz_si128( xx, xx );
}
我正在优化我的代码库中的热路径,并且我已转向矢量化。请记住,我对所有这些 SIMD 东西还是很陌生。这是我要解决的问题,使用非 SIMD
实现inline int count_unique(int c1, int c2, int c3, int c4)
{
return 4 - (c2 == c1)
- ((c3 == c1) || (c3 == c2))
- ((c4 == c1) || (c4 == c2) || (c4 == c3));
}
用-O3
编译后的汇编输出:
count_unique:
xor eax, eax
cmp esi, edi
mov r8d, edx
setne al
add eax, 3
cmp edi, edx
sete dl
cmp esi, r8d
sete r9b
or edx, r9d
movzx edx, dl
sub eax, edx
cmp edi, ecx
sete dl
cmp r8d, ecx
sete dil
or edx, edi
cmp esi, ecx
sete cl
or edx, ecx
movzx edx, dl
sub eax, edx
ret
将 c1,c2,c3,c4 存储为 16 字节整数向量时,如何完成这样的操作?
好的,我已经“简化”了问题,因为当我使用唯一计数时,唯一的情况是它是否为 1,但这与检查所有元素是否相同相同,可以是通过将输入与自身进行比较来完成,但使用 _mm_alignr_epi8
函数移动了一个元素(4 个字节)。
inline int is_same_val(__m128i v1) {
__m128i v2 = _mm_alignr_epi8(v1, v1, 4);
__m128i vcmp = _mm_cmpeq_epi32(v1, v2);
return ((uint16_t)_mm_movemask_epi8(vcmp) == 0xffff);
}
对于您的简化问题(测试所有 4 条车道是否相等),我会稍微不同地做,方法如下。这样只需要3条指令就可以完成测试。
// True when the input vector has the same value in all 32-bit lanes
inline bool isSameValue( __m128i v )
{
// Rotate vector by 4 bytes
__m128i v2 = _mm_shuffle_epi32( v, _MM_SHUFFLE( 0, 3, 2, 1 ) );
// The XOR outputs zero for equal bits, 1 for different bits
__m128i xx = _mm_xor_si128( v, v2 );
// Use PTEST instruction from SSE 4.1 set to test the complete vector for all zeros
return (bool)_mm_testz_si128( xx, xx );
}