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

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

NEON向量化

  基于ARM v7-A架构的ARM Cortex-A系列处理器(Cortex-A5, Cortex-A7, Cortex-A8, Cortex-A9, Cortex-A15)都可以选用NEON多媒体处理器加速程序运行,NEON是一种SIMD(Single Instruction Multiple Data)架构的协处理器,ARM的NEON处理器还可选配置成向量浮点VFPv3(Vector 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

选择NEON和VFP类型

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

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

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

  -mfloat-abi=hard使用VFP和NEON指令,并且改变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单元是否存在,如Linux下cat /proc/cpuinfo看是否包含NEON或者VFP, 
  如Tegra2 (双核 Cortex-A9 带 FPU),

# cat /proc/cpuinfo
Features : swp half thumb fastmult vfp edsp thumbee vfpv3vfpv3d16

  四核 Cortex-A9 带NEON单元

# cat /proc/cpuinfo
Features : swp half thumb fastmult vfp edsp thumbee neonvfpv3

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

Bare-metal模式下使能NEON

#include <stdio.h>
// Bare-minimum start-up code to run NEON code
__asm void EnableNEON(void)
{
MRC p15,,r0,c1,c0, // Read CPAccess register
ORR r0,r0,#0x00f00000 // Enablefull access to NEON/VFP by enabling access to
// Coprocessors 10 and 11
MCR p15,,r0,c1,c0, // Write CPAccess registerISB
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配置

 
          图1. NEON的Linux内核配置

  使能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= ; i < (len & ~) ; i++)
{
...
}
for (i=; i<(items*); i+=)
{
...
}

循环展开

#pragma unroll (n)

采用NEON Intrinsics

  armcc, GCC/g++和llvm等编译器都支持 NEON C/C++ intrinsics,并且采用相同的语法规范。因而代码可以在各个编译器间共享。NEON Intrinsics的代码容易维护而且效率高。NEON Intrinsics采用新的数据类型,这些类型对应于D和Q寄存器。NEONIntrinsics写起来像是函数调用但对应于每一条NEON指令。编程NEON Intrinsics时不用考虑具体的寄存器分配和代码的schedule,pipeline流水安排等。但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如何编译和优化,包含如何向量化、向量化的ARMCC和GCC编译器选项、NEON的汇编和EABI程序调用规范、如何在bare-metal和Linux操作系统上检测NEON硬件、如何指导编译器进行向量化NEON指令的优化等内容。

(二十三)ARM平台NEON指令的编译和优化的更多相关文章

  1. ARM平台指令虚拟化初探

    0x00:什么是代码虚拟化? 虚拟机保护是这几年比较流行的软件保护技术.这个词源于俄罗斯的著名软件保护软件“VmProtect”,以此为开端引起了软件保护壳领域的革命,各大软件保护壳都将虚拟机保护这一 ...

  2. linux下arm平台Qt编译环境搭建与解析

    一.概述:      我们知道QTcreator.这仅仅是个IDE,他包含了一个编译器--qmake.这两者的关系与codeblocks和g++的关系一样,首先要明确这些.      而我们在linu ...

  3. QtCreator动态编译jsoncpp完美支持x86和arm平台

    如果是做嵌入式开发. 在Qt下支持JSon最好的办法,可能不是采用qjson这个库.QJson这个库的实例只提供了x86环境下的编译方法. Installing QJson-------------- ...

  4. [转]ARM平台下独占访问指令LDREX和STREX

    参考:ARM平台下独占访问指令LDREX和STREX的原理与使用详解 全文转载如下: 为了实现线程间同步,一般都要在执行关键代码段之前加互斥(Mutex)锁,且在执行完关键代码段之后解锁.为了实现所谓 ...

  5. Tensorflowlite移植ARM平台iMX6

    一.LINUX环境下操作: 1.安装交叉编译SDK (仅针对该型号:i.MX6,不同芯片需要对应的交叉编译SDK) 编译方法参考:手动编译用于i.MX6系列的交叉编译SDK 2.下载Tensorflo ...

  6. 【安卓安全】ARM平台代码保护之虚拟化

    简介:代码的虚拟化即不直接通过CPU而是通过虚拟机来执行虚拟指令.代码虚拟化能有效防止逆向分析,可大大地增加了代码分析的难度和所需要的时间,若配合混淆等手段,对于动静态分析有着较强的防御能力. 背景: ...

  7. 移植mysql到嵌入式ARM平台

    移植MySQL到嵌入式ARM平台  MySQL没有专门针对ARM的版本,移植到ARM没有官方文档可参考,因此,暂时参考这样一篇文档: http://blog.chinaunix.net/space.p ...

  8. iOS安全攻防(二十三):Objective-C代码混淆

    iOS安全攻防(二十三):Objective-C代码混淆 class-dump能够非常方便的导出程序头文件,不仅让攻击者了解了程序结构方便逆向,还让着急赶进度时写出的欠完好的程序给同行留下笑柄. 所以 ...

  9. 4G通信模块在ARM平台下的应用

    4G模块是连接物与物的重要载体,是终端设备接入物联网的核心部件之一.随着4G的普及,许多新兴市场对4G通信模块的需求都在日益扩大,那么在ARM平台的嵌入式设备上如何快速的应用4G模块呢? 4G通信模块 ...

随机推荐

  1. HTML-T

    a标签跳转 <a href="new_link" target="_blank">在新建页面打开链接</a>. <a href=& ...

  2. MVC框架实例教程 【转载】

    1 什么是MVC MVC模式(Model-View-Controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model).视图(View)和控制器(Controller ...

  3. idea配置git的步骤

    第一次git到GitHub过程 打开到项目 这样就不用再用git Bash敲命令了

  4. CentOS7编译安装libc++和libc++abi

    本文介绍了如何在CentOS 7中构建C++11构建环境 Clang的定制C++库是libc++(libcxx).然后,libcxx还需要一个ABI库,libc++abi(libcxxabi).不幸的 ...

  5. VS2013:error C1069: 无法读取编译器命令行

    前一阵搞python和matlab,没用VS 2013,今天打开一个C++程序想跑一跑,突然蹦出这么个错误,然后发现电脑上所有的程序都会这样了. 后来发现是TMP/TEMP环境变量路径有空格的问题,更 ...

  6. mybatis+mysql insert添加数据后返回数据主键id

    1.根据useGeneratedKeys获取返回值,部分数据库不支持 修改mybatis xml <insert id="insertUser" useGeneratedKe ...

  7. js 中json遍历 添加 修改 类型转换

    <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&quo ...

  8. Oracle对象-视图和索引

    Oracle 对象-视图 视图概念 ​ 视图就是提供一个查询的窗口,所有的数据来自于原表 创建视图[必须有dba权限] --查询语句创建表 create table emp as select * f ...

  9. Hadoop学习(4)-mapreduce的一些注意事项

    关于mapreduce的一些注意细节 如果把mapreduce程序打包放到了liux下去运行, 命令java  –cp  xxx.jar 主类名 如果报错了,说明是缺少相关的依赖jar包 用命令had ...

  10. 运算符优先级 以及 && (逻辑与) 和||(逻辑或)的优先级:

    运算符优先级(从高到低列出) 运算符 描述 . [] () 字段访问.数组下标.函数调用以及表达式分组 ++ -- - ~ ! delete new typeof void 一元运算符.返回数据类型. ...