[toc]
include/asm-generic/bitops/const_hweight.h lib/hweight.c 软件汉明权重计算:__sw_hweight 系列函数 本代码片段提供了一组用于计算不同位宽整数(8、16、32、64位)汉明权重(Hamming Weight)的纯软件实现。汉明权重,又称“置位比特计数”(population count),指的是一个二进制数中 ‘1’ 的总个数。这些函数是内核中需要进行位操作统计时的基础工具,其实现采用了高效的、不依赖特定处理器指令的“分治”并行算法。
实现原理分析 这些函数的核心是采用了一种经典的分治算法(Divide and Conquer),在对数时间内完成计算,远比逐位检查的方法高效。以 __sw_hweight32 为例,其算法步骤如下:
两比特分组求和 :w -= (w >> 1) & 0x55555555;
0x55555555 的二进制表示为 01010101...。
(w >> 1) & 0x55555555 的作用是分离出 w 中每两个比特位中的高位。
将 w 减去这个值,其效果是在每个两比特的“窗口”内,独立地计算出 ‘1’ 的个数。例如,对于两位 b1b0,其计算为 (b1*2 + b0) - b1 = b1 + b0。运算后,w 的每两位存储了原始数值对应位置两位中 ‘1’ 的个数。
四比特分组求和 :w = (w & 0x33333333) + ((w >> 2) & 0x33333333);
0x33333333 的二进制表示为 00110011...。
w & 0x33333333 取出上一步中每四位里的低两位(即第一个两比特分组的和)。
(w >> 2) & 0x33333333 取出高两位(即第二个两比特分组的和)。
将两者相加,就在每个四比特的窗口内,得到了 ‘1’ 的总数。
八比特(字节)分组求和 :w = (w + (w >> 4)) & 0x0f0f0f0f;
0x0f0f0f0f 的二进制表示为 00001111...。
此步骤将相邻的两个四比特分组的和相加,得到每个字节(8位)中 ‘1’ 的总数。
最终求和(两种策略) :
快速乘法器策略 (CONFIG_ARCH_HAS_FAST_MULTIPLIER) :return (w * 0x01010101) >> 24; 这是一个非常巧妙的技巧。此时 w 的四个字节分别存储了原始数值对应四个字节的汉明权重。将 w 乘以 0x01010101,其效果会将所有字节的和累加到结果的最高字节中。右移24位即可分离出这个最终总和。此方法依赖于一个高效的硬件乘法器。
移位与加法策略 :res = res + (res >> 8); return (res + (res >> 16)) & 0x000000FF; 这是一种不依赖乘法器的通用方法。它通过移位和加法,先将相邻字节的和累加,再将相邻16位半字的和累加,最终结果会汇集到最低的字节中,通过与 0xFF 相与即可得到。
其他函数如 __sw_hweight16 和 __sw_hweight8 遵循完全相同的算法逻辑,只是使用的位掩码和移位数不同。__sw_hweight64 则根据系统的字长(BITS_PER_LONG)来决定实现方式。
代码分析 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 #include <linux/export.h> #include <linux/bitops.h> #include <asm/types.h> unsigned int __sw_hweight32(unsigned int w){ #ifdef CONFIG_ARCH_HAS_FAST_MULTIPLIER w -= (w >> 1 ) & 0x55555555 ; w = (w & 0x33333333 ) + ((w >> 2 ) & 0x33333333 ); w = (w + (w >> 4 )) & 0x0f0f0f0f ; return (w * 0x01010101 ) >> 24 ; #else unsigned int res = w - ((w >> 1 ) & 0x55555555 ); res = (res & 0x33333333 ) + ((res >> 2 ) & 0x33333333 ); res = (res + (res >> 4 )) & 0x0F0F0F0F ; res = res + (res >> 8 ); return (res + (res >> 16 )) & 0x000000FF ; #endif } EXPORT_SYMBOL(__sw_hweight32); unsigned int __sw_hweight16(unsigned int w){ unsigned int res = w - ((w >> 1 ) & 0x5555 ); res = (res & 0x3333 ) + ((res >> 2 ) & 0x3333 ); res = (res + (res >> 4 )) & 0x0F0F ; return (res + (res >> 8 )) & 0x00FF ; } EXPORT_SYMBOL(__sw_hweight16); unsigned int __sw_hweight8(unsigned int w){ unsigned int res = w - ((w >> 1 ) & 0x55 ); res = (res & 0x33 ) + ((res >> 2 ) & 0x33 ); return (res + (res >> 4 )) & 0x0F ; } EXPORT_SYMBOL(__sw_hweight8); unsigned long __sw_hweight64(__u64 w){ #if BITS_PER_LONG == 32 return __sw_hweight32((unsigned int )(w >> 32 )) + __sw_hweight32((unsigned int )w); #elif BITS_PER_LONG == 64 #ifdef CONFIG_ARCH_HAS_FAST_MULTIPLIER w -= (w >> 1 ) & 0x5555555555555555u l; w = (w & 0x3333333333333333u l) + ((w >> 2 ) & 0x3333333333333333u l); w = (w + (w >> 4 )) & 0x0f0f0f0f0f0f0f0fu l; return (w * 0x0101010101010101u l) >> 56 ; #else __u64 res = w - ((w >> 1 ) & 0x5555555555555555u l); res = (res & 0x3333333333333333u l) + ((res >> 2 ) & 0x3333333333333333u l); res = (res + (res >> 4 )) & 0x0F0F0F0F0F0F0F0Fu l; res = res + (res >> 8 ); res = res + (res >> 16 ); return (res + (res >> 32 )) & 0x00000000000000FFu l; #endif #endif } EXPORT_SYMBOL(__sw_hweight64);