我们提供安全,免费的手游软件下载!

安卓手机游戏下载_安卓手机软件下载_安卓手机应用免费下载-先锋下载

当前位置: 主页 > 软件教程 > 软件教程

统计无符号整数二进制中 1 的个数(Hamming Weight)

来源:网络 更新时间:2024-05-30 15:32:16

在网络上关于这个问题的讨论很多,可以找到大量的资料。下面这篇文章讲述了关于统计无符号整数二进制中1的个数的算法,以及在不同指令集下的优化方法。

统计无符号整数二进制中1的个数(Hamming Weight)是一个常见的计算问题。在没有指令集的情况下,分治法是速度最快的方法。然而,MIT HAKMEM 169算法由于最后涉及到取余操作,速度稍慢一些。另外,256元素的查表算法速度次之。建议使用unsigned char类型的256元素表,以减少内存占用量和减小cache miss的概率。16位的查表算法速度较慢,因为使用了while循环,即使展开后也需要8次数据组合。在SSE4指令集得到CPU支持时,可以使用_mm_popcnt_u32指令,能够加速处理速度。一千万的随机数据,使用这个指令大概只需要3.2毫秒多就可以处理完成。如果稍微改下代码,让它能并行化一点,处理速度可以提高到大约2.7ms。

现在几乎所有新的CPU都支持SSE4指令集,但也不排除还有一些老爷机不支持。对于不支持SSE4但支持SSE3的CPU,也有合适的指令集能加速这个过程。文章中提到了16元素查表算法的优化方法,以及如何利用SSE3的指令集进行优化。

作者提到了一种16元素查表算法的优化方法,通过使用_mm_shuffle_epi8指令进行优化,可以提前4年将硬件的适应性提升。具体的优化方法是加载16个字节数据,然后和0xF进行and操作,得到每个字节的低4位,然后进行shuffle,得到每个字节低4位的二进制中1的个数。接着将原始字节数右移4位,再和0xF进行and操作,得到每个字节的高4位,然后进行shuffle。最后将两次shuffle的结果相加,就得到了这16个字节数据的二进制中1的个数。使用这个优化后的代码,测试上述1千万数据,大概只需要2.1ms就能处理完,比优化后的_mm_popcnt_u32还要快。

作者还提到了一种使用SSE2指令集进行优化的方法。通过观察分治法的代码,发现其中的位运算和移位操作在SSE2中都得到了支持。作者尝试反汇编C的代码后发现,编译器已经帮助向量化了,即使在设置里设置无增强指令(/arch:IA32)选项,编译器也会进行向量化(VS2019)。因此,作者还得不到和纯C比的真正的加速比。但是,在编译器没有这个向量化能力时,直接手工嵌入SSE2的指令,还是能有明显的加速作用的。

作者还介绍了一种实际应用场景,即使用_mm_cmpxx_ps等函数组合多次应用,最后得到一个Mask,然后使用_mm_movemask_ps函数得到一个标记,最后统计数组中有多少个二进制1,就可以得到符合条件的目标数量。作者还提到了如果系统支持AVX2,那还可以进一步做速度优化。

最后,作者列出了各个算法的耗时比较数据,并提供了相关测试代码地址。如果想时刻关注作者的最新文章,也可关注公众号或者添加作者微信:laviewpbt。