部署TVM Runtime
部署TVM Runtime
本文主要介绍如何在开发板上部署TVM Runtime, 在本地机器安装完整的TVM(包含了TVM Runtime以及编译功能), 并且使用一个简单的远程调用例子测试是否部署成功。本地机器使用的是Linux操作系统,开发板使用的是预装的Fedora系统。
开发板与TVM的概述
开发板
开发板(Embedded AI Development Kit),以 Arm SoC 为硬件平台,Tengine(Arm 中国周易平台)为核心的人工智能基础软件平台 AID,集成典型应用算法,所形成的 “软硬一体化”的 AI 开发套件; 专为 AI 开发者精心打造,面向边缘计算的人工智能开发套件。
硬件平台具备语音,视觉等传感器数据采集能力,适用于多场景的运动控制接口。
软件平台支持视觉处理与分析,语音识别,语义分析,SLAM 等应用和主流开源算法,满足AI 教育,算法应用开发,产品原型开发验证等需求。
软件是产品系列套件,主芯片采用具备主流性能 Arm SoC 的RK3228H,搭载 OPEN AI LAB 嵌入式 AI 开发平台 AID(包含支持异构计算库 HCL,嵌入式深度学习框架 Tengine,以及轻量级嵌入式计算机视觉加速库 BladeCV)。为 AI 应用提供简洁,高效,统一的 API 接口,加速终端 AI 产品的场景化应用落地。
TVM
TVM是一个针对CPU,GPU和专用加速器的开放式深度学习编译器堆栈,旨在缩小以算力为重点的深度学习框架与性能或效率为导向的硬件后端之间的差距。TVM提供以下主要功能:
将Keras, MXNet, PyTorch, Tensorflow, CoreML, DarkNet中的深度学习模型,编译成各种硬件后端上可部署的最小模块。
基础架构可在更多后端自动生成和优化张量运算符,同时得到更好的性能。
一些概念
• 远程设备,目标设备,本地机器
将开发板称为“远程设备(remote device)”和“目标设备(target device)”,用来执行生成的代码所编译出来的二进制可执行文件。因为远程连接开发板,目标是生成对开发板高效的代码。连接开发板的机器叫做“本地机器(local machine)”,用来实现生成代码,对代码进行优化,编译二进制可执行文件等功能。本地机器也拥有执行二进制可执行文件的功能。
• 架构
开发板使用的是ARM Cortex-A53处理器,架构为ARMv8,本地机器通常是x86架构。这两种架构使用了不同的指令集,使用不同指令集的程序不能够同时在不同的架构上运行。比如针对x86指令集编译的程序不能够ARM架构上运行,同样针对ARM指令集编译的程序不能够在x86架构上运行。TVM可以生成一份代码,将这份代码编译成x86的程序,在本地机器上进行调试;也可以将这份代码交叉编译成ARM程序,在目标设备也就是开发板上运行。
• 完整版的TVM 和 TVM Runtime
完整版的TVM支持生成代码,优化代码,编译,执行等功能;而TVM Runtime是TVM的一个部件,用来执行生成的二进制文件,其优点是能够更轻便地部署。
在开发板上编译TVM Runtime
将在开发板上编译TVM Runtime,在编译前,需要安装第三方依赖库。
安装第三方依赖库
TVM Runtime需要使用psutil这个Python库,获取系统的运行状态信息。而安装psutil前,需要安装两个软件包,让psutil能够成功地编译。
• psutil
默认的Python为Python3, 依次输入以下命令即可安装psutil.
sudo dnf install redhat-rpm-config python3-devel
sudo pip3 install psutil
如果想使用Python2,可以输入以下命令:
sudo dnf install redhat-rpm-config python-devel
sudo pip2 install psutil
安装完psutil后,就可以开始在开发板上编译TVM Runtime.
在开发板上编译TVM Runtime
在开发板的命令行界面下,下载TVM的代码:
git clone https://github.com/dmlc/tvm
Cloning into 'tvm'...
remote: Enumerating objects: 7, done.
remote: Counting objects: 100% (7/7), done.
remote: Compressing objects: 100% (7/7), done.
remote: Total 46164 (delta 0), reused 0 (delta 0), pack-reused 46157
Receiving objects: 100% (46164/46164), 15.88 MiB | 302.00 KiB/s, done.
Resolving deltas: 100% (31261/31261), done.
进入TVM的代码目录,并且输入git⊂mode–update−−recursive−−∈itgit⊂mode̲update--recursive--∈it,下载第三方依赖代码:
[openailab@localhost ~]$ cd tvm
[openailab@localhost tvm]$ git submodule update --recursive --init
Submodule 'dlpack' (https://github.com/dmlc/dlpack) registered for path '3rdparty/dlpack'
Submodule 'dmlc-core' (https://github.com/dmlc/dmlc-core) registered for path '3rdparty/dmlc-core'
Submodule '3rdparty/rang' (https://github.com/agauniyal/rang) registered for path '3rdparty/rang'
Cloning into '/home/openailab/tvm/3rdparty/dlpack'...
Cloning into '/home/openailab/tvm/3rdparty/dmlc-core'...
Cloning into '/home/openailab/tvm/3rdparty/rang'...
Submodule path '3rdparty/dlpack': checked out '0acb731e0e43d15deee27b66f10e4c5b4e667913'
Submodule path '3rdparty/dmlc-core': checked out '808f485387f9a03f78fa9f1159f387d0d91b7a28'
Submodule path '3rdparty/rang': checked out 'cabe04d6d6b05356fa8f9741704924788f0dd762'
(由于TVM的版本可能不同,checked out后的哈希值也可能不同)
输入make runtime -j2进行编译,数字2指用2个线程进行编译。
[openailab@localhost tvm]$ make runtime -j2
等待大概1到2分钟,出现以下提示说明编译成功:
[100%] Built target runtime
将TVM文件夹的Python目录添加到环境变量PYTHONPATH中:
[openailab@localhost tvm]$ cd ..
[openailab@localhost ~]$ vim .bashrc
在.bashrc的最后一行加入export PYTHONPATH=$PYTHONPATH:~/tvm/python
如果使用其它路径,需要对路径进行修改。
再退出编辑器,执行命令:source .bashrc,立即使用.bashrc中的设置。
在本地机器编译完整版的TVM
打开本地机器的命令行,同样需要下载TVM代码及其第三方依赖代码。
git clone https://github.com/dmlc/tvm
cd tvm
git submodule update --recursive --init
在tvm文件夹下,拷贝cmake配置并修改,然后开始编译:
cp cmake/config.cmake ./
sed -i "s/USE_LLVM OFF/USE_LLVM ON/" config.cmake # 将USE_LLVM设置为ON
make -j5 # 使用5个线程编译
执行完后没有任何错误信息,则说明编译完成。
设置TVM Python绑定的路径:
假如使用bash,在~/.bashrc的最后一行添加以下脚本(如果使用zsh, 则在~/.zshrc中添加):
export TVM_HOME=/path/to/tvm
export PYTHONPATH=$TVM_HOME/python:$TVM_HOME/topi/python:$TVM_HOME/nnvm/python:${PYTHONPATH}
将/path/to/tvm替换为TVM代码所在路径,该路径有python,topi,nnvm三个文件夹。
在本地机器安装好TVM后,可以进行以下测试:
打开Python并导入TVM模块
➜ ~ python
Python 3.7.4 (default, Jul 16 2019, 07:12:58)
[GCC 9.1.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import tvm
>>>
没有任何错误提示,说明安装成功。
运行第一个TVM样例:交叉编译与远程过程调用(Cross Complication and RPC)
将使用TVM, 实现一个元素个数为1024的向量进行逐元素加1后,输出结果的核函数,在本地机器上进行调试,并在开发板开发板上运行。本样例来源于TVM的官方教程 - Cross Complication and RPC,对其进行稍许修改以适配开发板.
在开发板上启用TVM的RPC(远程过程调用)服务。
在开发板的命令行界面输入:python -m tvm.exec.rpc_server --host 0.0.0.0 --port=3939
3939为TVM RPC服务的端口,可以对其进行修改,注意开发板和本地机器上,其它程序都不要占用这个端口。TVM官方教程给的端口号为9090,本地机器的9090端口被占用了,修改为3939。
[openailab@localhost ~]$ python -m tvm.exec.rpc_server --host 0.0.0.0 --port=3939
INFO:root:If you are running ROCM/Metal, fork will cause compiler internal error. Try to launch with arg ```--no-fork```
INFO:RPCServer:bind to 0.0.0.0:3939
不要退出这个命令,以保持RPC服务的运行。
对本地机器进行配置
在本地机器的命令行界面,下载修改后的教程代码,并进入目录:
git clone https://github.com/wkcn/TVM_tutorials_for_EAIDK
cd TVM_tutorials_for_EAIDK
编辑config.py文件,将host和port修改为开发板的IP地址和开发板上TVM服务的端口。
假设开发板在局域网下的IP为192.168.43.33,TVM RPC的端口为3939,则将配置文件修改为下图:
因为开发板使用的是ARMv8指令集,所以LLVM的编译目标为aarch64-linux-gnu.
运行RPC例子
• 在本地机器调试
进入TVM_tutorials_for_EAIDK的目录,将cross_compilation_and_rpc.py文件中第115行的local_demo设为True,然后输入pythn cross_compilation_and_rpc.py.
➜ TVM_tutorials_for_EAIDK git:(master) ✗ python cross_compilation_and_rpc.py
1.225e-07 secs/op
命令行中将会输出核函数的实际调用时间。
• 在开发板上执行
将cross_compilation_and_rpc.py文件中第115行的local_demo设为False,然后输入python cross_compilation_and_rpc.py
TVM将会在本地机器上进行交叉编译,并将编译结果上传到开发板。开发板上的TVM RPC服务下载可执行的二进制文件,并执行,将结果返回到本地机器。
本地机器的命令行将输出核函数在开发板(目标设备)上的实际调用时间,不包含网络传输文件的时间。
➜ TVM_tutorials_for_EAIDK git:(master) ✗ python cross_compilation_and_rpc.py
4.3455e-06 secs/op
切换到开发板的命令行界面,可以看到:
INFO:RPCServer:bind to 0.0.0.0:3939
INFO:RPCServer:connection from ('192.168.43.57', 47010)
INFO:RPCServer:load_module /tmp/tmpge372920/lib.tar
INFO:RPCServer:Finish serving ('192.168.43.57', 47010)
说明开发板收到了本地机器(本地机器的IP为192.168.43.57)编译的可执行二进制文件的压缩包lib.tar,对其解包并执行模块,最终返回结果,结束服务。
这是一个TVM样例,可对TVM的例子进行分析,这些例子包括本文RPC例子,在开发板上运行一个分类模型,自动调整以生成适合开发板的代码。