在简单的加法任务中使用 ARM NEON 速度较慢

Posted

技术标签:

【中文标题】在简单的加法任务中使用 ARM NEON 速度较慢【英文标题】:Using ARM NEON is slower in a simple Addition task 【发布时间】:2020-01-07 18:40:02 【问题描述】:

我尝试编写一段简单的 NEON 代码,但发现它比常规 C++ 实现要慢。代码如下

float A[] = 1,2,3,4; 
float B[] = 2,3,4,5; 
float32x4_t v1;
float32x4_t v2;

int counter = 1000000;

while(counter--)
     v1 = vld1q_f32(A);
     v2 = vld1q_f32(B);
     v = vaddq_f32(v1,v2);

     vst1q_f32(A,v);
     // A[0] = A[0]+B[0]; // regular implementation
     // A[1] = A[1]+B[1]; // regular implementation 
     // A[2] = A[2]+B[2]; // regular implementation
     // A[3] = A[3]+B[3]; // regular implementation

 

我搜索了原因,所以我猜是因为顺序管道和这个简单的任务导致 CPU 停顿?但是有人能帮忙解释得更详细吗?有什么方法可以提高这个 NEON 实现的性能?还是在面对这种简单的任务时使用常规实现比使用 NEON 更好?谢谢你。

【问题讨论】:

【参考方案1】:

你的测试程序一开始就完全有缺陷:

由于编译器在构建时可以清楚地看到所有输入,因此编译器将简单地生成类似于以下的机器代码:

A[0] = 3.0f;
A[1] = 5.0f;
A[2] = 7.0f;
A[3] = 9.0f;

为了防止编译器进行这种作弊,你必须隐藏输入:

void myFunc_c(float *pA, float *pB, uint32_t count)

    if (count == 0) return;

    do 
        *pA++ += *pB++;
     while (--count);


void myFunc_neon(float *pA, float *pB, uint32_t count)

    float32x4_t a, b;

    count >>= 2;
    if (count == 0) return;

    do 
        a = vld1q_f32(pA);
        b = vld1q_f32(pB);

        a = vaddq_f32(a, b);

        vst1q_f32(pA, a);

        pA += 4;
        pB += 4;

     while (--count);    

您需要做的就是为pApB 分配足够的内存,根据需要初始化它们,然后调用上面的函数。

我认为霓虹灯版本会快大约 3 倍。

【讨论】:

【参考方案2】:

如果您使用内在函数进行加载/存储,编译器似乎不会理会它们。

如果没有内在函数,它可以优化加载/存储并仅将寄存器用于中间值。看看生成的程序集:https://www.godbolt.org/z/FvqKP3

此外,我猜想在另一个传输正在写入同一内​​存时将某些内容加载到 NEON 中可能是一种罕见的用例,CPU 设计人员不会费心实施直接旁路。您可能必须等到存储完全完成才能开始加载。

【讨论】:

以上是关于在简单的加法任务中使用 ARM NEON 速度较慢的主要内容,如果未能解决你的问题,请参考以下文章

Neon 在 Intrinsics 中的校验和代码实现

如何使用 ARM Neon 内在函数对 IF 块进行矢量化?

ARM Cortex A53上的NEON SIMD dotproduct速度不快

如何在 ARM NEON 中将 uint8x8_t 加载到 float32x4 中?

在 iOS 上使用 NEON 乘积

这些opencv函数是由ARM NEON加速的吗? [关闭]