设置 RPC 系统
远程过程调用 (RPC) 是 Apache TVM 非常重要且有用的功能,它允许我们在真实的硬件上运行已编译的神经网络 (NN) 模型,而无需接触远程设备,输出结果将通过网络自动传回。
通过消除手动工作,例如将输入数据转储到文件,将导出的 NN 模型复制到远程设备,设置设备用户环境,将输出结果复制到主机开发环境,RPC 极大地提高了开发效率。
此外,由于只有已编译的 NN 模型的执行部分在远程设备上运行,所有其他部分都在主机开发环境中运行,因此可以使用任何 Python 包来完成预处理和后处理工作。
RPC 在以下两种情况下非常有用
硬件资源有限
RPC 的队列和资源管理机制可以使硬件设备为许多开发人员和服务测试作业提供服务,以正确运行已编译的 NN 模型。
早期端到端评估
除了已编译的 NN 模型外,所有其他部分都在主机开发环境中执行,因此可以轻松实现复杂的预处理或后处理。
建议的架构
Apache TVM RPC 包含 3 个工具:RPC 跟踪器、RPC 代理和 PRC 服务器。RPC 服务器是必要的,即使没有 RPC 代理和 RPC 跟踪器,RPC 系统也可以正常工作。当您无法直接访问 RPC 服务器时,需要 RPC 代理。强烈建议在您的 RPC 系统中添加 RPC 跟踪器,因为它提供了许多有用的功能,例如队列功能、多 RPC 服务器管理、通过密钥而不是 IP 地址管理 RPC 服务器。
如上图所示,由于机器 A 和机器 C、D 之间没有物理连接通道,因此我们在机器 B 上设置了一个 RPC 代理。RPC 跟踪器为每个 RPC 密钥管理一个请求队列,每个用户可以在任何时候通过 RPC 密钥从 RPC 跟踪器请求 RPC 服务器,如果存在具有相同 RPC 密钥的空闲 RPC 服务器,则 RPC 跟踪器将 RPC 服务器分配给用户,如果目前没有空闲 RPC 服务器,则请求将放入该 RPC 密钥的请求队列中,并在稍后检查。
设置 RPC 跟踪器和 RPC 代理
一般来说,RPC 跟踪器和 RPC 代理只需要在主机上运行,例如开发服务器或 PC,它们不需要依赖设备机器的任何环境,因此设置它们的唯一工作是在按照官方文档 https://tvm.apache.org/docs/install/index.html 安装 Apache TVM 后,在相应的机器上执行以下命令。
RPC 跟踪器
$ python3 -m tvm.exec.rpc_tracker --host RPC_TRACKER_IP --port 9190 --port-end 9191
RPC 代理
$ python3 -m tvm.exec.rpc_proxy --host RPC_PROXY_IP --port 9090 --port-end 9091 --tracker RPC_TRACKER_IP:RPC_TRACKER_PORT
请根据您的具体环境修改上述命令中的 *RPC_TRACKER_IP*、*RPC_TRACKER_PORT*、*RPC_PROXY_IP* 和端口号,选项 port-end
可用于避免服务以意外的端口号启动,这可能会导致其他服务无法正确连接,这对于自动化测试系统尤其重要。
设置 RPC 服务器
在我们的社区中,有多种 RPC 服务器实现,例如 apps/android_rpc
、apps/cpp_rpc
、apps/ios_rpc
,以下内容仅关注由 python/tvm/exec/rpc_server.py
实现的 Python 版本 RPC 服务器,有关其他版本 RPC 服务器的设置说明,请参考其相应目录的文档。
RPC 服务器需要在设备机器上运行,它通常会依赖于 xPU 驱动程序、增强的 TVM 运行时(带有 xPU 支持)和其他库,因此请首先设置依赖组件,例如,安装 KMD 驱动程序,确保可以从环境变量 LD_LIBRARY_PATH
中找到所需的动态库。
如果可以在您的设备机器上设置所需的编译环境,即您不需要进行交叉编译,那么只需按照 https://tvm.apache.org/docs/install/from_source.html 的说明编译 TVM 运行时,然后直接跳到步骤 3. 启动 RPC 服务器。
1. 交叉编译 TVM 运行时
我们使用 CMake 来管理编译过程,对于交叉编译,CMake 需要一个工具链文件来获取所需的信息,因此您需要根据您的设备平台准备此文件,下面是 CPU 为 64 位 ARM 架构且操作系统为 Linux 的设备机器的示例。
set(CMAKE_SYSTEM_NAME Linux)
set(root_dir "/XXX/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu")
set(CMAKE_C_COMPILER "${root_dir}/bin/aarch64-linux-gnu-gcc")
set(CMAKE_CXX_COMPILER "${root_dir}/bin/aarch64-linux-gnu-g++")
set(CMAKE_SYSROOT "${root_dir}/aarch64-linux-gnu/libc")
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
在 TVM 仓库的根目录下执行类似以下的命令后,运行时将成功交叉编译,请根据您的具体要求在文件 config.cmake
中启用其他需要的选项。
$ mkdir cross_build
$ cd cross_build
$ cp ../cmake/config.cmake ./
# You maybe need to enable other options, e.g., USE_OPENCL, USE_xPU.
$ sed -i "s|USE_LLVM.*)|USE_LLVM OFF)|" config.cmake
$ sed -i "s|USE_LIBBACKTRACE.*)|USE_LIBBACKTRACE OFF)|" config.cmake
$ cmake -DCMAKE_TOOLCHAIN_FILE=/YYY/aarch64-linux-gnu.cmake -DCMAKE_BUILD_TYPE=Release ..
$ cmake --build . -j -- runtime
$ cd ..
2. 打包和部署到设备机器
通过类似以下的命令打包 Python 版本 RPC 服务器。
$ git clean -dxf python
$ cp cross_build/libtvm_runtime.so python/tvm/
$ tar -czf tvm_runtime.tar.gz python
然后将压缩包 tvm_runtime.tar.gz
复制到您的具体设备机器,并通过设备机器上类似以下的命令正确设置环境变量 PYTHONPATH
。
$ tar -xzf tvm_runtime.tar.gz
$ export PYTHONPATH=`pwd`/python:${PYTHONPATH}
3. 启动 RPC 服务器
RPC 服务器可以通过设备机器上类似以下的命令启动,请根据您的具体环境修改 *RPC_TRACKER_IP*、*RPC_TRACKER_PORT*、*RPC_PROXY_IP*、*RPC_PROXY_PORT* 和 *RPC_KEY*。
# Use this if you use RPC proxy.
$ python3 -m tvm.exec.rpc_server --host RPC_PROXY_IP --port RPC_PROXY_PORT --through-proxy --key RPC_KEY
# Use this if you needn't use RPC proxy.
$ python3 -m tvm.exec.rpc_server --tracker RPC_TRACKER_IP:RPC_TRACKER_PORT --key RPC_KEY
验证 RPC 系统
$ python3 -m tvm.exec.query_rpc_tracker --host RPC_TRACKER_IP --port RPC_TRACKER_PORT
通过以上命令,我们可以查询所有可用的 RPC 服务器和队列状态,如果您有 3 个通过 RPC 代理连接到 RPC 跟踪器的 RPC 服务器,则输出应如下所示。
Tracker address RPC_TRACKER_IP:RPC_TRACKER_PORT
Server List
----------------------------
server-address key
----------------------------
RPC_PROXY_IP:RPC_PROXY_PORT server:proxy[RPC_KEY0,RPC_KEY1,RPC_KEY2]
----------------------------
Queue Status
---------------------------------------
key total free pending
---------------------------------------
RPC_KEY0 0 0 3
---------------------------------------
故障排除
1. 设备机器上缺少 numpy
导致 RPC 服务器无法启动。
包 numpy
在一些 RPC 服务器依赖的 Python 文件中被导入,消除导入关系很困难,对于某些设备,交叉编译 numpy
也非常困难。
但实际上 TVM 运行时并不真正依赖 numpy
,因此一个非常简单的解决方法是创建一个虚拟的 numpy
,只需将以下内容复制到一个名为 numpy.py
的文件中,并将其放在类似 /usr/local/lib/python3.9/site-packages
的目录中。
class bool_:
pass
class int8:
pass
class int16:
pass
class int32:
pass
class int64:
pass
class uint8:
pass
class uint16:
pass
class uint32:
pass
class uint64:
pass
class float16:
pass
class float32:
pass
class float64:
pass
class float_:
pass
class dtype:
def __init__(self, *args, **kwargs):
pass
class ndarray:
pass
def sqrt(*args, **kwargs):
pass
def log(*args, **kwargs):
pass
def tanh(*args, **kwargs):
pass
def power(*args, **kwargs):
pass
def exp(*args, **kwargs):
pass
2. 设备机器上缺少 cloudpickle
导致 RPC 服务器无法启动。
由于 cloudpickle
包是一个纯 Python 包,因此只需将其从其他机器复制到设备机器的 /usr/local/lib/python3.9/site-packages
等目录即可解决问题。