最快的方法是在ARM程序集的寄存器中计数1

所以在进行位操作之前我有一个面试问题。 该公司是一家知名的GPU公司。 我在汇编语言方面的背景很少(尽管我是计算机体系结构的博士生,但很奇怪),正如这篇叙述所指出的那样,我对它进行了拙劣的处理。 这个问题很简单:

“写一个快速的代码,将在一个32位寄存器中计数1的个数。”

现在我正在研究手臂装配过程。 所以很自然我再次重新讨论这个问题,并通过研究ISA来提出这个代码。

因为你在那里武装专家,这是正确的吗? 有没有更快的方法来做到这一点? 作为初学者,我自然认为这是不完整的。 “xx”中的AND指令感觉多余,但没有其他方法可以在ARM isa中移位寄存器...

R1将包含结尾的位数,而R2是具有我们要计数的位的寄存器。 r6只是一个虚拟寄存器。 评论包含在()中

    MOV   R1, #0                (initialize R1 and R6 to zero)
    MOV   R6, #0        
xx: AND   R6, R6, R2, LSR #1    (Right shift by 1, right most bit is in carry flag)
    ADDCS R1, #1                (Add #1 to R1 if carry  flag is set)
    CMP R2, #0                  (update the status flags if R2 == 0 or not)
    BEQ xx                      (branch back to xx until R2==0)

您可以使用预先计算的查找表并将迭代次数减少到2或4。

你也可以使用对数方法。

欲了解更多信息,请参阅维基百科文章。


如果此代码速度很快取决于处理器。 Cortex-A8的确不会很快,但在Cortex-A9和更新的CPU上运行速度可能非常快。

然而,这是一个非常短的解决方案。

期望在r0中输入,并在r0中返回输出

  vmov.32 d0[0], r0
  vcnt.8  d0, d0
  vmov.32 r0, d0[0]

  add r0, r0, r0, lsr #16
  add r0, r0, r0, lsr #8
  and r0, r0, #31

主要工作是在vcnt.8指令中完成的,该指令对NEON寄存器中每个字节的位进行计数,并将bitcount存回D0的字节。

没有vcnt.32表单,只有.8 ,所以你需要将4个字节水平地添加在一起,这就是其余代码所做的。


由于这是标记ARM, clz指令是最有帮助的。 这个问题也被描述为人口数量。 gcc有__builtin_popcount()这个。 与ARM工具一样。 有这样的链接(对你的解决方案不要感到clz ,有些网站的网页几乎相同),还有Dave Seal的版本,其中包含针对非clz ARM的六条指令。 根据clz是有利的,可用于生成更快的算法。

除了auselen的良好阅读建议之外,Hacker's Delight这个有趣的博客也许有用,它在图形背景下讨论这些问题。 至少我发现了解Qt的一些传输代码很有用。 然而,它在编码人口计数例程方面有一定用处。

carry add单位在分而治之意义上是有用的,使得问题O(ln n) 。 如果数据运行1或0,则clz更有用。

Hacker's Delight条目对Dave Seal的ARM代码有更多背景。

链接地址: http://www.djcxy.com/p/12595.html

上一篇: Fastest way to count number of 1s in a register, ARM assembly

下一篇: How does this implementation of bitset::count() work?