本文内容基于《NEON Prgrammer’s Guide》(下称NPG)第二章Compiling NEON Instruction,所有编译命令都基于gcc-arm
让你的程序使用arm内核的NEON unit进行并行计算,提升嵌入式程序性能,一般有3种方法:
第1项是NPG的正题,以后再说,这一章还是说一些简单的,比如第2项和第3项。
NPG中推荐了几个开源的利用NEON进行优化的函数库(不过我看的是2013年的NPG,是不是后来又出了其他库就不知道了),有需要百度库名即可:
NGP书中,一般用vectorization来表示使用NEON指令来进行并行计算加速,所以让编译器来帮我们进行vectorization就叫做auto-vectorization。一般有3个步骤:
linux环境下命令行输入cat /proc/cpuinfo | grep neon
,如果有输出Features : swp half thumb fastmult vfp edsp thumbee neon vfpv3
类似的输出,恭喜你,你的cpu有neon unit。如果没有,那么我也没啥办法,查查cpu的datasheet吧。
不过要注意的是,NEON unit在开机了之后是默认disable的(上述指令表示你的cpu有NEON unit这个硬件)。
但是Linux系统会在执行程序的时候,第一次遇到NEON指令时,会使能NEON unit,直至遇到上下文切换。所以使用Linux的同学应该不用担心。如果自己编译内核的同学,则需要注意一下,在编译过程中需要在Floating point emulation选项下打开VFP-fomat floating point maths和Advanced SIMD(NEON) Extension support。
对于使用其他OS或者裸机跑程序(ARM裸机,这个有点虎)的同学,则需要手动使能NEON unit,
// Bare-minimum start-up code to run NEON code
__asm void EnableNEON(void)
{
MRC p15,0,r0,c1,c0,2 // Read CP Access register
ORR r0,r0,#0x00f00000 // Enable full access to NEON/VFP by enabling access to
// Coprocessors 10 and 11
MCR p15,0,r0,c1,c0,2 // Write CP Access register
ISB
MOV r0,#0x40000000 // Switch on the VFP and NEON hardware
MSR FPEXC,r0 // Set EN bit in FPEXC
}
NPG中提供了一些原则,可以帮助我们的code更好的被编译器实现vetorization,
int a[10]={0}, b[10]={0}, c[10]={0};
// ×
for (int i=0; i<10; i++)
{
a[i] += 1;
b[i] += 2;
c[i] += 3;
}
// √
for (int i=0; i<10; i++)
a[i] += 1;
for (int i=0; i<10; i++)
b[i] += 2;
for (int i=0; i<10; i++)
c[i] += 3;
// 其实上面这样写的目的是为了在一个循环里面可以连续寻址,加载进NEON寄存器,
// 所以下面这种写法就不如合起来写
struct rgb {char r; char g; char b};
struct rgb out[10];
for (...)
out[i].r = ...;
for (...)
out[i].g = ...;
for (...)
out[i].b = ...;
// 编译器对循环次数一无所知
void func(int n)
{
for (int i=0; i<n; i++)
{
...
}
}
// 编译器知道循环次数是4的倍数
void func(int n)
{
for (int i=0; i<n & (~3); i++)
// or for (int i=0; i
{
...
}
}
// 有的同学会为了对齐到32-bit在下面结构体的最后加上一个not_used的字段,
// 但是在循环中没有用到not_used字段,会打断第1点中说的NEON连续寻址,所以编译器也不会做auto-vectorization
struct aligned_pixel
{
char r;
char g;
char b;
char not_used; /* Padding used to keep r aligned to a 32-bit word */
}screen[10];
// 这个就更不用说了, 数据类型都不一样,编译器会说我好难。
// 不缺内存的情况下,可以将结构体中的数据类型都对齐到最宽的数据类型
struct pixel
{
char r;
short g; /* Green channel contains more information */
char b;
}screen[10];
NPG中在好多地方分散说了很多进行vectorization的代码注意要点,我这边总结了我认为比较重要的部分,但难免有些细节遗漏,如果有兴趣,可以深入看看NPG第2章。其实总结起来,无非是如果你的code你自己要做vectorization都觉得困难,那就别指望编译器了。
编译使用选项——gcc/g++ xxx.c -o target -ftree-vectorize -mfpu=neon -mcpu=cortex-aN
其中,
-ftree-vectorize
表示让编译器进行vectorization。在我的实际使用中,感觉没啥效果,要直接-O3
才能vectorization-mfpu
指定FPU。对于我使用的cortex-a7,NPG推荐使用-mfpu=neon-vfpv4
(不过只用neon
好像也能用…)处理器 | -mfpu |
---|---|
cortex-a5 | neon-fp16 |
cortex-a7 | neon-vfpv4 |
cortex-a8 | neon |
cortex-a9 | neon-fp16 |
cortex-a15 | neon-vfpv4 |
-mcp
指定cpu型号。