这是什么?getproccount

huangapple go评论90阅读模式
英文:

What is this? getproccount

问题

这段代码是用来获取机器上的核心数量的。代码中的位操作用于计算核心数量。具体来说,代码的执行过程如下:

  1. 声明了一个名为getproccount的静态函数,返回类型为int32
  2. 创建了一个长度为16的uintptr类型的数组buf,以及一些辅助变量trcnti
  3. cnt初始化为0。
  4. 调用runtime·sched_getaffinity函数,获取当前进程的亲和性信息,并将结果存储在buf中。runtime·sched_getaffinity是一个库/系统调用,用于获取进程的亲和性信息。
  5. 如果调用成功(返回值大于0),则进入循环,遍历buf数组。
  6. 在循环中,首先将buf[i]的值赋给变量t
  7. 接下来的几行代码使用位操作计算t中包含的位数,即计算t中1的个数。这个过程使用了一种称为"位计数"的算法,通过不断地将t的一半与掩码进行与运算,并将结果累加到t中,最终得到t中1的个数。
  8. 最后一行代码将计算得到的1的个数累加到cnt中。
  9. 返回cnt的值,如果cnt为0,则返回1。

需要注意的是,代码中的runtime·sched_getaffinity是一个虚拟的函数调用,实际上是一个库/系统调用,用于获取进程的亲和性信息。

英文:

What is going on in this code? From the name and the context it's finding the number of cores on the machine, but how does it work? What's all that bit fiddling for?

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;
}

Note: ignore the · in runtime·sched_getaffinity, think of that line as just an arbitrary library/system call that does what the name and arguments imply. (In this case this specific call comes from the old pre-Go1.4 runtime written in a slight variation of C that deals with ·).

答案1

得分: 2

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

从上面可以清楚地看到为什么分组是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
)

当前,每组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。

英文:

The for loop runs over as many elements that get filled into the buf array. For each of those elements, it calculates the number of bits that are set in that element (the bit fiddling with t does just that) and that is added into cnt. At the end cnt is returned (or 1 if cnt is 0).

Explanation for the bit fiddling:

Bit fiddling line 1

The line t = t - ((t >> 1) & 0x5555555555555555ULL); basically groups off the bits of t into 2 bits and replaces each group with the count of the number of set bits in that group. This works like follows:

Consider t = ... w x y z where w,x,y,z are individual bits. Then

t                 = ... w x y z
t>>1              = ..... w x y
t>>1 & 0x...55ULL = ... 0 w 0 y

From above, it is clear to see why the grouping happens into 2 bits (for example, y and z get grouped together here). Now t - ((t >> 1) & 0x5555555555555555ULL) will have each group of 2 bits y z transformed to (y-z). Using a table of the 4 possibilities (00, 01, 10, 11), we see that the answers are (00, 01, 01, 10) which matches with the number of bits set in that group of 2 bits.

Bit fiddling line 2

Moving on to the next bit fiddling line t = (t & 0x3333333333333333ULL) + ((t >> 2) & 0x3333333333333333ULL);, we can see that it is adding up adjacent groups of 2 in groups of 2.

t & 0x..33ULL takes the rightmost 2 bits of each group of 4 bits.
(t>>2) & 0x..33ULL takes the leftmost 2 bits of each group of 4 bits and shifts them right by 2.
Since these two groups of 2 bits were the number of bits set in the original number, it has added up the number of bits set in every group of 4 bits. (i.e. now, each group of 4 bits has the number of bits that were set originally in those positions)

Bit fiddling line 3

As for the last bit fiddling line cnt += (int32)((((t + (t >> 4)) & 0xF0F0F0F0F0F0F0FULL) * 0x101010101010101ULL) >> 56);, we can break it down into a few parts for easier understanding.

(int32)(
  (
    (
      (
        t + (t >> 4)
      ) & 0xF0F0F0F0F0F0F0FULL
    ) * 0x101010101010101ULL
  ) >> 56
)

Currently, each group of 4 bits stores the number of bits that were set originally in the number. Shifting the number over by 4 and adding would add all adjecent groups together. &ing with 0x..0F0FULL picks out the right 4 bits of each group of 8 bits. Hence, at the end of (t + (t >> 4)) & 0x...0F0FULL, there are groups of 8 bits which contain the number of bits that were there in those locations in the original number. Let's just call this number s = (t + (t >> 4)) & 0x...0F0FULL for simplicity.

We now have to do (s * 0x...0101ULL) >> 56. Notice that t and thus s are 64 bits in size. This means that there are 8 groups of 8 bits. By simple multiplication with 0x...0101ULL (which is just the rightmost bit turned on for each group), the leftmost group will now be the sum of all the groups. By shifting right by 56 (i.e. (64 - 8)), we move this leftmost group into the rigthmost position. This means that the rightmost group of 8 bits now has the sum of all the groups of s. But s's groups were the number of set bits in those locations in the number, therefore, after the >>56 operation, we have the total number of set bits in the original number. The (int32) is just typecasting to a lower size datatype (which is actually enough to store this number.

Note: A bit being set means that the bit is equal to 1.

huangapple
  • 本文由 发表于 2015年5月23日 03:54:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/30404905.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定