Arm 从NEON Intrinsic生成的程序集[LLVM-Xcode]

Arm 从NEON Intrinsic生成的程序集[LLVM-Xcode],arm,inline-assembly,intrinsics,Arm,Inline Assembly,Intrinsics,我正试图学习更多关于ARM组装的知识,并了解霓虹灯内部技术在幕后到底发生了什么。我正在使用最新的Xcode LLVM编译器。我发现,通常情况下,由intrinsic生成的程序集实际上比简单的朴素C代码还要慢 例如,此代码: void ArmTest::runTest() { const float vector[4] = {1,2,3,4}; float result[4]; float32x4_t vA = vld1q_f32(vector); asm("#

我正试图学习更多关于ARM组装的知识,并了解霓虹灯内部技术在幕后到底发生了什么。我正在使用最新的Xcode LLVM编译器。我发现,通常情况下,由intrinsic生成的程序集实际上比简单的朴素C代码还要慢

例如,此代码:

void ArmTest::runTest()
{

    const float vector[4] = {1,2,3,4};
    float result[4];
    float32x4_t vA = vld1q_f32(vector);

    asm("#Begin Test");

    vA = vmulq_f32(vA, vA);

    asm("#End Test");

    vst1q_f32(result, vA);
}
生成此输出:

#Begin Test

ldr q0, [sp, #16]
stp q0, q0, [fp, #-48]
ldur    q1, [fp, #-32]
fmul.4s v0, v1, v0
str q0, [sp, #16]

#End Test
我不明白的是,为什么所有的加载/存储都会命中内存?我肯定错过了一些明显的东西,对吧?另外,如何在内联汇编中编写此代码以使其达到最佳状态?我希望只有一条指令,但输出是完全不同的

请帮我理解


谢谢

你需要一个更好的测试。您的测试没有将计算结果用于任何事情,因此编译器只是做一些动作来让您满意。看起来您正在使用-O0进行编译,这将产生大量不必要的加载和存储,用于调试目的。如果使用-O3编译,所有代码都将被剥离。我重写了您的测试以保留结果,并使用-O3编译,结果如下:

$ cat neon.c 
#include <arm_neon.h>

void runTest(const float vector[], float result[])
{
    float32x4_t vA = vld1q_f32(vector);
     vA = vmulq_f32(vA, vA);
     vst1q_f32(result, vA);
}

$ xcrun -sdk iphoneos clang -arch arm64 -S neon.c -O3
$ cat neon.s 
    .section    __TEXT,__text,regular,pure_instructions
    .globl  _runTest
    .align  2
_runTest:                               ; @runTest
; BB#0:
    ldr q0, [x0]
    fmul.4s v0, v0, v0
    str q0, [x1]
    ret lr
$cat neon.c
#包括
无效运行测试(常量浮点向量[],浮点结果[])
{
float32x4_t vA=vld1q_f32(向量);
vA=vmulq_f32(vA,vA);
vst1q_f32(结果,vA);
}
$xcrun-sdk iphoneos clang-arch arm64-S neon.c-O3
$cat neon.s
.section\uuuu TEXT、\uuuu TEXT、常规、纯指令
.globl\u运行测试
.对齐2
_运行测试:@运行测试
; BB#0:
ldr q0[x0]
fmul.4s v0,v0,v0
strq0[x1]
ret lr

此代码看起来是最佳的

您永远不应该信任编译器。看看你的例子,尤其是64位代码远远不够好。停止浪费时间使用内部函数和检查生成的代码,而是学习汇编。在不知道实际指令的情况下,使用intrinsic不会走得很远。您还应该知道,使用ARM和NEON访问同一内存区域在“切换”时会浪费很多周期。因此,如果编写测试函数,应该让它们通过多次迭代来处理大量数据。在您的示例中,向量可能是由堆栈上的ARM初始化的,并由NEON读取和计算-非常慢的确是.void square(float*pDst,float*pSrc,unsigned int size);非常感谢。我知道我错过了一些简单的事情。你得到的结果正是我所期望的,你的猜测是对的,我不是用O3标志编译的