You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何高效统计__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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 15:08:20