英文:
What is this? getproccount
问题
这段代码是用来获取机器上的核心数量的。代码中的位操作用于计算核心数量。具体来说,代码的执行过程如下:
- 声明了一个名为
getproccount
的静态函数,返回类型为int32
。 - 创建了一个长度为16的
uintptr
类型的数组buf
,以及一些辅助变量t
、r
、cnt
和i
。 - 将
cnt
初始化为0。 - 调用
runtime·sched_getaffinity
函数,获取当前进程的亲和性信息,并将结果存储在buf
中。runtime·sched_getaffinity
是一个库/系统调用,用于获取进程的亲和性信息。 - 如果调用成功(返回值大于0),则进入循环,遍历
buf
数组。 - 在循环中,首先将
buf[i]
的值赋给变量t
。 - 接下来的几行代码使用位操作计算
t
中包含的位数,即计算t
中1的个数。这个过程使用了一种称为"位计数"的算法,通过不断地将t
的一半与掩码进行与运算,并将结果累加到t
中,最终得到t
中1的个数。 - 最后一行代码将计算得到的1的个数累加到
cnt
中。 - 返回
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论