NVIDIA 初创加速计划,免费加速您的创业启动 了解详情
写点什么

TaiShan 服务器代码移植经验分享

王博

  • 2019-09-29
  • 本文字数:3712 字

    阅读完需:约 12 分钟

TaiShan服务器代码移植经验分享

经历过 2 个项目的业务代码从 X86 服务器迁移到 aarch64 泰山服务器上,以前没有相关经验摸索了好久,踩了很多坑,现在迁移工作也差不多收尾了,Taishan 服务器上跑比 X86 的溜多了。写了一篇代码迁移经验总结,欢迎大家参考。

编程语言简介

按照翻译方式的不同,高级语言通常可以分为两类:一类是编译翻译,一类是解释翻译,分别对应着编译型语言和解释型语言。

1.编译型语言

典型的如 C、C++语言,都属于编译型语言,源代码到执行的过程概括如图 1-1 所示。C/C++编译好的程序是机器指令,由操作系统加载到存储器(一般为内存)后由 CPU 直接执行。



图 编译型语言执行过程


基于编译型语言开发的应用程序,例如 C/C++语言应用程序,其编译后得到可执行程序,可执行程序执行时依赖的指令是 CPU 架构相关的。因此,基于 x86 架构编译的 C/C++语言应用程序,无法直接在 TaiShan 服务器运行,需要进行移植编译,移植编译过程中遇到的问题可以参考第 2、3 章提供的方法解决。

2.解释型语言

典型的如 Java、Python 语言,都属于解释型语言,源代码到执行的过程概括如图 1-2 所示。Java/Python 编译好的程序是平台无关的字节码,由虚拟机解释执行,虚拟机完成平台差异的屏蔽。



图 解释型语言执行过程


基于解释型语言开发的应用程序,是 CPU 架构不相关的,例如 Java、Python,将这类应用程序移植到 TaiShan 服务器,无需修改和重新编译,按照与 x86 一致的方式部署和运行应用程序即可。Java 应用程序 jar 包内,可能包含基于 C/C++语言开发的 so 库文件,这类 so 库需要移植编译,移植编译 so 库遇到的问题可以参考第 2、3 章提供的方法解决,使用编译得到的 so 库重新打包 jar 包。

准备工作

C/C++程序移植需要安装编译器,推荐使用 gcc7.3 及以上版本(最低不低于 4.8.5),下载安装参考链接:


gcc7.3版本下载地址


安装步骤参考

移植相关问题处理-编译脚本移植类问题

1.1 -m64 编译选项

现象描述


告警信息:gcc:error: unrecognized command line option ‘-m64’


可能原因


-m64 是 x86 64 位应用编译选项,m64 选项设置 int 为 32bits 及 long、指针为 64 bits,为 AMD 的 x86 64 架构生成代码。在 ARM64 平台无法支持。


处理步骤


将 ARM64 平台对应的编译选项设置为-mabi=lp64。

1.2 char 数据类型的符号

现象描述


告警信息:warning:comparison is always false due to limitedrange of data type


可能原因


char 变量在不同 CPU 架构下默认符号不一致,在 x86 架构下为 signed char,在 ARM64 平台为 unsigned char,移植时需要指定 char 变量为 signed char。


处理步骤


在编译选项中加入“-fsigned-char”选项,指定 ARM64 平台下的 char 为有符号数。

源码修改类问题

2.1 代码中汇编指令需要重写

现象描述


ARM 的汇编语言与 x86 完全不同,需要重写,涉及使用嵌入汇编的代码,都需要针对 ARM 进行配套修改。


处理步骤


需要重新实现汇编代码段。


示例:


在 x86 架构下:



在 ARM64 平台下,使用 gcc 内置函数实现:


2.2 替换 x86 CRC32 汇编指令

现象描述


编译错误:unknownmnemonic crc32q' --crc32q (x3),x2’或 operand 1 should be an integer register – `crc32b (x1),x0’


或 unrecognizedcommand line option ‘-msse4.2’。


可能原因


x86 使用的是 crc32b 和 crc32q 汇编指令完成 CRC32C 校验值计算功能,而 ARM64 平台使用 crc32cb、crc32ch、crc32cw、crc32cx 4 个汇编指令完成 CRC32C 校验值计算功能。


处理步骤


请使用 crc32cb、crc32ch、crc32cw、crc32cx 取代 x86 的 CRC32 系列汇编指令,替换方法如表所示,并在编译时添加编译参数-mcpu=generic+crc。



示例:


在 x86 下的实现:



在 ARM64 平台下的实现:


2.3 替换 x86 bswap 汇编指令

现象描述


编译报错:Error:unknown mnemonic bswap' --bswap x3’。


可能原因


bswap 是 x86 的字节序反序指令,需替换为 ARM64 的 rev 指令。


处理步骤


x86 指令实现的 bswap 如下:



替换为 ARM64 指令后如下:


2.4 替换 x86 rep 汇编指令

现象描述


编译报错:unknownmnemonic reprep


可能原因


rep 为 x86 的重复执行指令,需替换为 ARM64 的 rept 指令。


处理步骤


替换方法如下:


替换前:



替换后:


2.5 快速移植内联 SSE/SSE2 应用

现象描述


部分应用采用了 gcc 封装的用 SSE/SSE2 实现的函数,但是 gcc 目前没有提供对应的 ARM64 平台版本,需要实现对应函数。


处理步骤


目前已有开源代码实现了部分 ARM64 平台的函数,代码下载地址:https://github.com/open-estuary/sse2neon.git


使用方法如下:


步骤 1 将下载项目中的 SSE2NEON.h 文件拷贝到待移植项目中。


步骤 2 在源文件中删除如下代码。



步骤 3 在源代码中包含头文件 SSE2NEON.h


----结束

2.6 弱内存序导致程序执行结果和预期不一致

现象描述


弱内存序导致程序执行结果和预期不一致。


可能原因


ARM64 平台是弱内存序,原理如下:


  1. 同一份数据,在 cache 里面存在多份,需要 CPU 之间进行同步。



  1. 代码编写顺序和执行顺序可能不一样。



CPU 内部是流水线执行,在执行到 x=1 时,如果 x 在内存,那么 CPU 就会等待 x 导入到 cache,在等待的过程中如果 y 已经在 cache 中了,那么 CPU 会执行 y=1,这样就导致后面的语句先执行。


对系统的影响


  • 影响无锁编程的代码。

  • 对于使用信号量机制写的互斥代码,因为信号量函数已经带了内存屏障的指令,所以无影响。


处理步骤


找到使用无锁编程的代码,检查是否用内存屏障指令保证了数据的一致性。


使用内存屏障指令保证对共享数据的访问和预期一致。


示例:


2.7 对结构体中的变量进行原子操作时程序异常 coredump

现象描述


程序调用原子操作函数对结构体中的变量进行原子操作,程序 coredump,堆栈如下:



可能原因


ARM64 平台对变量的原子操作、锁操作等用到了 ldaxr、stlxr 等指令,这些指令要求变量地址必须按变量长度对齐,否则执行指令会触发异常,导致程序 coredump。


一般是因为代码中对结构体进行强制字节对齐,导致变量地址不在对齐位置上,对这些变量进行原子操作、锁操作等会触发问题。


处理步骤


代码中搜索“#pragmapack”关键字(该宏改变了编译器默认的对齐方式),找到使用了字节对齐的结构体,如果结构体中变量会被作为原子操作、自旋锁、互斥锁、信号量、读写锁的输入参数,则需要修改代码保证这些变量按变量长度对齐。

2.8 核数目硬编码

TaiShan 服务器相对于 x86 服务器,CPU 核数会有变化,如果模块代码针对处理器 core 数目硬编码,则会造成无法充分利用系统能力的情况,例如 CPU 核的利用率差异大或者绑核出现跨 numa 的情况。


处理步骤


您可以通过搜索代码中的绑核接口(sched_setaffinity)来排查绑核的实现是否存在 CPU 核数硬编码的情况。


如果存在,则根据 TaiShan 服务器实际核数进行修改,消除硬编码,可通过接口(sysconf(_SC_NPROCESSORS_CONF))来获取实际核数再进行绑核。

2.9 双精度浮点型转整型时数据溢出,与 X86 平台表现不一致

现象描述


C/C++双精度浮点型数转整型数据时,如果超出了整型的取值范围,TaiShan 平台的表现与 x86 平台的表现不同。



可能原因


在两个平台下,是两套 CPU 架构,其中的算数逻辑单元的实现可能会有差异,操作系统、编译器的实现都会有所不同。x86(指令集)中的浮点到整型的转换指令,定义了一个 indefinite integer value——“不确定数值”(64bit:0x8000000000000000),大多数情况下 x86 平台确实都在遵循这个原则,但是在从 double 向无符号整型转换时,又出现了不同的结果。鲲鹏的处理则非常清晰和简单,在上溢出或下溢出时,保留整型能表示的最大值或最小值,开发者并不会面对不确定或无法预期的结果。


处理步骤


参考如下数据转换的表格,调整代码中的实现:


double 数据向 long 转换:



double 数据向 unsigned long 转换:



double 数据向 int 转换:



double 数据向 unsigned int 转换:


编译优化项

4.1 gcc 编译器优化浮点运算精度

现象描述


编译优化选项设置-O2 级别及以上时,相同的浮点数乘加运算在 x86 平台和 ARM64 平台的运算结果,在小数点后 16 位存在差异。


可能原因


ARM64 平台编译优化选项设置为-O2 级别及以上,进行浮点数的乘加运算(a+=b*c),运算结果的精度只能精确到小数点后 16 位。在配置-O2 选项时,gcc 使用融合指令 fmadd 完成乘加运算,而不是 fadd 和 fmul。


fmadd 将浮点数的乘法和加法看成不可分的一个操作,不对中间结果进行舍入,从而导致计算结果有所差别。


对系统的影响


编译优化选项设置-O2 级别及以上时,浮点乘加运算的性能有提升,但是运算的精度受到影响。


处理步骤


添加编译选项-ffp-contract=off 可以关闭该优化。

4.2 增加编译选项匹配 Kunpeng 处理器架构,提升性能

在编译时增加编译选项指定处理器架构为 armv8,使编译器按照 Kunpeng 处理器的架构和微架构生成可执行程序,提升性能。


处理步骤


编译选项中添加-march=armv8-a。

4.3 增加编译选项匹配 Kunpeng 处理器流水线,提升性能

如果使用了 gcc 9.1 以上的版本,在编译时增加编译选项指定使用 tsv110 流水线,使编译器按照 Kunpeng 处理器的流水线编排指令执行顺序,充分利用流水线的指令集并行,提升性能。


处理步骤


编译选项中添加 -mtune=tsv110。


本文转载自公众号华为开发者社区(ID:Huawei_Developer)。


原文链接:


https://mp.weixin.qq.com/s/_-6H99jfp8D-MIm3GIcpWQ


2019-09-29 16:281452

评论

发布
暂无评论
发现更多内容

Docker入门简介

爱好编程进阶

Java 面试 后端开发

Elasticsearch聚合学习之三:范围限定

爱好编程进阶

Java 面试 后端开发

Java 结合实例学会使用 静态代理、JDK动态代理、CGLIB动态代理

爱好编程进阶

Java 面试 后端开发

Java IO

爱好编程进阶

Java 面试 后端开发

Java 反射 getClass()

爱好编程进阶

Java 面试 后端开发

Java并发编程(实战)

爱好编程进阶

Java 面试 后端开发

DevOps转型到底值不值?

华为云开发者联盟

DevOps 敏捷 敏捷开发 软件工程 DevSecOps

IDEA-2021首个大版本发布,Java开发者感动哭了(附新亮点演示

爱好编程进阶

Java 面试 后端开发

J2EE基础集合框架之Map集合

爱好编程进阶

Java 面试 后端开发

JAVA 百度地图 API

爱好编程进阶

Java 面试 后端开发

泡沫之下或许是中国的“第四消费时代”

基调听云

K8S太火了!花10分钟玩转它不香么?

爱好编程进阶

Java 面试 后端开发

java冒泡排序的实现以及优化

爱好编程进阶

Java 面试 后端开发

Java反射

爱好编程进阶

Java 面试 后端开发

如何设计一条稳定的应用交付流程?|云效工程师指北

阿里云云效

云计算 阿里云 云原生 持续交付 应用交付

混迹过超多开源社区的黄之鹏老师,为你解码AI开源的技术应用与未来趋势

OpenI启智社区

java并发锁ReentrantLock源码分析二之Condition实现原理

爱好编程进阶

Java 面试 后端开发

龙蜥大讲堂:如何利用硬件SIMD指令提升Java程序的性能?|第14期

OpenAnolis小助手

Java simd arm sig 龙蜥大讲堂

Demo:第三章:权限框架spring security oauth2

爱好编程进阶

Java 面试 后端开发

Java-进阶:多线程2

爱好编程进阶

Java 面试 后端开发

30个实用SQL语句,玩转PostgreSQL

慕枫技术笔记

数据库 后端 4月月更

Java应用日志如何与Jaeger的trace关联

爱好编程进阶

Java 面试 后端开发

CentOS8安装Docker

爱好编程进阶

Java 面试 后端开发

eclispe的快捷键大全

爱好编程进阶

Java 面试 后端开发

Java多线程-死锁的出现和解决

爱好编程进阶

Java 面试 后端开发

Java岗大厂面试百日冲刺 - 日积月累,每日三题【Day26

爱好编程进阶

Java 面试 后端开发

CRUD多年,终获腾讯offer,就靠这几套面试题

爱好编程进阶

Java 面试 后端开发

for(;;)和while(true)的区别

爱好编程进阶

Java 面试 后端开发

走向数据科学:在字节跳动广告投放这么干

字节跳动数据平台

大数据 字节跳动 ab测试

java8 Stream API及常用方法

爱好编程进阶

Java 面试 后端开发

进程内优雅管理多个服务

万俊峰Kevin

微服务 RPC web开发 go-zero Go 语言

TaiShan服务器代码移植经验分享_文化 & 方法_InfoQ精选文章