C++ 霓虹灯浮点乘法比预期慢

C++ 霓虹灯浮点乘法比预期慢,c++,gcc,arm,simd,neon,C++,Gcc,Arm,Simd,Neon,我有两个浮动标签。我需要将第一个选项卡中的元素乘以第二个选项卡中相应的元素,并将结果存储在第三个选项卡中 我想使用NEON来并行化浮点乘法:同时进行四个浮点乘法,而不是一个浮点乘法 我期望显著的加速,但我只实现了大约20%的执行时间减少。这是我的代码: #include <stdlib.h> #include <iostream> #include <arm_neon.h> const int n = 100; // table size /* fill

我有两个浮动标签。我需要将第一个选项卡中的元素乘以第二个选项卡中相应的元素,并将结果存储在第三个选项卡中

我想使用NEON来并行化浮点乘法:同时进行四个浮点乘法,而不是一个浮点乘法

我期望显著的加速,但我只实现了大约20%的执行时间减少。这是我的代码:

#include <stdlib.h>
#include <iostream>
#include <arm_neon.h>

const int n = 100; // table size

/* fill a tab with random floats */
void rand_tab(float *t) {
    for (int i = 0; i < n; i++)
        t[i] = (float)rand()/(float)RAND_MAX;
}

/* Multiply elements of two tabs and store results in third tab
 - STANDARD processing. */
void mul_tab_standard(float *t1, float *t2, float *tr) {
    for (int i = 0; i < n; i++)
         tr[i] = t1[i] * t2[i]; 
}

/* Multiply elements of two tabs and store results in third tab 
- NEON processing. */
void mul_tab_neon(float *t1, float *t2, float *tr) {
    for (int i = 0; i < n; i+=4)
        vst1q_f32(tr+i, vmulq_f32(vld1q_f32(t1+i), vld1q_f32(t2+i)));
}

int main() {
    float t1[n], t2[n], tr[n];

    /* fill tables with random values */
    srand(1); rand_tab(t1); rand_tab(t2);


    // I repeat table multiplication function 1000000 times for measuring purposes:
    for (int k=0; k < 1000000; k++)
        mul_tab_standard(t1, t2, tr);  // switch to next line for comparison:
    //mul_tab_neon(t1, t2, tr);  
    return 1;
}
#包括
#包括
#包括
常数int n=100;//表大小
/*用随机浮动填充选项卡*/
无效随机选项卡(浮动*t){
对于(int i=0;i
我运行以下命令进行编译: g++-mfpu=neon-ffast math neon_test.cpp

我的CPU:ARMv7处理器版本0(v7l)


您知道如何实现更显著的加速吗?

Cortex-A8和Cortex-A9每个周期只能执行两次SP FP乘法,因此您最多可以将这些(最流行的)CPU的性能提高一倍。实际上,ARM CPU的IPC非常低,因此最好尽可能地展开循环。如果您想要最终的性能,请编写汇编:gcc针对ARM的代码生成器远不如x86


我还建议使用CPU特定的优化选项:“-O3-mcpu=cortex-a9-march=armv7-a-mtune=cortex-a9-mfpu=neon-mthumb”用于cortex-a9;对于Cortex-A15,Cortex-A8和Cortex-A5相应地替换-mcpu=-mtune=Cortex-A15/A8/A5。gcc没有针对高通CPU的优化,因此对于高通Scorpion,请使用Cortex-A8参数(并且比您通常使用的参数还要多),对于高通Krait,请尝试使用Cortex-A15参数(您需要最新版本的gcc支持它)。

neon intrinsics的一个缺点是,无法在加载时使用自动增量,它显示为neon实现的额外说明

使用gcc版本4.4.3和选项-c-std=c99-mfpu=neon-O3编译并使用objdump转储,这是mul_tab_neon的循环部分

000000a4 <mul_tab_neon>:
  ac:   e0805003    add r5, r0, r3
  b0:   e0814003    add r4, r1, r3
  b4:   e082c003    add ip, r2, r3
  b8:   e2833010    add r3, r3, #16
  bc:   f4650a8f    vld1.32 {d16-d17}, [r5]
  c0:   f4642a8f    vld1.32 {d18-d19}, [r4]
  c4:   e3530e19    cmp r3, #400    ; 0x190
  c8:   f3400df2    vmul.f32    q8, q8, q9
  cc:   f44c0a8f    vst1.32 {d16-d17}, [ip]
  d0:   1afffff5    bne ac <mul_tab_neon+0x8>
000000a4:
ac:e0805003添加r5、r0、r3
b0:e0814003添加r4、r1、r3
b4:e082c003添加ip、r2和r3
b8:e2833010添加r3,r3,#16
bc:f4650a8f vld1.32{d16-d17}[r5]
c0:f4642a8f vld1.32{d18-d19}[r4]
c4:e3530e19 cmp r3,#400;0x190
c8:f3400df2 vmul.f32 q8、q8、q9
cc:f44c0a8f vst1.32{d16-d17}[ip]
d0:1afffff5 bne ac
这是mul_tab_标准的循环部分

00000000 <mul_tab_standard>:
  58:   ecf01b02    vldmia  r0!, {d17}
  5c:   ecf10b02    vldmia  r1!, {d16}
  60:   f3410db0    vmul.f32    d16, d17, d16
  64:   ece20b02    vstmia  r2!, {d16}
  68:   e1520003    cmp r2, r3
  6c:   1afffff9    bne 58 <mul_tab_standard+0x58>
00000000:
58:ecf01b02 vldmia r0!,{d17}
5c:ecf10b02 vldmia r1!,{d16}
60:f3410db0 vmul.f32 d16、d17、d16
64:ece20b02 vstmia r2!,{d16}
68:e1520003凸轮轴位置r2,r3
6c:1AFFF9 bne 58

正如您在标准情况下所看到的,编译器创建了更紧密的循环。

在谷歌搜索了您的函数
vst1q_f32
vmulq_f32
,我找不到关于它们的更多信息。你能提供一个文档链接吗?这里列出了这些功能:我没有找到它们的详细描述。它们产生正确的算术结果。您需要将
-O3
添加到
g++
命令行。顺便说一句,我不推荐快速数学。所以:
g++-Wall-O3-mfpu=neon-neon_test.cpp
“Cortex-A8和Cortex-A9每个周期只能进行两次SP-FP乘法”——我不知道这一点。这是我不满意成绩的主要原因。谢谢我已经尝试过优化选项,但并没有帮助。