由于一些 AVX512 操作,我得到了
__mask64
:
__mmask64 mboth = _kand_mask64(lres, hres);
我想计算其中已设置所有位的半字节数(
0xF
)。
简单的解决方案是这样做:
uint64 imask = (uint64)mboth;
while (imask) {
if (imask & 0xf == 0xf)
ret++;
imask = imask >> 4;
}
我想要更好的东西,但我想出的东西并不优雅:
//outside the loop
__m512i b512_1s = _mm512_set1_epi32(0xffffffff);
__m512i b512_0s = _mm512_set1_epi32(0x00000000);
//then...
__m512i vboth = _mm512_mask_set1_epi8(b512_0s, mboth, 0xff);
__mmask16 bits = _mm512_cmpeq_epi32_mask(b512_1s, vboth);
ret += __builtin_popcount((unsigned int)fres);
上面将一个
0xff
字节放入一个向量中,其中掩码中存在 1 位,然后当现在发现放大的 bits
半字节为 0xf
时,在 0xffffffff
掩码中获取 1 位int32
的。
我觉得当原始数据存在于 64 位数字中时,两次 512 位操作太过分了。这个替代方案可能要糟糕得多。它的指令太多了,而且仍然在 128 位上运行:
//outside the loop
__m128i b128_1s = _mm_set1_epi32(0xffffffff);
//then...
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);
只需一些标量运算,您就可以做到这一点:
imask &= imask << 2;
imask &= imask << 1;
ret += std::popcount(imask & 0x8888888888888888);
对于每个半字节,前两个步骤将该半字节的位的水平与放在该半字节的最高有效位中。半字节的其他部分变成了我们不想要的东西,所以我们只是将它们屏蔽掉。然后对结果进行popcount。
轮班可以向右(如本答案的早期版本所示),也可以轮换,以效果最好者为准。