【问题标题】:Bit Twiddling in C - Counting BitsC语言中的Bit Twiddling - 计数位
【发布时间】:2012-03-05 21:18:01
【问题描述】:

我想计算一个非常大的位向量(即 100,000 位)中设置的位。

我目前正在做的是使用指向 char 的指针(即 char *cPtr)来指向位数组的开头。我然后:

1. look at each element of the array (i.e. cPtr[x]),   
2. convert it to an integer (i.e. (int) cPtr[x])   
3. use a 256 element look-up table to see how many bits are set in the given byte (i.e. cPtr[x]). 

我突然想到,如果我使用一个短整型指针(即短整型 * sPtr),那么我只需要一半的查找次数,但使用一个 65534 元素的查找表,它将有它的自己的内存使用成本。

我想知道每次检查的最佳位数是多少。此外,如果该数字不是某些预设类型的大小,我如何才能遍历我的位向量并将指针设置为 ANY 位数组起始位置之后的任意位数。

我知道还有其他方法可以计算位数,但现在我想确定我可以在与其他方法进行比较之前优化此方法。

【问题讨论】:

  • 这个网站可能值得一看:graphics.stanford.edu/~seander/bithacks.html
  • 测试是找出特定机器性能的唯一方法。您几乎肯定会发现 8 的倍数对您来说是最有效的,因为移位的开销很大,而且由于缓存局部性,可能正好是 8 位。

标签: c bit-manipulation bitcount


【解决方案1】:

您可以使用按位运算来计算它:

char c = cPtr[x];
int num = ((c & 0x01) >> 0) +
          ((c & 0x02) >> 1) +
          ((c & 0x04) >> 2) +
          ((c & 0x08) >> 3) +
          ((c & 0x10) >> 4) +
          ((c & 0x20) >> 5) +
          ((c & 0x40) >> 6) +
          ((c & 0x80) >> 7);

它可能看起来有点长,但它不需要多次访问内存,所以对我来说毕竟它看起来很便宜

您甚至可以通过每次读取一个 int 来降低成本,但是您可能需要解决对齐问题。

【讨论】:

  • 我不确定。似乎要计算一个字节中设置的位数,我已经用 1 次内存访问(即查表)换取了 8 次移位和 7 次加法。我错过了什么吗?
  • 我认为在大多数架构中位操作会更快,但我没有测量。
【解决方案2】:

我想知道每次检查的最佳位数是多少

找出答案的唯一方法是测试。一次见this question for a discussion of the fastest way to count 32 bits

另外,如果那个数字不是某些预设类型的大小,我怎么能 走下我的位向量并将指针设置为任意数字 位数组起始位置之后的位数。

您不能将指针设置为任意位。大多数机器都有字节寻址,有些只能寻址字。

可以构造一个以任意位开头的单词,如下所示:

long wordAtBit(int32_t* array, size_t bit)
{
    size_t idx = bit>>5;
    long word = array[idx] >> (bit&31);
    return word | (array[idx+1] << (32 - (bit&31));
}

【讨论】:

  • 谢谢 - 我忘记了字节寻址。但这很有意义。现在我只需要比较我现有的两种方法,然后尝试理解汉明权重方法,您提供了一个链接。
【解决方案3】:

这应该很快(取自Wikipedia):

static unsigned char wordbits[65536] = { bitcounts of ints between 0 and 65535 };
static int popcount(uint32 i)
{
    return (wordbits[i&0xFFFF] + wordbits[i>>16]);
}

通过这种方式,您可以每次迭代检查 32 位。

【讨论】:

    【解决方案4】:

    我参加聚会有点晚了,但有比目前建议的方法更快的方法。原因是许多现代架构提供硬件指令来以各种方式计算位数(前导零、前导 1、尾随 0 或 1、计算设置为 1 的位数等)。计算设置为 1 的位数称为 Hamming 权重,通常也称为人口计数,或简称 popcount。

    事实上,x86 CPU 有一条 POPCNT 指令作为 SSE4.2 指令集的一部分。 Intel 最新的 CPU 架构(昵称 Haswell)通过 BMI1 和 BMI2 扩展为位操作提供了更多的硬件支持 - 也许还有其他东西可以使用!

    【讨论】:

      猜你喜欢
      • 2011-07-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多