我有两个浮标选项卡。我需要将第一个选项卡中的元素与第二个选项卡中的相应元素相乘,并将结果存储在第三个选项卡中。
我想使用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;
}
我运行以下命令进行编译: g ++ -mfpu = neon -ffast-math neon_test.cpp
我的CPU:ARMv7处理器rev 0(v7l)
您对我如何实现更显着的加速有任何想法吗?
答案 0 :(得分:5)
Cortex-A8和Cortex-A9每个周期只能进行两次SP FP乘法运算,因此最多可以将这些(最常用)CPU的性能提高一倍。实际上,ARM CPU的IPC非常低,因此最好尽可能地展开循环。如果你想要最终性能,请写入汇编:gcc的ARM代码生成器无法与x86一样好。
我还建议使用特定于CPU的优化选项:Cortex-A9的“-O3 -mcpu = cortex-a9 -march = armv7-a -mtune = cortex-a9 -mfpu = neon -mthumb”;对于Cortex-A15,Cortex-A8和Cortex-A5相应地替换-mcpu = -mtune = cortex-a15 / a8 / a5。 gcc没有针对Qualcomm CPU的优化,因此对于Qualcomm Scorpion使用Cortex-A8参数(并且还会比平时更多地展开),而对于Qualcomm Krait尝试Cortex-A15参数(您将需要最新版本的gcc支持它)。
答案 1 :(得分:2)
霓虹内在函数的一个缺点是,你不能在负载上使用自动增量,这显示为你的霓虹灯实现的额外指令。
使用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>
这是mul_tab_standard
的循环部分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>
正如您在标准情况下所看到的,编译器会创建更紧密的循环。