c++ - 何时使用并行计数 - 当内存有问题时使用 MIT HAKMEM 进行位计数?

标签 c++ bit-manipulation bitcount

比特计数可以通过多种方式完成,例如。带有设置位迭代器、未设置位迭代器、带有查找表或并行计数的预计算位。正如我通过搜索网络发现的那样,当未设置位较少时,未设置位迭代器速度很快,而设置位迭代器则相反。但是什么时候应该使用并行计数,尤其是 MIT HAKMEM(见下文)?它看起来相当快,尽管可能比查找表慢。就速度而言,它总是比设置/未设置位更好吗?除了速度和内存之外,还有其他关于选择哪一个的问题吗?

 int BitCount(unsigned int u) {
     unsigned int uCount;

     uCount = u - ((u >> 1) & 033333333333) - ((u >> 2) & 011111111111);
     return ((uCount + (uCount >> 3)) & 030707070707) % 63;
 }

最佳答案

为什么选择一种位计数方法而不是另一种?好吧,这真的取决于您的机器和您要解决的问题。请注意,我在下面给出的所有指令数都是针对基本 RISC 处理器的,可能无法很好地转换为 x86 等更复杂的野兽。

您引用的 HAKMEM 算法将在 13 条指令中执行,但由于模运算符的原因不太可能很快。通过目测,它看起来确实具有一些非常好的指令级并行性,如果您的处理器能够利用它,这应该会有所帮助。

Bo Persson 提出的算法非常快(2 + 5*pop(x) 指令),但前提是单词是稀疏填充的。它还可以修改以处理人口稠密的单词。它还包含分支并且没有任何重要的指令级并行性。

编辑: 表查找方法也可以非常快,但确实会进行内存访问。如果整个表都在 L1 缓存中,那么它可能是最快的算法之一。如果表不在缓存中,那么它几乎肯定是最慢的表之一。

下面的算法是 HAKMEM 算法之一的变体,在书 Hacker's Delight 中有介绍。 (如果你喜欢这类东西,我强烈推荐这本书)。它执行 19 条指令并且是无分支的。它也不使用除法,但确实有乘法。它通过尽可能重复使用相同的掩码来使用寄存器的方式也非常经济。我仍然看不到明显的指令级并行性。

int pop(unsigned x) {
  unsigned n;

  n = (x >> 1) & 0x77777777;
  x = x - n;
  n = (n >> 1) & 0x77777777;
  x = x - n;
  n = (n >> 1) & 0x77777777;
  x = x - n;
  x = (x + (x >> 4)) & 0x0F0F0F0F;
  x = x * 0x01010101;
  return x >> 24;
}

《Hacker's Delight》一书还介绍了一些针对 9-8-7 位宽字段或使用浮点运算符的更专业的算法。请注意,我上面介绍的大部分分析也部分取自那本书。

事实上,有很多方法,要确定哪种方法最适合您的特定情况,唯一的方法就是测量和比较。我确实意识到这是一个很好的固定答案,但另一种选择是彻底了解您的处理器和编译器。

关于c++ - 何时使用并行计数 - 当内存有问题时使用 MIT HAKMEM 进行位计数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8590432/

相关文章:

c++ - 从函数返回 shared_ptr 的取消引用值

c - 如何在不更改其他位的情况下更改 32 位寄存器的特定位?

c - C中的算术右移

mysql将varchar二进制表示字段转为二进制用bit_count做汉明距离计算

binary - 计算 Redshift 列中每个位位置中 '1' 值的数量

c++ - vfork导致内存 "free()"崩溃,但是fork却没有,这是怎么回事?

c++ - 如何使用修饰键组合?

c++ - 检查是否至少设置了一点而不跳转

c++ - 从文件读取文本到无符号字符数组,尝试使用示例时出错