近两年随着外部环境的影响,国产化支持和认证变得热了起来。自从公司接触了一些国企背景的项目之后,公司也自然希望自己的产品得到国产化认证。其中,自主操作系统和处理器的认证就显得很重要了。
非x86处理器中,最普及的就是ARM架构的处理器,这些处理器也相对容易获得,软件生态也较好,况且本身Tensorflow也是支持ARM的,所以就来编译ARM环境的libtensorflow了。
第一部分:在鲲鹏CPU上编译libtensoflow 1.15.5
这一部分参考了KumaTea提供的tensorflow aarch64版本 和教程,以及官方的树莓派脚本。
鲲鹏920 CPU 本身是一个64位ARM架构的处理器。也是市场上ARM架构CPU的服务器中,体验起来最简单的一款,要体验飞腾CPU嘛,那恐怕得上京东高价买笔记本了。
使用ARM处理器的服务器,好处是不存在交叉编译,对依赖系统编译器的Tensorflow bazel脚本来说比较友好。但缺点也是不小的——各类工具都需要安装ARM版本的,很多还得从源码编译。
环境安装
考虑到和公司产品步调一致,我选择的是 CenOS 7.6 64位 镜像,因此下面的安装基于yum。
yum -y install java-1.8.0-openjdk java-1.8.0-openjdk-devel swig python3
目前,yum源默认的Python3是Python3.6.8,对编译libtensoflow没啥影响,编译python的tensorflow就需要考虑版本了。
安装git
编译tensorflow,git需要2.x版本, x86_64的CPU可以从endpoint-dev仓库获取git 2.x版本。但是换成ARM64的就不行了。所以还得自己动手编译。
参考这篇文章,开始手动编译git:
wget http://mirrors.edge.kernel.org/pub/software/scm/git/git-2.36.2.tar.gz \&& tar -zxvf git-2.36.2.tar.gz \yum install -y curl-devel expat-devel gettext-devel openssl-devel zlib-devel perl-ExtUtils-MakeMakercd git-2.36.2/
接下来就要开始编译git了。
mkdir -p /usr/local/gitmake prefix=/usr/local/git allmake prefix=/usr/local/git install
接着就需要替换系统中的git,git本身和go-lang绑定,所以还不能轻易卸载。
安装必要python库
编译用到的python库中,NumPy新版本也没有ARM64的,由于编译Numpy还需要安装gfortran,简单起见,可以直接安装 yum 源上面的1.12版本:
yum install python36-numpy
其他几个必需的库倒是不限定架构,直接安装
pip --no-cache-dir install keras_preprocessing --no-deps -i https://mirrors.aliyun.com/pypi/simple
安装bazel
Tensoeflow 1.15 需要 bazel 0.24.1 以上,0.26.1 以下,然而,bazel官方提供linux ARM64的最低版本都已经是3.4.0了。
尝试新版,翻车
首先尝试用官方提供linux ARM64的最低版本,3.4.0,wget https://github.com/bazelbuild/bazel/releases/download/3.4.0/bazel-3.4.0-linux-arm64
这文件相当大,华为云要反复断点续传才能拖下来。
下载下来的并不是安装包,而是一个可执行文件,所以执行的好似第一行显示安装,结果直接显示帮助了。chmod a+x bazel-3.4.0-linux-arm64cp bazel-3.4.0-linux-arm64 /usr/local/bin/ln -s /usr/local/bin/bazel-3.4.0-linux-arm64 /usr/bin/bazel
然后修改
./configure.py
,把_TF_MAX_BAZEL_VERSION = '0.26.1'
改为_TF_MAX_BAZEL_VERSION = '3.4.0'
看起来,编译流程好像正常跑起来了,lintensorflow_framework.so好像也能编译,可走到编译各个op的地方,还没链接就报错了。链接报错,lintensorflow_framework.so缺少ApiDefs::ApiDefs() ApiDefs::~ApiDefs()等等,甚至没有protobuf等等东西。
google百度找了半天也没看到解决方法,只能比对一下编译日志,好在确实发现问题了。毕竟在刚开始的时候,bazel3.4也没有像bazel0.26.1那样报警告嘛。没办法,只能回退bazel版本了。下载bazel0.26.1并手动编译:
yum -y install zipwget https://github.com/bazelbuild/bazel/releases/download/0.26.1/bazel-0.26.1-dist.zipunzip bazel-0.26.1-dist.zip -d bazel/cd bazel/env EXTRA_BAZEL_ARGS="--host_javabase=@local_jdk//:jdk" bash ./compile.sh
好在bazel0.26.1并不需要想官方版本一样修改代码。
这源码包比程序大多了,唉,没办法,上加速器了。
另外有些教程上直接执行compile.sh
也是不对的,按官方的来。
下载源码并编译
从git下载官方版本Tensorflow源码:
git clone --branch=r1.15 --depth=1 https://github.com/tensorflow/tensorflow.git .
接下来就是配置了,配置流程和x86最简配置差不多,基本就是一路N。config=opt的时候留空,方便测试传参数。
gcc 4.8.5-aarch64 并没有 aarch64 这个架构参数,不仅如此,鲲鹏CPU上连native都不支持,看gcc手册,还是配成armv8-a吧。
参考官方树莓派脚本,参数配置应该是这样的:
export PI_COPTS="--copt=-march=armv8-a --copt=-mfpu=neon-vfpv4--copt=-std=gnu11 --copt=-DS_IREAD=S_IRUSR --copt=-DS_IWRITE=S_IWUSR--copt=-O3 --copt=-fno-tree-pre --copt=-U__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1--copt=-U__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2 --copt=-U__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8--define=raspberry_pi_with_neon=true"bazel build -c opt ${PI_COPTS} --config=monolithic \--copt=-funsafe-math-optimizations --copt=-ftree-vectorize --copt=-fomit-frame-pointer \--cpu=aarch64 --define tensorflow_mkldnn_contraction_kernel=0 --verbose_failures \//tensorflow/java:libtensorflow_jni //tensorflow:libtensorflow_framework.so
开始,我用树莓派的参数去编译,编译倒是编译完成了,可一执行,就报“pure virtual method called terminate called without an active exception”。没办法,只能把不靠谱的参数删掉一点点试。
最后试验出来,用下面的配置编译成功:
export AARCH64_COPTS="--copt=-march=armv8-a --copt=-std=gnu11 --copt=-O3 \ --copt=-fno-tree-pre --copt=-funsafe-math-optimizations --copt=-ftree-vectorize"bazel build -c opt ${AARCH64_COPTS} --cpu=aarch64 --config=noaws --config=nogcp \ --define tensorflow_mkldnn_contraction_kernel=0 \ //tensorflow/java:libtensorflow_jni //tensorflow:libtensorflow_framework.so
这时候,使用--local_ram_resources=10240 --jobs=4
之类的参数,限制bazel编译过程的资源占用就很有必要了,毕竟尝试要花钱的,等到正式编译了,建议在华为云中动态调整CPU资源。