Что это? getproccount

Что происходит в этом коде? Из названия и контекста он находит количество ядер на машине, но как это работает? Зачем все это битку?

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

Примечание: игнорировать · в runtime·sched_getaffinity, подумайте об этой строке как о произвольном библиотечном/системном вызове, который делает то, что подразумевает имя и аргументы. (В этом случае этот конкретный вызов происходит от старой версии времени pre-Go1.4, написанной в небольшом варианте C, который имеет дело с ·).

Ответы

Ответ 1

В цикле for выполняется столько элементов, которые заполняются в массив buf. Для каждого из этих элементов он вычисляет количество бит, которые заданы в этом элементе (бит-скрипт с t делает именно это), и который добавляется в cnt. В конце возвращается cnt (или 1, если cnt равно 0).

Объяснение для бит-бит:

Строка бит-бит 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 принимает самые правые 2 бита каждой группы из 4 бит.
(t>>2) & 0x..33ULL принимает самые левые 2 бита каждой группы из 4 бит и сдвигает их вправо на 2.
Поскольку эти две группы из 2 битов были числом бит, установленным в исходном номере, оно добавило количество бит, установленных в каждой группе из 4 бит. (то есть теперь каждая группа из 4 бит имеет количество бит, которые первоначально были установлены в этих позициях)

Строка бит-бит 3

Что касается последней строки бит-бит cnt += (int32)((((t + (t >> 4)) & 0xF0F0F0F0F0F0F0FULL) * 0x101010101010101ULL) >> 56);, мы можем разбить ее на несколько частей для более легкого понимания.

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

В настоящее время каждая группа из 4 бит хранит количество бит, которые первоначально были установлены в номере. Сдвигая число на 4 и добавляя вместе все связанные группы. & ing с 0x..0F0FULL выбирает правильные 4 бита каждой группы из 8 бит. Следовательно, в конце (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.