Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Assembly 计数寄存器、ARM组件中1数的最快方法_Assembly_Arm - Fatal编程技术网

Assembly 计数寄存器、ARM组件中1数的最快方法

Assembly 计数寄存器、ARM组件中1数的最快方法,assembly,arm,Assembly,Arm,所以我之前有一个关于钻头操纵的采访问题。该公司是一家知名的GPU公司。我几乎没有汇编语言的背景(尽管我是计算机体系结构的博士生,但很奇怪),正如这个故事所表明的,我把它搞砸了。问题很简单: “编写一个快速代码,对32位寄存器中的1进行计数。” 现在我正在学习手臂装配。因此,我很自然地再次讨论了这个问题,通过研究ISA,我就产生了这段代码 对于你们这些武器专家来说,这是正确的吗?有没有更快的方法可以做到这一点?作为初学者,我自然认为这是不完整的。“xx”中的AND指令感觉冗余,但ARM isa中的

所以我之前有一个关于钻头操纵的采访问题。该公司是一家知名的GPU公司。我几乎没有汇编语言的背景(尽管我是计算机体系结构的博士生,但很奇怪),正如这个故事所表明的,我把它搞砸了。问题很简单:

“编写一个快速代码,对32位寄存器中的1进行计数。”

现在我正在学习手臂装配。因此,我很自然地再次讨论了这个问题,通过研究ISA,我就产生了这段代码

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

R1将包含末尾的位数,而R2是包含我们要计数的位数的寄存器。r6只是一个伪寄存器。注释附在()


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

您也可以使用对数方法


有关更多信息,请参阅。

位黑客的最佳参考资料是

  • (印刷)
  • (在线)
Bit-twidlinghacks
page说

The best method for counting bits in a 32-bit
integer v is the following:

v = v - ((v >> 1) & 0x55555555);                    // reuse input as temporary
v = (v & 0x33333333) + ((v >> 2) & 0x33333333);     // temp
c = ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24; // count
然后我建议您使用
gcc
objdump
(或)来查看这个高级代码片段作为arm指令的样子

00000000 <popcount>:
 0: 1043        asrs    r3, r0, #1
 2: f003 3355   and.w   r3, r3, #1431655765 ; 0x55555555
 6: 1ac0        subs    r0, r0, r3
 8: 1083        asrs    r3, r0, #2
 a: f000 3033   and.w   r0, r0, #858993459  ; 0x33333333
 e: f003 3333   and.w   r3, r3, #858993459  ; 0x33333333
12: 18c0        adds    r0, r0, r3
14: eb00 1010   add.w   r0, r0, r0, lsr #4
18: f000 300f   and.w   r0, r0, #252645135  ; 0xf0f0f0f
1c: eb00 2000   add.w   r0, r0, r0, lsl #8
20: eb00 4000   add.w   r0, r0, r0, lsl #16
24: 1600        asrs    r0, r0, #24
26: 4770        bx  lr

此代码是否快速取决于处理器。当然,它在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
主要工作在中完成,它对NEON寄存器中每个字节的位进行计数,并将位计数存储回D0的字节中


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

由于这是标记为ARM的,因此该指令非常有用。这个问题也被描述为人口计数
gcc
对此有详细说明。同样的道理。有(不要对你的解决方案感到不好,有人制作了一个几乎相同的网页),还有一个版本有六条针对非
clz
ARMs的指令。
clz
和可用于生成更快的算法,具体取决于输入

除了很好的阅读建议外,在图形环境中谈论这些事情可能会有所帮助。至少我发现理解一些Qt的blitting代码很有用。然而,它在编码总体计数例程时有一些用处

carry-add
单元在分而治之的意义上很有用,使问题
O(ln)
<如果数据的运行次数为1或0,则code>clz更有用

该条目有更多关于Dave Seal的ARM代码的背景信息

    LDR r0, = 0x000000FF;
    MOV r1, #0;
    MOV r3, #0; this will always be zero
    MOV r2,r0;
rep MOVS r2, r2, LSR #1;
    ADC r1,r1, r3;  this adds r1 with zero plus the carry bit
    CMP r2, #0;
    BNE rep
这样就可以了,r3只是一个0的虚拟寄存器,以使ADC正常工作。

长计数位长(long)


看起来汉明体重是个众所周知的问题。谢谢+1用于汉明重量物品。请注意,特别是对于ARM,它提到Neon SIMD指令提供了
vcnt
,以准确执行此操作。对于SIMD类型的指令集,也有。使用洗牌指令作为表查找来加速它的方法(每个SSE2寄存器可以通过
pshufb
,或在ARM Neon的情况下,
vtbl
,用作16字节的查找表)。查看什么是最快的还取决于您希望设置的位数。只有很少的1,它将赢得每一位集运行一轮循环。您的算法对所有
1
执行
4*32+2
指令。Dave Seal的算法需要六条指令和一次内存访问;除非内存非常慢,否则对于所有类型的输入,它可能都一样快。我怀疑很少有人会接受戴夫·西尔的解决方案。
reverse subtract
对我来说太奇怪了。我认为uuu builtin\u popcount是从查找表实现中编译的,应该足够好了,但没有专门为arm设计的。@auselen:检查您针对的arm体系结构级别
clz
仅在ARMv5之后才可用。@IgorSkochinsky我认为没有由
gcc
llvm
提供的
clz
实现
\uuuu builtin\u popcount()
链接到
\uuuupopcountsi2
。好吧,popcount做的不仅仅是
clz
,所以它需要一个助手例程
\uuuu builtin\u clz
\uuuuu builtin\u ctz
直接为支持的体系结构发出
clz
。例如,二进制图像/图片数据的人口计数可能受益于
clz
,特别是当它有大量低频内容时。我认为这个问题是在图形的背景下提出的。如何将32位常量放入32位指令?@LưuVĩnhPhúc说什么?像这样
和.w r0,r0,#252645135;0xF0F
常量的大小为32bits@L嗯,这是编译器的输出,所以我认为这是非常可能的。这个想法是,若常数的熵很低,你们可以把它塞进指令中。应该有一种有效的方法将0x8000000加载到寄存器中,否则它将是一个糟糕的ISA。
和.w
指令不是普通指令,您可以从操作码的条件字段中的
f
前缀看出。在这种情况下,它被赋予一个单字节的立即数,
0x0f
,它应用于寄存器输入的所有4个字节,有效地
0x0f0f
vmov.32 r0,d0[0]
导致NEON和CoreShifting之间的同步延迟非常大,一次1位并不快。@Avsharian:“CMP”在这里是多余的(你知道为什么吗?)。与r3相同:#0足以“使ADC正常工作”(至于最初的“LDR”,我只是想知道。。
  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
    LDR r0, = 0x000000FF;
    MOV r1, #0;
    MOV r3, #0; this will always be zero
    MOV r2,r0;
rep MOVS r2, r2, LSR #1;
    ADC r1,r1, r3;  this adds r1 with zero plus the carry bit
    CMP r2, #0;
    BNE rep
    vmov.32 d0[0], r0       // R0 --> SIMD

    vcnt.8  d0, d0          // count bits in bytes
    vpaddl.u8 d0, d0        // add adjacent pairs of bytes and put into 16b words
    vpaddl.u16 d0, d0       // add adjacent pairs of 16b words and put into 32b word

    vmov.32 r0, d0[0]       // SIMD --> R0

    mov pc, lr              // return