我正在研究在Android上编写几种实时DSP算法,因此我决定直接在Assembly中对ARM进行编程,以尽可能地优化所有内容,并使数学最大化。起初我得到的速度基准并没有多大意义,所以我开始阅读有关管道危险,双重问题的能力等等。我仍然对我得到的一些数字感到困惑,所以我将它们发布在这里,希望有人可以解释为什么我得到的东西。特别是,我很感兴趣为什么NEON需要不同的时间来运行不同数据类型的计算,即使它声称在一个周期内完成每个操作。我的发现如下。
我正在使用一个非常简单的循环进行基准测试,并且我运行了2,000,000次迭代。这是我的功能:
hzrd_test:
@use received argument an number of iterations in a loop
mov r3 , r0
@come up with some simple values
mov r0, #1
mov r1, #2
@Initialize some NEON registers (Q0-Q11)
vmov.32 d0, r0, r1
vmov.32 d1, r0, r1
vmov.32 d2, r0, r1
...
vmov.32 d21, r0, r1
vmov.32 d22, r0, r1
vmov.32 d23, r0, r1
hzrd_loop:
@do some math
vadd.s32 q0, q0, q1
vadd.s32 q1, q0, q1
vadd.s32 q2, q0, q1
vadd.s32 q3, q0, q1
vadd.s32 q4, q0, q1
vadd.s32 q5, q0, q1
vadd.s32 q6, q0, q1
vadd.s32 q7, q0, q1
vadd.s32 q8, q0, q1
vadd.s32 q9, q0,s q1
vadd.s32 q10, q0, q1
vadd.s32 q11, q0, q1
@decrement loop counter, branch to loop again or return
subs r3, r3, #1
bne hzrd_loop
@return
mov r0, r3
mov pc, lr
注意指定为vector add(vadd
)和signed 32-bit int(s32
)的计算操作和数据类型。此操作在一定时间内完成(参见下面的结果表)。根据{{3}}以及后面几页,几乎所有NEON中的基本算术运算都应该在一个周期内完成,但这就是我得到的:
vmul.f32 ~62ms vmul.u32 ~125ms vmul.s32 ~125ms vadd.f32 ~63ms vadd.u32 ~29ms vadd.s32 ~30ms
我通过简单地替换上面循环中所有操作和数据类型来完成它们。是否有vadd.u32
比vadd.f32
快两倍且vmul.f32
比vmul.u32
快两倍的原因?
干杯! =)
答案 0 :(得分:6)
很好的实验。
也许你已经知道,但在为NEON编码时要小心:
以上所有这些都会导致巨大的打嗝。
祝你好运!
PS:我宁愿优化A9(稍微不同的周期时间)因为几乎所有新设备都带有A9。 ARM的A9时序图更具可读性。 : - )答案 1 :(得分:4)
我会猜测(因为我没有方便的文档链接)你正在遇到管道问题。我知道现在称为VFPU的FPU - err具有与CPU执行循环的整数数学部分不同的管道长度。我看到第二个算术运算依赖于第一个算法操作将阻止任何一个管道,并可能暴露你所看到的差异。
另外,我认为乘法不是针对整数的1周期指令,而是取决于第二个值的msb的2-5个周期 - 这里是2个周期,因为数字大小可以解释这种差异。要验证这一点,请从较大的乘号开始,看看它是否在较大的尺寸上减慢。
我还要验证您的代码是否都适合1个缓存页面,以消除这种可能性。
我还会查看上面关于双重执行的部分,因为当事物是交叉相关的时,也会发生各种各样的管道停滞。