5

我正致力于在 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

干杯! = )

4

2 回答 2

6

哇,您的结果非常准确:

  • 32 位整数 Q 乘法需要 4 个周期,而浮点数需要 2 个。
  • 32 位整数 Q add 需要 1 个周期,而 float 需要 2 个周期。

不错的实验。

也许您已经知道,但是在为 NEON 编码时要小心:

  • 不要在 NEON 工作繁重时使用 ARM 访问内存
  • 不要将 VFP 指令与 NEON 混合使用。(除了那些共享的)
  • 不要访问 S 寄存器。
  • 不要从 NEON 寄存器转移到 ARM 的

以上所有这些都会导致巨大的打嗝。

祝你好运!

PS:我宁愿针对 A9 进行优化(周期时间略有不同),因为几乎所有新设备都带有 A9。ARM 的 A9 时序图更具可读性。:-)

于 2011-11-08T21:21:45.443 回答
4

我猜(因为我手头没有文档链接)您遇到了管道问题。我知道现在称为 VFPU 的 FPU - err 的管道长度与 CPU 用于执行循环的整数数学部分的管道长度不同。我看到第二个算术运算取决于第一个算术运算,这将停止任一管道并可能暴露您所看到的差异。

另外,我相信乘法不是整数的 1 个周期指令,而是 2-5 个周期,具体取决于第二个值的 msb - 由于数字大小较小,这里的 2 个周期可以解释这种差异。为了验证这一点,从一个更大的乘数开始,看看它是否会在更大的尺寸上变慢。

我还将验证您的代码是否都适合 1 个缓存页面,以消除这种可能性。

我还会查看上面关于双重执行的部分,因为当事情是交叉依赖时,那里也会发生各种管道停顿。

于 2011-11-08T18:38:30.310 回答