偷得浮生半桶水(半日闲), 好记性不如抄下来(烂笔头). 信息爆炸的时代, 学习是一项持续的工作.
全部博文(1747)
分类: 其他平台
2018-05-15 16:03:04
本文介绍了ARM代码编译时的软浮点(soft-float)和硬浮点(hard-float)的编译以及链接实现时的不同。从VFP浮点单元的引入到软浮点(soft-float)和硬浮点(hard-float)的概念,然后是在GCC和ARMCC RVCT工具链下的具体编译参数。
VFP (vector floating-point)
从ARMv5开始,就有可选的 Vector Floating Point (VFP)模块,当然最新的如 Cortex-A8, Cortex-A9 和 Cortex-A5 可以配置成不带VFP的模式供芯片厂商选择。VFP经过若干年的发展,有VFPv2 (一些 ARM9 / ARM11)、 VFPv3-D16(只使用16个浮点寄存器,默认为32个)和VFPv3+NEON (如大多数的Cortex-A8芯片)。对于包含NEON的ARM芯片,NEON一般和VFP公用寄存器。
硬浮点Hard-float
编译器将代码直接编译成发射给硬件浮点协处理器(浮点运算单元FPU)去执行。FPU通常有一套额外的寄存器来完成浮点参数传递和运算。使用实际的硬件浮点运算单元FPU当然会带来性能的提升。因为往往一个浮点的函数调用需要几个或者几十个时钟周期。
软浮点 Soft-float
编译器把浮点运算转换成浮点运算的函数调用和库函数调用,没有FPU的指令调用,也没有浮点寄存器的参数传递。浮点参数的传递也是通过ARM寄存器或者堆栈完成。 现在的Linux系统默认编译选择使用hard-float,即使系统没有任何浮点处理器单元,这就会产生非法指令和异常。因而一般的系统镜像都采用软浮点以兼容没有VFP的处理器。
armel和armhf
ABI
在armel中,关于浮点数计算的约定有三种。以gcc为例,对应的-mfloat-abi参数值有三个:soft,softfp,hard。soft是指所有浮点运算全部在软件层实现,效率当然不高,会存在不必要的浮点到整数、整数到浮点的转换,只适合于早期没有浮点计算单元的ARM处理器;softfp是目前armel的默认设置,它将浮点计算交给FPU处理,但函数参数的传递使用通用的整型寄存器而不是FPU寄存器;hard则使用FPU浮点寄存器将函数参数传递给FPU处理。需要注意的是,在兼容性上,soft与后两者是兼容的,但softfp和hard两种模式不兼容。默认情况下,armel使用softfp,因此将hard模式的armel单独作为一个abi,称之为armhf。而使用hard模式,在每次浮点相关函数调用时,平均能节省20个CPU周期。对ARM这样每个周期都很重要的体系结构来说,这样的提升无疑是巨大的。在完全不改变源码和配置的情况下,在一些应用程序上,使用armhf能得到20%——25%的性能提升。对一些严重依赖于浮点运算的程序,更是可以达到300%的性能提升。
Soft-float和hard-float的编译选项
在CodeSourcery gcc的编译参数上,使用-mfloat-abi=name来指定浮点运算处理方式。-mfpu=name来指定浮点协处理的类型。可选类型如fpa,fpe2,fpe3,maverick,vfp,vfpv3,vfpv3-fp16,vfpv3-d16,vfpv3-d16-fp16,vfpv3xd,vfpv3xd-fp16,neon,neon-fp16,vfpv4,vfpv4-d16,fpv4-sp-d16,neon-vfpv4等。使用-mfloat-abi=hard (等价于-mhard-float) -mfpu=vfp来选择编译成硬浮点。使用-mfloat-abi=softfp就能兼容带VFP的硬件以及soft-float的软件实现,运行时的连接器ld.so会在执行浮点运算时对于运算单元的选择,是直接的硬件调用还是库函数调用,是执行/lib还是/lib/vfp下的libm。-mfloat-abi=soft (等价于-msoft-float)直接调用软浮点实现库。
在ARM RVCT工具链下,定义fpu模式:
? --fpu softvfp
? --fpu softvfp+vfpv2
? --fpu softvfp+vfpv3
? --fpu softvfp+vfpv_fp16
? --fpu softvfp+vfpv_d16
? --fpu softvfp+vfpv_d16_fp16.
定义浮点运算类型
--fpmode ieee_full :所有单精度float和双精度double的精度都要和IEEE标准一致,具体的模式可以在运行时动态指定;
--fpmode ieee_fixed:舍入到最接近的实现的IEEE标准,不带不精确的异常;
--fpmode ieee_no_fenv:舍入到最接近的实现的IEEE标准,不带异常;
--fpmode std:非规格数flush到0、舍入到最接近的实现的IEEE标准,不带异常;
--fpmode fast:更积极的优化,可能会有一点精度损失。
一个浮点软链接实现的汇编例子
IMPORT __softfp_cos
BL __softfp_cos
ARMCC fplib浮点运算库
__aeabi_dadd 浮点double类型数据的加法,__aeabi_fdiv 单精度浮点除法。
附录:常见的芯片和VFP配置
Partial reference of SoC and supported ISAs
Manufacturer http://houh-1984.blog.163.com/ |
SoC |
architecture |
VFP |
SIMD |
Notes |
Freescale |
iMX5x |
armv7 |
|
|
; only reliable in Tape-Out 3 or above |
Nvidia |
Tegra2 |
armv7 |
|
none |
|
Marvell |
Dove |
armv7 |
|
iwMMXt |
|
OMAP3xxx |
armv7 |
|
|
|
|
OMAP4xxx |
armv7 |
|
|
|
|
|
OMAP5xxx |
armv7 |
VFPv4 |
|
(ARMv7-A) + (ARMv7-ME) |
Qualcomm |
Snapdragon |
armv7 |
|
[1] |
Qualcomm "Scorpion" core |
|
S5PC100 |
armv7 |
|
|
|
|
A1x |
armv7 |
|
|
|
Reference
http://houh-1984.blog.163.com/
本文介绍了ARM代码编译时的软浮点(soft-float)和硬浮点(hard-float)的编译以及链接实现时的不同。从VFP浮点单元的引入到软浮点(soft-float)和硬浮点(hard-float)的概念,然后是在GCC和ARMCC RVCT工具链下的具体编译参数。
-----------------------------------------------------------------------------------------------------------
NCNN在RK3288 Linux系统的原生编译问题解决
NCNN默认支持Android和iOS,使用Android NDK编译时会自动定义__ARM_NEON宏,从而获得较高的处理性能。在RK3288 Ubuntu Linux系统上编译时,默认是不开启__ARM_NEON宏的,虽然也能编译过,但是其性能却比Android系统低了一倍。
因此,要对NCNN做如下修改才能获得和Android一样的运行效果:
1、在顶层目录的CMakelist.txt文件中增加如下语句,强制打开宏__ARM_NEON:
add_definitions(-D__ARM_NEON)
add_definitions("-mfloat-abi=hard -mfpu=neon")
2、按如下方式修改src/CMakeLists.txt:
if(TURE OR (ANDROID AND ("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "armv7-a"))
即强制进入ARM平台分支编译,否则会编译X86平台对应的C程序。
3、在RK3288的Linux系统上按照软件包:
sudo apt-get install gcc-multilib g++-multilib
如果不安装此包,则会报告错误:/usr/include/c++/5/string:38:28: fatal error: bits/c++config.h: No such file or directory
特别说明:
RK3288 Ubuntu系统默认的浮点数格式hard,所以上面要用参数-mfloat-abi=hard
但是对于安卓或者不支持hard模式的处理器,应该定义参数-mfloat-abi=softfp(NCNN的README.md文档中提到的3519处理器就是这样)
如果这两个参数不一致,那么虽然编译能通过,但是程序运行时就会报出找不到文件的错误,比如:./mtcnn: error while loading shared libraries: libopencv_highgui.so.2.4: cannot open shared object file: No such file or directory。我解决这个错误花了较长时间,它产生的原因就是:mtcnn程序使用“-mfloat-abi=softfp -mfpu=neon”编译,而安装的libopencv-dev软件包却默认采用-mfloat-abi=hard编译。
===========================================================
出于低功耗、封装限制等种种原因,之前的一些ARM架构处理器因为内部资源宝贵,加入浮点运算单元是十分奢侈的,因为需要额外的软件实现。随着技术发展,目前高端的ARM处理器已经具备了硬件执行浮点操作的能力。这样新旧两种架构之间的差异,就产生了两个不同的嵌入式应用程序二进制接口(EABI)——软浮点与矢量浮点(VFP)。但是软浮点(soft float)和硬浮点(hard float)之间有向前兼容却没有向后兼容的能力,也就是软浮点的二进制接口(EABI)仍然可以用于当前的高端ARM处理器。
在ARM体系架构内核中,有些有浮点运算单元(fpu),有些没有。对于没有fpu内核,是不能使用armel和armhf的。在有fpu的情况下,就可以通过gcc的选项-mfloat-abi来指定使用哪种,有如下三种值:
kernel、rootfs和app编译的时候,指定的必须保持一致才行。
使用softfp模式,会存在不必要的浮点到整数、整数到浮点的转换。而使用hard模式,在每次浮点相关函数调用时,平均能节省20个CPU周期。对ARM这样每个周期都很重要的体系结构来说,这样的提升无疑是巨大的。
在完全不改变源码和配置的情况下,在一些应用程序上,虽然armhf比armel硬件要求(确切的是指fpu硬件)高一点,但是armhf能得到20-25%的性能提升。对一些严重依赖于浮点运算的程序,更是可以达到300%的性能提升。
之前EABI中,armel(低端ARM硬件,支持armv4以上版本),在执行浮点运算之前,浮点参数必须首先通过整数寄存器,然后传递到浮点运算单元。新的EABI ,也就是armhf,通过直接传递参数到浮点寄存器优化了浮点运算的调用约定。
相比我们熟悉的armel,armhf代表了另一种不兼容的二进制标准。在一些社区的支持下,armhf目前已经得到了很大的发展。像Ubuntu,已经计划在之后的发行版中放弃armel,转而支持armhf编译的版本。正如目前依然很火热的Raspberry Pi(ARM11),由于ubuntu只支持armv7架构的编译,Raspberry Pi将不能直接安装ubuntu系统。而BB Black(Cortex-A8)和Cubietruct(Cortex-A7)则同时支持ubuntu的armel与armhf的编译。
以上就是armel与armhf的比较。相信大家也应该有个大概的了解了。在Ubuntu系统下,如何根据需求分别实现两种交叉编译器的安装呢?
sudo apt-get install gcc-arm-linux-gnueabi
两种方法,
然后就是解压到自定义目录中,并添加bin环境变量。
armhf的开启需要硬件的支持,在Debian的wiki上要求ARMv7 CPU、Thumb-2指令集以及VFP3D16浮点处理器。
在gcc的编译参数上,使用-mfloat-abi=hard -mfpu=vfp即可。
在工具上,CodeSourcery最早支持hard模式。或者,也可已自己编译工具链。