我正致力于在 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
vadd
请注意指定为向量 add ( ) 和有符号 32 位 int ( )的计算操作和数据类型s32
。此操作在一定时间内完成(见下表)。根据此 ARM Cortex-A8 文档和后续页面,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
?
干杯! = )