我有一个用例,其中我有一个位数组,每个位都表示为8位整数,例如uint8_t data[] = {0,1,0,1,0,1,0,1};
我想通过仅提取每个值的lsb来创建一个整数。我知道使用int _mm_movemask_pi8 (__m64 a)
函数可以创建掩码,但是此内在函数仅占用字节的msb而不是lsb。是否有类似的固有或有效方法来提取lsb以创建单个8位整数?
答案 0 :(得分:5)
没有直接的方法,但是显然您可以简单地将lsb移入msb,然后将其提取:
_mm_movemask_pi8(_mm_slli_si64(x, 7))
这几天使用MMX很奇怪,应该避免。
这是SSE2版本,仍仅读取8个字节:
int lsb_mask8(uint8_t* bits) {
__m128i x = _mm_loadl_epi64((__m128i*)bits);
return _mm_movemask_epi8(_mm_slli_epi64(x, 7));
}
使用SSE2而不是MMX可以避免使用EMMS
答案 1 :(得分:2)
如果您有高效的BMI2 pext
(例如,Haswell和更新的版本,与AVX2相同),请对问题的另一方向使用{wim答案的反方向(How to efficiently convert an 8-bit bitmap to array of 0/1 integers with x86 SIMD)。
unsigned extract8LSB(uint8_t *arr) {
uint64_t bytes;
memcpy(&bytes, arr, 8);
unsigned LSBs = _pext_u64(bytes ,0x0101010101010101);
return LSBs;
}
此compiles like you'd expect至qword加载+一条pext
指令。内联后,编译器将0x01...
常量设置从循环中取消。
pext
/ pdep
在支持它们的Intel CPU上非常有效(3个周期延迟/ 1c吞吐量,1 uop,与乘法相同)。但是它们在AMD上效率不高,例如18c延迟和吞吐量。 (https://agner.org/optimize/)。如果您关心AMD,则绝对应该使用@harold的pmovmskb
答案。
或者,如果您有8个字节的多个连续块,请使用单个宽矢量进行处理,然后获得32位位图。您可以根据需要将其拆分,或者使用4展开循环,以右移位图以获得所有4个单字节结果。
如果您只是立即将其存储到内存中,那么您应该应该在写入源数据的循环中进行此提取,而不是在单独的循环中进行提取,因此它在高速缓存中仍然很热。 AVX2 _mm256_movemask_epi8
是低延迟的单个uop(在Intel CPU上),因此,如果您的数据在L1d缓存中不热,那么 just 这样做的循环将无法保持其执行单元正在等待内存。