例如0x1230560181feab00
有4个零半字节而0x00000123456780ab
有6个。如何在不进行天真循环和计数的情况下快速计算出来?这有什么好酷的吗?
答案 0 :(得分:4)
是的,首先“收集整个半字节的zeroness”(所有4位的OR):
x |= x >> 1;
x |= x >> 2;
删除垃圾:
x &= 0x1111111111111111UL;
然后只要popcnt
以任何你想要的方式,如果你有可用的指令那么好。当然,这给出了不为零的半字节数,但这是相同的信息,只需从16减去它。
如果您要使用后备,可以优化其中一些,因为您知道每个半字节都是0或1,所以例如在以ulong result = value - ((value >> 1) & 0x5555555555555555UL);
开头的典型方法中,您可以跳过两个阶段的减少。
所以你可以使用它:
x = (x + (x >> 4)) & 0xF0F0F0F0F0F0F0FUL;
count = (x * 0x101010101010101UL) >> 56
做最后的计数。
不幸的是,尝试使用半字节很小,因此尝试通过使用不同的乘数只是来删除倒数第二个减少步骤不能解决 - 最终总和可以是16但是使用(x * 0x1111111111111111UL) >> 60
不能导致16。
答案 1 :(得分:1)
通过使用256或65536个条目(1个字节或1个短)的预先计算查找表,您可以获得一些加速,告诉您有多少个半字节为零。这需要256或64k字节。我想,更大的LUT是不合理的。
如果允许SSE指令,可以实现纯粹的加速,这要归功于神奇的_mm_movemask_epi8操作,该操作将16字节的16 MSb打包为16位。
您需要屏蔽掉每个其他半字节,然后将字节比较为零(_mm_cmpeq_epi8),然后将字节打包到位,并使用65536个条目的预先计算的LUT,告知有多少个零位。