如何高效统计__mask64中全1半字节(0xF)的数量?
统计__mask64中全1半字节(0xF)的高效优雅方案
我通过AVX512操作得到__mask64类型变量:
__mmask64 mboth = _kand_mask64(lres, hres);
需要统计其中所有位均为1的半字节(即0xF)的数量。
现有实现
简易循环实现
uint64 imask = (uint64)mboth; while (imask) { if ((imask & 0xf) == 0xf) // 注:原代码存在运算符优先级问题,需添加括号修正 ret++; imask = imask >> 4; }
尝试的两种SIMD实现
512位操作实现
// 循环外初始化 __m512i b512_1s = _mm512_set1_epi32(0xffffffff); __m512i b512_0s = _mm512_set1_epi32(0x00000000); // 统计逻辑(修正原代码笔误:fres → bits) __m512i vboth = _mm512_mask_set1_epi8(b512_0s, mboth, 0xff); __mmask16 bits = _mm512_cmpeq_epi32_mask(b512_1s, vboth); ret += __builtin_popcount((unsigned int)bits);
128位操作实现
// 循环外初始化 __m128i b128_1s = _mm_set1_epi32(0xffffffff); // 统计逻辑 uint64 maskl = mboth & 0x0f0f0f0f0f0f0f0f; uint64 maskh = mboth & 0xf0f0f0f0f0f0f0f0; uint64 mask128[2] = { (maskl << 4) | maskl, (maskh >> 4) | maskh }; __m128i bytes = _mm_cmpeq_epi8(b128_1s, *(__m128i*)mask128); uint bits = _mm_movemask_epi8(bytes); ret += __builtin_popcount(bits);
更优雅高效的解决方案
方案1:纯整数位运算(低延迟,无SIMD依赖)
通过位运算快速标记全1半字节,再用乘法累加统计数量:
uint64 imask = (uint64)mboth; // 将每个半字节的"是否为0xF"转化为单独标记位 uint64 temp = imask ^ 0xFFFFFFFFFFFFFFFF; // 半字节为0xF时取反后为0,否则非0 temp &= 0x0F0F0F0F0F0F0F0F; temp |= temp >> 2; temp |= temp >> 1; temp = ~temp; // 全1半字节对应位为1,其余为0 temp &= 0x0101010101010101; // 每个半字节仅保留1个标记位 // 用64位乘法快速累加所有标记 ret += (uint32_t)((temp * 0x0101010101010101) >> 56);
全程无循环、无SIMD指令,延迟低,适合对响应速度敏感的场景。
方案2:简洁的AVX512掩码操作
直接利用AVX512掩码指令处理__mask64,无需扩展为向量寄存器:
// 直接比较每个半字节是否为0xF,生成对应掩码 __mmask64 check = _kand_mask64(mboth, 0x0F0F0F0F0F0F0F0F); // 统计掩码中匹配的半字节数量 ret += __builtin_popcountll(_kcmpeq_mask64(check, 0x0F0F0F0F0F0F0F0F) >> 4);
代码极简,全程使用掩码专用指令,避免不必要的寄存器转换,效率更高。
方案3:查找表(LUT)实现
预计算字节级别的全1半字节计数,适合高吞吐量场景:
// 预初始化查找表(全局/静态区) const uint8_t nibble_full_count[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, // ... 省略中间值,按字节值计算对应全1半字节数量 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 }; // 统计逻辑 uint64 imask = (uint64)mboth; ret += nibble_full_count[(uint8_t)(imask >> 0)]; ret += nibble_full_count[(uint8_t)(imask >> 8)]; ret += nibble_full_count[(uint8_t)(imask >> 16)]; ret += nibble_full_count[(uint8_t)(imask >> 24)]; ret += nibble_full_count[(uint8_t)(imask >> 32)]; ret += nibble_full_count[(uint8_t)(imask >> 40)]; ret += nibble_full_count[(uint8_t)(imask >> 48)]; ret += nibble_full_count[(uint8_t)(imask >> 56)];
预计算后查表求和,代码简洁且吞吐量高,适合需要多次调用的场景。
内容的提问来源于stack exchange,提问作者martona
相关产品推荐
相关产品推荐

