这是什么?getproccount

这是什么?getproccount,c,go,bit-manipulation,C,Go,Bit Manipulation,这段代码是怎么回事?从名称和上下文中可以找到机器上的内核数,但它是如何工作的?那一点点摆弄是为了什么 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

这段代码是怎么回事?从名称和上下文中可以找到机器上的内核数,但它是如何工作的?那一点点摆弄是为了什么

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;
}
static int32
getproccount(无效)
{
uintptr buf[16],t;
int32r,cnt,i;
cnt=0;
r=runtime·sched_getaffinity(0,sizeof(buf),buf);
如果(r>0)
对于(i=0;i>1)和0x55555555);
t=(t&0x3333ull)+(t>>2&0x3333ull);
cnt+=(int32)((t+(t>>4))&0xf0f0full)*0x101010101010101ULL)>>56);
}
返回cnt?cnt:1;
}

注意:忽略
运行时·sched_getaffinity
中的
·
,将该行视为任意库/系统调用,按照名称和参数的含义执行。(在本例中,这个特定调用来自于C的一个微小变化,它处理
·
)。

for循环运行在
buf
数组中填充的元素的数量上。对于这些元素中的每一个,它计算在该元素中设置的位数(摆弄
t
的位就是这样做的),并将其添加到
cnt
中。最后返回
cnt
(如果
cnt
为0,则返回1)

对钻头摆弄的解释: 位摆线1

t=t-((t>>1)和0x5555Ull)
基本上将
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
从上面可以清楚地看出,为什么分组发生在2位中(例如,这里y和z被分组在一起)。现在
t-((t>>1)和0x55555555Ull)
将把每组2位
y z
转换为
(y-z)
。使用一个包含4种可能性(00,01,10,11)的表格,我们可以看到答案是(00,01,01,10),它与该组2位中设置的位数相匹配

位摆线2

移动到下一个位的摆线
t=(t&0x3333ull)+(t>>2&0x3333ull),我们可以看到它将相邻的2组相加为2组

t&0x..33ULL
取每组4位中最右边的2位。
(t>>2)&0x..33ULL
获取每组4位中最左边的2位,并将其右移2。
由于这两组2位是原始数字中设置的位数,因此它将每组4位中设置的位数相加。(即,现在,每组4位具有最初在这些位置设置的位数)

位摆线3

至于最后一位的摆线
cnt+=(int32)((t+(t>>4))&0xf0f0full)*0x101010101010101ULL)>>56),我们可以将其分解为几个部分以便于理解

(int32)(
  (
    (
      (
        t + (t >> 4)
      ) & 0xF0F0F0F0F0F0F0FULL
    ) * 0x101010101010101ULL
  ) >> 56
)
目前,每组4位存储最初在数字中设置的位数。将数字移位4,然后相加,将所有相邻的组相加<代码>&
使用
0x..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。

签出。搜索“并行计数位集”所有这些位操作都是
popcount
函数的一个相当常见的实现……它是在计算缓冲区中设置了多少位。哇。这些代码应该有一个注释来描述它在做什么。@Jongwire项目Go是否使用了自己的C编译器来支持它。回滚注意:我认为这不应该被标记为Go。它是的C代码,它恰好是在旧的Go运行时(后来在Go btw中被重写了)。唯一的非标准C代码是在标识符中使用
(Go ABI使用该代码)。