折叠 __mask64 又名 64 位整数值,计算已设置所有位的半字节?

问题描述 投票:0回答:1

由于一些 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);
c++ bit-manipulation avx avx512
1个回答
2
投票

只需一些标量运算,您就可以做到这一点:

imask &= imask << 2;
imask &= imask << 1;
ret += std::popcount(imask & 0x8888888888888888);

对于每个半字节,前两个步骤将该半字节的位的水平与放在该半字节的最高有效位中。半字节的其他部分变成了我们不想要的东西,所以我们只是将它们屏蔽掉。然后对结果进行popcount。

轮班可以向右(如本答案的早期版本所示),也可以轮换,以效果最好者为准。

© www.soinside.com 2019 - 2024. All rights reserved.