交叉编译和 RPC
单击 此处 下载完整的示例代码
本教程介绍了如何在 TVM 中使用 RPC 进行交叉编译和远程设备执行。
利用交叉编译和 RPC,可以实现程序在本地机器编译,在远程设备运行。这个特性在远程设备资源有限时(如在树莓派和移动平台上)尤其有用。本教程将把树莓派作为 CPU 示例,把 Firefly-RK3399 作为 OpenCL 示例进行演示。
在设备上构建 TVM Runtime
首先在远程设备上构建 TVM runtime。
本节和下一节中的所有命令都应在目标设备(例如树莓派)上执行。假设目标设备运行 Linux 系统。
由于在本地机器上只做编译,而远程设备用于运行生成的代码。所以只需在远程设备上构建 TVM runtime。
git clone --recursive https://github.com/apache/tvm tvm
cd tvm
make runtime -j2
成功构建 runtime 后,要在 ~/.bashrc
文件中设置环境变量。可以用 vi ~/.bashrc
命令编辑 ~/.bashrc
,在这个文件里添加下面这行代码(假设 TVM 目录在 ~/tvm
中):
export PYTHONPATH=$PYTHONPATH:~/tvm/python
执行 source ~/.bashrc
来更新环境变量。
在设备上设置 RPC 服务器
在远程设备(本例为树莓派)上运行以下命令来启动 RPC 服务器:
python -m tvm.exec.rpc_server --host 0.0.0.0 --port=9090
看到下面这行提示,则表示 RPC 服务器已成功启动。
INFO:root:RPCServer: bind to 0.0.0.0:9090
在本地机器上声明和交叉编译内核
现在回到本地机器(已经用 LLVM 安装了完整的 TVM)。
在本地机器上声明一个简单的内核:
import numpy as np
import tvm
from tvm import te
from tvm import rpc
from tvm.contrib import utils
n = tvm.runtime.convert(1024)
A = te.placeholder((n,), name="A")
B = te.compute((n,), lambda i: A[i] + 1.0, name="B")
s = te.create_schedule(B.op)
然后交叉编译内核。对于树莓派 3B,target 是“llvm -mtriple=armv7l-linux-gnueabihf”,但这里用的是“llvm”,使得本教程可以在网页构建服务器上运行。请参阅下面的详细说明。
local_demo = True
if local_demo:
target = "llvm"
else:
target = "llvm -mtriple=armv7l-linux-gnueabihf"
func = tvm.build(s, [A, B], target=target, name="add_one")
# 将 lib 存储在本地临时文件夹
temp = utils.tempdir()
path = temp.relpath("lib.tar")
func.export_library(path)
要使本教程运行在真正的远程设备上,需要将 local_demo
改为 False,并将 build
中的 target
替换为适合设备的 target 三元组。不同设备的 target 三元组可能不同。例如,对于树莓派 3B,它是 llvm -mtriple=armv7l-linux-gnueabihf
;对于 RK3399,它是 llvm -mtriple=aarch64-linux-gnu
。
通常,可以在设备上运行 gcc -v
来查询 target,寻找以 Target
开头的行:(尽管它可能仍然是一个松散的配置。)
除了 -mtriple
,还可设置其他编译选项,例如:
-
-mcpu=<cpuname\>
指定生成的代码运行的芯片架构。默认情况这是从 target 三元组推断出来的,并自动检测到当前架构。
-
-mattr=a1,+a2,-a3,…
覆盖或控制 target 的指定属性,例如是否启用 SIMD 操作。默认属性集由当前 CPU 设置。要获取可用属性列表,执行:
llc -mtriple=<your device target triple> -mattr=help
这些选项与 llc 一致。建议设置 target 三元组和功能集,使其包含可用的特定功能,这样我们可以充分利用单板的功能。查看 LLVM 交叉编译指南 获取有关交叉编译属性的详细信息。