为什么在没有特殊代码路径的情况下设置 LOCAL_ARM_NEON 会使速度加倍?

Why does setting LOCAL_ARM_NEON double the speed without special code-paths?

我在 Android 上使用 NDK 来处理一些繁重的数字代码,使用 LLVM 工具链。

我发现,如果我在我的 Android.mk 中设置 LOCAL_ARM_NEON := true,我的代码将获得近 50% 的加速。我 没有 编写任何 NEON 特定的源文件,并且我的代码中没有 NEON 内在函数。这是否意味着编译器会自动将 NEON 指令注入其代码?

如果是这种情况,那么因为这都是在编译器中生成的,所以我无法通过检查硬件支持来包装 NEON 代码路径。这里有最佳实践吗?或者 LOCAL_ARM_NEON := true 从根本上来说是不安全的?

更多细节:(注意,我在阅读 ARM 程序集方面经验不足)

生成程序集的比较。

我发现这里较慢的程序集可读性很强。我发现更快的汇编非常难以阅读。 我也不知道它里面是否有 NEON 特定的指令,因为两个生成的文件似乎都有 vmul 指令,其中 this page claims 是 NEON 特定的 编辑:显然 vmul 不是 NEON 特定的。

较慢的代码(未设置 LOCAL_ARM_NEON 标志)

00000000 <_dotProduct>:
   0:   ed9f 0a08   vldr    s0, [pc, #32]   ; 24 <_dotProduct+0x24>
   4:   2a01        cmp r2, #1
   6:   bfb8        it  lt
   8:   4770        bxlt    lr
   a:   ed90 1a00   vldr    s2, [r0]
   e:   3004        adds    r0, #4
  10:   ed91 2a00   vldr    s4, [r1]
  14:   3104        adds    r1, #4
  16:   3a01        subs    r2, #1
  18:   ee22 1a01   vmul.f32    s2, s4, s2
  1c:   ee31 0a00   vadd.f32    s0, s2, s0
  20:   d1f3        bne.n   a <_dotProduct+0xa>
  22:   4770        bx  lr
  24:   00000000    .word   0x00000000

更快的代码(LOCAL_ARM_NEON := true

00000000 <_dotProduct>:
   0:   b510        push    {r4, lr}
   2:   2a01        cmp r2, #1
   4:   db1b        blt.n   3e <_dotProduct+0x3e>
   6:   2a00        cmp r2, #0
   8:   d01c        beq.n   44 <_dotProduct+0x44>
   a:   efc0 0050   vmov.i32    q8, #0  ; 0x00000000
   e:   f022 0c03   bic.w   ip, r2, #3
  12:   f1bc 0f00   cmp.w   ip, #0
  16:   d01a        beq.n   4e <_dotProduct+0x4e>
  18:   46e6        mov lr, ip
  1a:   460b        mov r3, r1
  1c:   4604        mov r4, r0
  1e:   f964 2a8f   vld1.32 {d18-d19}, [r4]
  22:   f1be 0e04   subs.w  lr, lr, #4
  26:   f104 0410   add.w   r4, r4, #16
  2a:   f963 4a8f   vld1.32 {d20-d21}, [r3]
  2e:   f103 0310   add.w   r3, r3, #16
  32:   ff44 2df2   vmul.f32    q9, q10, q9
  36:   ef42 0de0   vadd.f32    q8, q9, q8
  3a:   d1f0        bne.n   1e <_dotProduct+0x1e>
  3c:   e009        b.n 52 <_dotProduct+0x52>
  3e:   ef80 0010   vmov.i32    d0, #0  ; 0x00000000
  42:   bd10        pop {r4, pc}
  44:   ef80 0010   vmov.i32    d0, #0  ; 0x00000000
  48:   f04f 0c00   mov.w   ip, #0
  4c:   e00b        b.n 66 <_dotProduct+0x66>
  4e:   f04f 0c00   mov.w   ip, #0
  52:   eff0 28e0   vext.8  q9, q8, q8, #8
  56:   4594        cmp ip, r2
  58:   ef40 0de2   vadd.f32    q8, q8, q9
  5c:   fffc 2c60   vdup.32 q9, d16[1]
  60:   ef00 0de2   vadd.f32    q0, q8, q9
  64:   d011        beq.n   8a <_dotProduct+0x8a>
  66:   eb01 018c   add.w   r1, r1, ip, lsl #2
  6a:   eb00 008c   add.w   r0, r0, ip, lsl #2
  6e:   eba2 020c   sub.w   r2, r2, ip
  72:   ed90 2a00   vldr    s4, [r0]
  76:   3004        adds    r0, #4
  78:   ed91 3a00   vldr    s6, [r1]
  7c:   3104        adds    r1, #4
  7e:   3a01        subs    r2, #1
  80:   ff43 0d12   vmul.f32    d16, d3, d2
  84:   ef00 0d80   vadd.f32    d0, d16, d0
  88:   d1f3        bne.n   72 <_dotProduct+0x72>
  8a:   bd10        pop {r4, pc}

好的,我将根据@Michael 和@Notlikethat 的有用评论自己回答这个问题。那么,我的加速是因为 NEON 指令(当然)。

似乎设置 LOCAL_ARM_NEON := true 允许编译器生成 NEON 指令,即使对于非 .neon 文件也是如此。这将使代码无法移植到不支持 NEON 的 ARMv7。

我认为这给了我两个选择,一个:使用和不使用 LOCAL_ARM_NEON := true 编译我的 lib 版本,并在 Java 中根据是否 CPU 决定加载哪个版本支持霓虹灯。

第二个是设置LOCAL_ARM_NEON := true,而是将我对性能敏感的代码路径复制到.c.neon文件中(这将允许只有 文件编译时支持 NEON。然后,在主文件中,使用 cpufeatures 库检测 NEON 支持并切换到该文件(如果可用)。