这是C++代码:

#define ARR_SIZE_TEST ( 8 * 1024 * 1024 )

void cpp_tst_add( unsigned* x, unsigned* y )
{
    for ( register int i = 0; i < ARR_SIZE_TEST; ++i )
    {
        x[ i ] = x[ i ] + y[ i ];
    }
}

这是一个 NEON 版本:
void neon_assm_tst_add( unsigned* x, unsigned* y )
{
    register unsigned i = ARR_SIZE_TEST >> 2;

    __asm__ __volatile__
    (
        ".loop1:                            \n\t"

        "vld1.32   {q0}, [%[x]]             \n\t"
        "vld1.32   {q1}, [%[y]]!            \n\t"

        "vadd.i32  q0 ,q0, q1               \n\t"
        "vst1.32   {q0}, [%[x]]!            \n\t"

        "subs     %[i], %[i], $1            \n\t"
        "bne      .loop1                    \n\t"

        : [x]"+r"(x), [y]"+r"(y), [i]"+r"(i)
        :
        : "memory"
    );
}

测试功能:
void bench_simple_types_test( )
{
    unsigned* a = new unsigned [ ARR_SIZE_TEST ];
    unsigned* b = new unsigned [ ARR_SIZE_TEST ];

    neon_tst_add( a, b );
    neon_assm_tst_add( a, b );
}

我已经测试了这两种变体,下面是一份报告:
add, unsigned, C++       : 176 ms
add, unsigned, neon asm  : 185 ms // SLOW!!!

我还测试了其他类型:
add, float,    C++       : 571 ms
add, float,    neon asm  : 184 ms // FASTER X3!

问题:
为什么 NEON 在32位整数类型时速度较慢?

我将最新版本的GCC用于Android NDK。 NEON优化标志已打开。
这是反汇编的C++版本:
                 MOVS            R3, #0
                 PUSH            {R4}

 loc_8
                 LDR             R4, [R0,R3]
                 LDR             R2, [R1,R3]
                 ADDS            R2, R4, R2
                 STR             R2, [R0,R3]
                 ADDS            R3, #4
                 CMP.W           R3, #0x2000000
                 BNE             loc_8
                 POP             {R4}
                 BX              LR

这是 NEON 的拆卸版本:
                 MOV.W           R3, #0x200000
.loop1
                 VLD1.32         {D0-D1}, [R0]
                 VLD1.32         {D2-D3}, [R1]!
                 VADD.I32        Q0, Q0, Q1
                 VST1.32         {D0-D1}, [R0]!
                 SUBS            R3, #1
                 BNE             .loop1
                 BX              LR

这是所有基准测试:
add, char,     C++       : 83  ms
add, char,     neon asm  : 46  ms FASTER x2

add, short,    C++       : 114 ms
add, short,    neon asm  : 92  ms FASTER x1.25

add, unsigned, C++       : 176 ms
add, unsigned, neon asm  : 184 ms SLOWER!!!

add, float,    C++       : 571 ms
add, float,    neon asm  : 184 ms FASTER x3

add, double,   C++       : 533 ms
add, double,   neon asm  : 420 ms FASTER x1.25

问题:
为什么 NEON 在32位整数类型时速度较慢?

最佳答案

Cortex-A8上的NEON管道按顺序执行,并且未命中次数有限(无重命名),因此受内存延迟限制(因为您使用的缓存大小超过L1/L2)。您的代码直接依赖于从内存加载的值,因此它将不断停滞以等待内存。这可以解释为什么NEON代码比非NEON稍慢(很小)。

您需要展开组装循环并增加负载与使用之间的距离,例如:

vld1.32   {q0}, [%[x]]!
vld1.32   {q1}, [%[y]]!
vld1.32   {q2}, [%[x]]!
vld1.32   {q3}, [%[y]]!
vadd.i32  q0 ,q0, q1
vadd.i32  q2 ,q2, q3
...

NEON 寄存器很多,因此您可以展开很多东西。整数代码在较小程度上会遇到相同的问题,因为A8整数具有更好的命中率不足而不是停顿。与L1/L2缓存相比,如此大的基准测试的瓶颈将是内存带宽/延迟。您可能还希望以较小的大小(4KB..256KB)运行基准测试,以查看将数据完全缓存在L1和/或L2中时的效果。

关于c++ - 为什么ARM NEON不比普通C++快?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/5729964/

10-11 20:38