ARM平台NEON指令的编译和优化



ARM平台NEON指令的编译和优化  

本文介绍了ARM平台基于ARM v7-A架构的ARM Cortex-A系列处理器(Cortex-A5, Cortex-A7,Cortex-A8, Cortex-A9, Cortex-A15)上的NEON多媒体处理硬件加速器针对C/C++语言、汇编语言和NEON intrinsics如何编译和优化,包含如何向量化、向量化的ARMCCGCC编译器选项、NEON的汇编和EABI程序调用规范、如何在bare-metalLinux操作系统上检测NEON硬件、如何指导编译器进行向量化NEON指令的优化等内容。

NEON向量化

基于ARM v7-A架构的ARM Cortex-A系列处理器(Cortex-A5, Cortex-A7, Cortex-A8, Cortex-A9, Cortex-A15)都可以选用NEON多媒体处理器加速程序运行,NEON是一种SIMDSingle Instruction Multiple Data)架构的协处理器,ARMNEON处理器还可选配置成向量浮点VFPv3Vector Floating-Point)指令集处理器。

常用的编译器选项配置

自动向量化选项

armcc编译器使用--vectorize选项来使能向量化编译,一般选择更高的优化等级如-O2或者-O3就能使能--vectorize选项。

gcc编译器的向量化选项-ftree-vectorize来使能向量化选项,使用-O3会自动使能-ftree-vectorize选项。

选择处理器类型

armcc编译器使--cpu 7-A或者--cpu Cortex-A8来指定指令集架构和CPU类型。

gcc编译器的处理器选项-mfpu=neon-mcpu来指定cpu类型。如-mcpu=cortex-a5

选择NEONVFP类型

gcc选择用-mfpu=vfpv3-fp16来指定为vfp协处理,而-mfpu=neon-vfpv4等就能指定为NEON+VFP结构。

选择浮点处理器和ABI接口类型

-mfloat-abi=soft使用软件浮点库,不是用VFP或者NEON指令;-mfloat-abi=softfp使用软件浮点的调用规则,而可以使用VFPNEON指令,编译的目标代码和软件浮点库链接使用;

-mfloat-abi=hard使用VFPNEON指令,并且改变ABI调用规则来产生更有效率的代码,如用vfp寄存器来进行浮点数据的参数传递,从而减少NEON寄存器和ARM寄存器的拷贝。

常用的CPU类型编译器选项

CPU类型

CPU类型选项

FP选项

FP + SIMD选项

备注

Cortex-A5

-mcpu=cortex-a5

-mfpu=vfpv3-fp16

-mfpu=vfpv3-d16-fp16

-mfpu=neon-fp16

-d16表明只有前16个浮点寄存器可用

Cortex-A7

-mcpu=cortex-a7

-mfpu=vfpv4

-mfpu=vfpv4-d16

-mfpu=neon-vfpv4

-fp16表明支持16bit半精度浮点操作

Cortex-A8

-mcpu=cortex-a8

-mfpu=vfpv3

-mfpu=neon

 

Cortex-A9

-mcpu=cortex-a9

-mfpu=vfpv3-fp16

-mfpu=vfpv3-d16-fp16

-mfpu=neon-fp16

 

Cortex-A15

-mcpu=cortex-a15

-mfpu=vfpv4

-mfpu=neon-vfpv4

 

常用的gcc组合编译器选项

Cortex-A15 with a NEON unit

arm-gcc -O3 -mcpu=cortex-a15 -mfpu=neon-vfpv4-mfloat-abi=hard -ffast-math -omyprog.exe myprog.c

Cortex-A9 with a NEON unit

arm-gcc -O3 -mcpu=cortex-a9 -mfpu=neon-vfpv3-fp16-mfloat-abi=hard -ffast-math -omyprog.exe myprog.c

Cortex-A7 without a NEON unit

arm-gcc -O3 -mcpu=cortex-a7 -mfpu=vfpv4-d16-mfloat-abi=softfp -ffast-math -omyprog2.exe myprog2.c

Cortex-A8 without a NEON unit

arm-gcc -O3 -mcpu=cortex-a8 -mfloat-abi=soft -c -o myfile.omyfile.c

NEON汇编和EABI程序调用规范

GNU assembler (gas) and ARM Compiler toolchain assembler(armasm)都支持NEON指令的汇编。但必须遵循ARMEmbedded Application Binary Interface (EABI)EABI的规范,即NEON寄存器的S0-S15 (D0-D7, Q0-Q3)用于传递参数和返回值,被调用函数内可以直接使用,不用保存;D16-D31 (Q8-Q15)则有调用函数来保存,被调用函数内可以不保存的随意使用;而S16-S31(D8-D15, Q4-Q7)则必须由被调用函数内部保存。对于调用传参规范则有,对于软件浮点,参数有R0~R3和堆栈stack传递,而硬件浮点,可以通过NEON寄存器来传递参数。

NEON硬件检测和使能

编译时指定NEON单元是否存在

ARM编译器(armcc)4.0之后就支持在某些处理器和FPU的选项中预定义宏__ARM_NEON__ armasm的宏TARGET_FEATURE_NEON.

运行时指定检测NEON单元

OS内可以检测NEON单元是否存在,如Linuxcat /proc/cpuinfo看是否包含NEON或者VFP,如Tegra2 (双核 Cortex-A9 FPU), cat /proc/cpuinfo:

Features : swp half thumb fastmult vfp edsp thumbee vfpv3vfpv3d16

四核 Cortex-A9 NEON单元

Features : swp half thumb fastmult vfp edsp thumbee neonvfpv3

另外可以查看/proc/self/auxv,这里会包含二进制格式的hwcap,可以通过AT_HWCAP来搜索到。HWCAP_NEON bit (4096).另外如Ubuntu的发布在路径/lib/neon/vfp下包含libNEON优化版本。

Bare-metal模式下使能NEON

#include <stdio.h>

// Bare-minimum start-up code to run NEON code

__asm void EnableNEON(void)

{

MRC p15,0,r0,c1,c0,2 // Read CPAccess register

ORR r0,r0,#0x00f00000 // Enablefull access to NEON/VFP by enabling access to

// Coprocessors 10 and 11

MCR p15,0,r0,c1,c0,2 // Write CPAccess register

ISB

MOV r0,#0x40000000 // Switch onthe VFP and NEON hardware

MSR FPEXC,r0 // Set EN bit inFPEXC

}

下面的EnableNEON函数使能NEON协处理器;使用下面的编译选择就能在bare-metal下使能NEON

armcc -c --cpu=Cortex-A8 --debug hello.c -o hello.o

armlink --entry=EnableNEON hello.o -o hello.axf

系统运行时使能NEON

内核在遇到第一个NEON指令时会产生一个UndefinedInstruction的异常,这会让内核自动重启NEON协处理器,内核还可以在上下文切换时关闭NEON来省电。

Linux内核的NEON配置

http://img1.ph.126.net/u_Hf9cV_xPfBgWUxlJUBjw==/3381921845278864702.jpg

1. NEONLinux内核配置

使能NEON,需要选择

Floating point emulation VFP-format floating point maths

Floating pointemulation Advanced SIMD (NEON) Extension

检查Linux的配置文件来确认内核是否使能NEON

zcat /proc/config.gz | grep NEON

看是否存在

CONFIG_NEON=y

确认处理器是否支持NEON

cat /proc/cpuinfo | grep neon

看是否有如下内容

Features : swp half thumb fastmult vfp edsp neon vfpv3 tlsvfpv4 idiva idivt

向量化NEON优化指南

避免指针混叠alias

C90不要求指针位置,不同指针可以指向相同的内存区域,C99中引入了__restrict关键字来表明只有这个指针能指向它工作的区域。

告诉编译器循环信息

如循环是否某个整数的整数倍,以方便向量化;如下表明循环次数是4的整数倍:

for(i=0 ; i < (len & ~3) ; i++)

{

。。。

}

for (i=0; i<(items*4); i+=1)

{

。。。

}

循环展开

#pragma unroll (n)

采用NEON Intrinsics

armcc, GCC/g++llvm等编译器都支持 NEON C/C++ intrinsics,并且采用相同的语法规范。因而代码可以在各个编译器间共享。NEON Intrinsics的代码容易维护而且效率高。NEON Intrinsics采用新的数据类型,这些类型对应于DQ寄存器。NEONIntrinsics写起来像是函数调用但对应于每一条NEON指令。编程NEON Intrinsics时不用考虑具体的寄存器分配和代码的schedulepipeline流水安排等。但NEON Intrinsics往往不能产生想象的代码,性能上相比纯汇编要稍差一些。

减少循环内的相关性

如果当前迭代时使用的数据是上次迭代计算的结果,就产生了迭代间的相关性,可以拆分循环来减少相关。

向量化其他准则

  • 短小的循环更容易让编译器实现自动向量化;
  • 避免在循环内使用break退出循环
  • 避免在循环内使用过多的条件语句,减少可能产生的条件跳转;
  • 让循环次数尽可能是2的幂次
  • 让编译器知晓循环次数,减少对循环次数为0等的判断;
  • 循环内调用的函数尽量inline内联
  • 使用数组+索引的方式访问比指针形式更容易向量化;
  • 间接寻址(多重索引)不会向量化;
  • 使用restrict关键字来告诉编译器没有重叠的内存区域;

总结

本文介绍了ARM平台基于ARM v7-A架构的ARM Cortex-A系列处理器(Cortex-A5, Cortex-A7,Cortex-A8, Cortex-A9, Cortex-A15)上的NEON多媒体处理硬件加速器针对C/C++语言、汇编语言和NEON intrinsics如何编译和优化,包含如何向量化、向量化的ARMCCGCC编译器选项、NEON的汇编和EABI程序调用规范、如何在bare-metalLinux操作系统上检测NEON硬件、如何指导编译器进行向量化NEON指令的优化等内容。

参考

http://houh-1984.blog.163.com/

本文介绍了ARM平台基于ARM v7-A架构的ARM Cortex-A系列处理器(Cortex-A5, Cortex-A7,Cortex-A8, Cortex-A9, Cortex-A15)上的NEON多媒体处理硬件加速器针对C/C++语言、汇编语言和NEON intrinsics如何编译和优化,包含如何向量化、向量化的ARMCCGCC编译器选项、NEON的汇编和EABI程序调用规范、如何在bare-metalLinux操作系统上检测NEON硬件、如何指导编译器进行向量化NEON指令的优化等内容。

 

你可能感兴趣的:(ARM平台NEON指令的编译和优化)