Power平台迁移指南
Posted beyondma
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Power平台迁移指南相关的知识,希望对你有一定的参考价值。
在使用Power平台过程中,可能会遇到现有X86平台的应用程序无法在Power平台中直接运行的问题,这是由Power平台与X86平台的架构差异造成的,因此需要通过软件迁移对相关应用进行基于Power平台的适配。迁移工作首先是指令集的适配,该部分工作通过使用合适的编译器及部分代码修改即可完成,同时针对Power处理器Cacheline大、支持的页框大等特性,也需要对应用代码进行针对性的调整,以充分发挥Power处理器的硬件优势,最终实现应用程序的稳定高效运行。
最近我在浪潮商用机器的官方微信上,看到了一篇文章,只需三步:①Set up好环境,②指令集编译,③代码优化,即可轻松地把X86上的应用迁移到Power上来,进而享受Power集成的一体化应用管理环境、卓越的性能、优异的拓展性、卓越的安全性以及简化的系统管理等优势。当然原文有一定的局限性,我做了一些增补和修改,以下是我亲历的整个过程。
1软件迁移的必要性
1.1程序执行过程
所有的软件均由编程语言实现,编程语言在计算机上最终会被翻译成机器语言,以指令的形式在处理器上执行。
1.2Power处理器和X86处理器的指令差异
处理器能够识别的所有指令的集合就是指令集,而不同架构处理器所使用的指令集是不同的。Power架构处理器使用的是基于RISC(Reduced Instruction Set Computing)的ISA指令集,而X86架构使用的是CISC(Complex Instruction Set Computing)指令集。
因此,X86架构下软件在Power平台上运行必须经过相应的迁移。
1.3迁移过程中的常见问题类型
- Makefile文件及configure文件兼容性问题
Makefile文件中不同架构使用的编译选项可能会有所不用,如果Makefile文件使用不当会导致编译问题,通常编译时会使用Configure来生成Makefile文件,因此需要在Configure文件中加入相应的架构选项,在Power平台编译时需要加入PPC64le架构选项。
例如Python2.7的configure.guess,需要在configure.guess中增加对Power架构的支持:
ppc64le:Linux:*:*)
GUESS=powerpc64le-unknown-linux-$LIBC
;;
ppcle:Linux:*:*)
GUESS=powerpcle-unknown-linux-$LIBC
;;
vax:Linux:*:*)
GUESS=$UNAME_MACHINE-dec-linux-$LIBC
;;
x86_64:Linux:*:*)
set_cc_for_build
GUESS=$UNAME_MACHINE-pc-linux-$LIBCABI
- 汇编指令兼容性问题
不同平台使用的汇编指令是完全不同的,跨平台执行汇编指令会触发指令异常,因此在Power平台编译软件时需要将软件代码中的X86平台的汇编指令执行进行相应的替换。
如CPU序列号获取功能,需要对X86的汇编指令进行如下替换:
X86:
asm volatile
(
"movl $0x01, %%eax; \\n\\t"
"xorl %%edx, %%edx; \\n\\t"
"cpuid; \\n\\t"
"movl %%edx, %0; \\n\\t"
"movl %%eax, %1; \\n\\t"
: "=m"(s1), "=m"(s2)
);
PPC64le:
从"/proc/device-tree/vpd/"系统文件读取
2.unsign char问题
X86 平台中,GCC编译器默认char为有符号类型,比如char a = -1 GCC编译器则默认为signed char a = -1,其二进制原码为10000001,它的补码是除了符号外取反加 1,补码为 11111111,在计算机中数值是以补码形式存储,同样为 -1在计算机中存储的二进制 11111111,由于Power平台中GCC编译器对char 默认是无符号类型且计算机中无符号数的原码、补码的值相同,因此其表示的数值为 255。所以代码编译时需要加入-fsigned-char 编译选项以避免这个问题,如下所示:
gcc -fsigned-char -o transfer *.c
3.类型转换溢出的问题
双精度浮点型转整型或者浮点型时,不同架构处理器的算数逻辑单元的实现会有差异,编译器的实现也有所不同,因此Power平台与X86平台相比上述转换操作会有不同的结果。在X86的转换指令中定义了一个 indefinite integer value(0x8000000000000000)表示出现溢出问题。 而Power平台的转换策略则是无论是正数溢出还是负数溢出,转换结果为转换目标类型(整型/浮点)的最大或最小值,因此在移植过程中需要对上述类型转换进行重点关注,避免不同转换结果引起的问题。具体差异参见下表
处理器 | Double值 | 转换为long结果 | 描述 |
X86 | 正值超出long范围 | 0x8000000000000000 | indefinite integer value |
负值超出long范围 | 0x8000000000000000 | indefinite integer value | |
Power | 正值超出long范围 | 0x7FFFFFFFFFFFFFFF | long最大值 |
负值超出long范围 | 0x8000000000000000 | long最小值 | |
处理器 | Double值 | 转换为int结果 | 描述 |
X86 | 正值超出int范围 | 0x80000000 | indefinite integer value |
负值超出int范围 | 0x80000000 | indefinite integer value | |
Power | 正值超出int范围 | 0x7FFFFFFF | long最大值 |
负值超出int范围 | 0x80000000 | long最小值 |
4.XCHGL问题:
chgl 是 x86 上的汇编指令,作用是通过原子操作交换内存变量的值,Power平台提供lwarx/ldarx及stwcx/stdcx指令组合来实现内存变量的原子交换
__xchg_u64_local(volatile void *p, unsigned long val)
unsigned long prev;
__asm__ __volatile__(
"1: ldarx %0,0,%2 \\n"
PPC405_ERR77(0,%2)
" stdcx. %3,0,%2 \\n\\
bne- 1b"
: "=&r" (prev), "+m" (*(volatile unsigned long *)p)
: "r" (p), "r" (val)
: "cc", "memory");
5.SIMD扩展指令兼容性问题
目前不同平台均可以通过SIMD(单指令多数据流)扩展指令集提供向量技术,但不同架构平台提供的指令集完全不同,X86平台使用MMX/SSE/AVX指令集,而目前Power平台目前使用VSX指令集
如:popcnt指令,需要在代码中将X86的SSE指令集替换为PPC64le的指令:
X86: |
PPC64le: |
6.内存页大小兼容性问题
不同架构平台使用的内存页大小有所差异,X86平台使用页大小为4KB,Power平台默认使用的页大小为64KB,如果应用程序中使用了错误的页大小会导致不可预测的问题。
如MozJS17(Mozilla javascript 引擎)代码中将Page Size固化为4KB导致在64KB内存页大小的平台中运行时产生段错误,因此需要修改Page Size为64KB:
#if defined(SOLARIS) && (defined(__sparc) || defined(__sparcv9))
const size_t PageShift = 13;
const size_t ArenaShift = PageShift;
#elif defined(__powerpc__) || defined(__aarch64__)
const size_t PageShift = 16;
2软件迁移步骤
整个软件迁移需要经过四个步骤:迁移准备、软件分析、编译迁移、优化。
2.1迁移准备
Power平台环境准备,可采用下列任意一种方式:
- 向浪潮商用远程测试中心申请迁移测试环境
- 本地可连接网络的Power服务器
2.2软件分析
本阶段主要进行被迁移软件的软件栈分析及迁移策略制定。
2.2.1
软件栈分析
- 基础环境分析:分析当前软件的基础环境,主要包括操作系统、中间件、编译器等,以便于后续软件迁移时尽可能的让Power平台的基础环境与之对齐,尽量减少两者的差异。
- 上层应用分析:分析软件包及其依赖组件,根据能否获得源码,将待移植软件及其组件分为开源/自研以及闭源两种类型,不同类型的软件采取不同的迁移策略。
2.2.2
开源/自研软件
对于开源/自研软件而言,可以根据实现软件编程语言类型的不同制定不同的迁移策略。高级编程语言通常可以分为两类:一类是编译型语言,一类是解释型语言。编译型语言通过编译器生成机器语言程序,机器语言程序可以直接被CPU执行;解释型语言通过对应的语言虚拟机或者解释器执行,语言虚拟机或者解释器屏蔽了不同CPU架构的差异。
翻译方式 | 迁移策略 |
编译型语言 | 源码:重新编译 |
依赖组件:获取Power版本或者下载源码重新编译,闭源组件需要替换Power版本或替换类似组件库 | |
解释型语言 | 替换Power版本的语言虚拟机或者解释器,如JDK或Python解释器 |
依赖组件:获取Power版本或者下载源码重新编译,闭源软件需要替换Power版本或者替换类似组件库 |
2.3编译迁移
由于解释型语言迁移相对简单,我们将重点介绍编译型语言的迁移过程,主要需要进行编译构建脚本和C/C++源码的修改。
2.3.1
解释型语言
直接翻译:对于解释性语言实现的程序,代码无需修改,程序也不需要重新编译,直接替换对应Power版本的语言虚拟机或者解释器即可,如系统自带的OpenJDK及Python解释器。
依赖库编译:如果软件包含由编译型语言实现的依赖库,则需要获取依赖库对应的Power版本,如无法获取可用的Power版本,则需下载依赖库的源码包重新编译依赖库。
2.3.2
编译型语言
编译环境准备
安装支持Power平台的编译器,目前可用的编译器如下:
GCC4.8.5及以上版本、LLVM8.0及以上、IBM XLC/C++、IBM XLF、IBM AT(IBM Advance-toolchain)
编译选项移植
- Configure文件中增加适配PPC64le编译选项,例如使用支持PPC64le架构的config.guess
- 显式定义Char类型变量为有符号类型,在Makefile文件中添加“-fsigned-char”编译选项(X86默认是有符号数,Power平台默认是无符号数)
- Power平台不支持MMX/SSE/AVX指令,去掉“-msse”、“-msse4.1”等AVX编译选项
编译宏移植
- GCC编译器自定义平台属性编译宏:将“__X86_64__”或“__x86_64”替换为“__powerpc64__”
- 用户自定义平台属性编译宏:例如将“HAVE_X86_64”替换为“HAVE_POWERPC64”
源码移植
- SIMD扩展指令集移植:
①MMX/SSE代码适配:升级到GCC 8+,添加编译选项“-DNO_WARN_X86_INTRINSICS”或者直接修改代码,使用VSX指令进行替换
②AVX代码适配:使用VSX指令进行替换
- 汇编指令移植:
Power的汇编语言与X86的完全不同,所有涉及到使用汇编语言的代码,都需要基于Power进行移植。
2.4优化
2.4.1编译器优化
可以将GCC升级为以下两种编译器:
- IBM XLC/C++、IBM XLF,IBM发布的针对C/C++及Fortran的编译器,针对Power平台进行了深度优化,提高应用程序运行效率
- IBM AT(IBM Advance-toolchain),IBM发布的充分利用Power硬件特性的编译工具集,包含了最新版本的编译器(基于GCC)、更完整的功能库、DEBUG分析工具等
2.4.2编译选项优化
GCC/AT编译选项:-O3 -flto -funroll-loops -ffast-math -mcpu=power9 -mtune=power9
XLC/C++编译选项:-O3 -qhot -qipa -qpdf2 -qarch=pwr9 -qtune=pwr9
XLC/C++编译选项 | GCC/AT编译选项 | 优化内容 |
-O3 | -O3 | 提高代码的并行执行度,充分利用CPU的流水线、Cache等 |
-qhot | -funroll-loops | 循环优化以及函数内联,可以得到较好的程序性能 |
-qipa | -flto | 跨编译单元优化,适用于调用小函数特别多的场景 |
-qpdf1-qpdf2 | -fprofile-generate-fprofile-use | 反馈式编译优化,适用于大量的跳转或分支以及间接函数调用等 |
- | -ffast-math | 触发non-standards-compliant浮点优化,适用于浮点精度要求不高的浮点计算优化 |
-qarch=pwr9 | -mcpu=power9 | 针对POWER9的架构优化 |
2.4.3组件库优化
使用IBM提供的针对Power平台深度优化过的组件库,如IBMESSL、IBM SpectrumR MPI。
2.4.4OpenJDK优化
Power平台与X86平台相比,需要增加针对垃圾回收的优化:
-XX:+UseParallelGC -XX:+UseAdaptiveSizePolicy -XX:ParallelGCThreads=4。 |
2.4.5代码优化
- 基于Cacheline数据对齐调整:为提升Cache读写效率,同时避免Cacheline伪共享问题影响程序性能,可在程序代码中进行Cacheline对齐。Power上Cacheline Size是128 Bytes,X86 Cacheline Size是64 Bytes,代码需要针对Power平台Cacheline Size大小进行相应调整。例如:
-
struct int count __attribute__((aligned(128))); counts[N_CPUS];
该版本已经上传至GitHub,供开源社区小伙伴们查阅使用:基于内存页数据对齐调整 :应用程序通过基于内存页的数据隔离以避免高并发时的内存资源竞争问题,提升程序性能。Power上Page Size是64KB,X86上Page Size是4KB,一些应用中显式指定了Page Size的大小来实现,这种情况下代码需要针对Power平台Page Size大小进行调整。例如:-
long page_size =sysconf(_SC_PAGE_SIZE); void *data; size_t data_size = (size_of_data); posix_memalign(&data,page_size, data_size);
https://github.com/powerfans/PortingSW2POWER
以上是关于Power平台迁移指南的主要内容,如果未能解决你的问题,请参考以下文章