为什么选择一种位计数方法而不是另一种?好吧,这实际上取决于您的机器和您要解决的问题。请注意,我在下面给出的所有指令计数都是针对基本 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 位宽的字段或使用浮点运算符。请注意,我上面介绍的大部分分析也部分取自那本书。
事实是,有一大堆方法,而确定哪种方法在您的特定情况下效果最好的唯一方法是测量和比较。我确实意识到这是一个非常固定的答案,但另一种方法是彻底了解您的处理器和编译器。