如何从__m64值的 lsb 创建 8 位掩码?
How to create a 8 bit mask from lsb of __m64 value?
我有一个用例,其中我有位数组,每个位表示为 8 位整数uint8_t data[] = {0,1,0,1,0,1,0,1};
例如,我想通过仅提取每个值的 lsb 来创建单个整数。我知道使用int _mm_movemask_pi8 (__m64 a)
函数我可以创建一个掩码,但这个内在函数只需要一个字节的 msb 而不是 lsb。是否有类似的内在或有效的方法来提取 lsb 以创建单个 8 位整数?
没有直接的方法可以做到这一点,但显然你可以简单地将 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
如果您有高效的 BMI2pext
(例如 Haswell 和更新,与 AVX2 相同(,那么在你关于另一个方向的问题中使用 @wim 的答案相反(如何使用 x86 SIMD 有效地将 8 位位图转换为 0/1 整数数组(。
unsigned extract8LSB(uint8_t *arr) {
uint64_t bytes;
memcpy(&bytes, arr, 8);
unsigned LSBs = _pext_u64(bytes ,0x0101010101010101);
return LSBs;
}
这就像你期望的qword加载+pext
指令一样。 编译器将在内联后将0x01...
常量设置提升到循环之外。
pext
/pdep
在支持它们的英特尔 CPU 上是有效的(3 个周期延迟/1c 吞吐量,1 uop,与乘法相同(。 但它们在AMD上效率不高,比如18c延迟和吞吐量。 (https://agner.org/optimize/(。 如果你关心AMD,你绝对应该使用@haroldpmovmskb
答案。
或者,如果您有多个 8 字节的连续块,请使用单个宽向量执行它们,并获得 32 位位图。 如果需要,您可以将其拆分,或者使用 4 展开循环,以右移位图以获得所有 4 个单字节结果。
如果您只是立即将其存储到内存中,那么您可能应该在写入源数据的循环中完成此提取,而不是单独的循环,因此它在缓存中仍然很热。 AVX2_mm256_movemask_epi8
是具有低延迟的单个 uop(在英特尔 CPU 上(,因此,如果您的数据在 L1d 缓存中不是热的,那么仅执行此操作的循环不会在等待内存时使其执行单元保持忙碌。
- 位移操作和位掩码未检测到重复字符
- OpenCV - 带有掩码的absdiff
- 生成前缀位掩码
- 如何从__m64值的 lsb 创建 8 位掩码?
- 如何对无符号长 int 进行位掩码?
- 删除K的背景掩码-意味着Python或C++中的集群/
- 如何在C++中优雅地处理位掩码
- 将uint64_t位掩码转换为 std::布尔数组
- 使输入二进制掩码适应 ITK 网格生成器
- 如何从 getifaddr 读取子网掩码
- 优化从子位掩码生成父位掩码
- 基于模式创建位掩码作为 constexpr
- 使用二进制掩码 C++ ITK 获取感兴趣区域
- C++中的运行时位复制(位掩码)
- 根据 IP 和掩码C++打印所有 IP
- C++设置"blank"或重置 ifstrean (ios) 的异常掩码
- OPENCV:如何创建多边形形状的掩码
- 分割后,如何创建掩码来选择分割类
- OpenCV 均值函数崩溃,掩码从 OpenCV 阈值函数创建
- 通过克隆创建的子项是否会继承其父级的 CPU 关联掩码?