bro*_*s94 14 c bit-manipulation go
这段代码发生了什么?从名称和上下文中可以找到机器上的核心数量,但它是如何工作的?什么都有点摆弄?
static int32
getproccount(void)
{
uintptr buf[16], t;
int32 r, cnt, i;
cnt = 0;
r = runtime·sched_getaffinity(0, sizeof(buf), buf);
if(r > 0)
for(i = 0; i < r/sizeof(buf[0]); i++) {
t = buf[i];
t = t - ((t >> 1) & 0x5555555555555555ULL);
t = (t & 0x3333333333333333ULL) + ((t >> 2) & 0x3333333333333333ULL);
cnt += (int32)((((t + (t >> 4)) & 0xF0F0F0F0F0F0F0FULL) * 0x101010101010101ULL) >> 56);
}
return cnt ? cnt : 1;
}
Run Code Online (Sandbox Code Playgroud)
注意:忽略·in runtime·sched_getaffinity,将该行视为一个任意库/系统调用,它执行名称和参数所暗示的操作.(在这种情况下,这个特定的呼叫来自的老 Go1.4预运行时间写成了交易的C略有变化·).
循环for运行与填充到buf数组中的元素一样多的元素。对于每个元素,它计算在该元素中设置的位数(摆弄的位t就是这样做的)并将其添加到 中cnt。最后cnt返回(如果cnt是 0,则返回 1)。
位摆弄线1
该行t = t - ((t >> 1) & 0x5555555555555555ULL);基本上将 的位分组t为 2 位,并用该组中设置的位数的计数替换每个组。其工作原理如下:
考虑t = ... w x y z其中 w、x、y、z 是各个位。然后
t = ... w x y z
t>>1 = ..... w x y
t>>1 & 0x...55ULL = ... 0 w 0 y
Run Code Online (Sandbox Code Playgroud)
从上面可以清楚地看出为什么分组发生在 2 位中(例如,y 和 z 在这里分组在一起)。现在t - ((t >> 1) & 0x5555555555555555ULL)将每组 2 位y z转换为(y-z). 使用 4 种可能性 (00, 01, 10, 11) 的表,我们看到答案是 (00, 01, 01, 10),它与该 2 位组中设置的位数相匹配。
位摆弄线2
继续进行下一个位调整行t = (t & 0x3333333333333333ULL) + ((t >> 2) & 0x3333333333333333ULL);,我们可以看到它将相邻的 2 组以 2 组为一组相加。
t & 0x..33ULL取每组 4 位中最右边的 2 位。
(t>>2) & 0x..33ULL取每组4位中最左边的2位,右移2位。
由于这两组2位是原数中设置的位数,因此将每组4中设置的位数相加位。(即现在,每组 4 位具有最初在这些位置设置的位数)
位摆弄线3
至于最后的一点小插曲cnt += (int32)((((t + (t >> 4)) & 0xF0F0F0F0F0F0F0FULL) * 0x101010101010101ULL) >> 56);,我们可以把它分成几个部分,以便于理解。
(int32)(
(
(
(
t + (t >> 4)
) & 0xF0F0F0F0F0F0F0FULL
) * 0x101010101010101ULL
) >> 56
)
Run Code Online (Sandbox Code Playgroud)
目前,每组 4 位存储最初在数字中设置的位数。将数字移动 4 并相加会将所有相邻组加在一起。&ing with0x..0F0FULL选出每组 8 位中正确的 4 位。因此,在 的末尾(t + (t >> 4)) & 0x...0F0FULL,有 8 位组,其中包含原始数字中这些位置中的位数。为了简单起见,我们就称呼这个号码吧s = (t + (t >> 4)) & 0x...0F0FULL。
我们现在要做的(s * 0x...0101ULL) >> 56。请注意,t和 的s大小都是 64 位。这意味着有 8 组 8 位。通过简单的乘法0x...0101ULL(这只是每个组打开的最右边的位),最左边的组现在将是所有组的总和。通过右移 56(即(64 - 8)),我们将最左边的组移动到最右边的位置。这意味着最右边的 8 位组现在具有 的所有组的总和s。但是s的组是数字中这些位置的设置位数,因此,经过>>56运算后,我们得到了原始数字中设置位数的总数。只是(int32)类型转换为较小尺寸的数据类型(实际上足以存储这个数字。
注:某位被设置意味着该位等于 1。