设置 RPC 系统#
远程过程调用(Remote procedure call,简称 RPC)是 Apache TVM 中非常重要且实用的功能。它使我们能够在真实硬件上运行编译过的神经网络(NN)模型,而无需直接接触远程设备。输出结果将通过网络自动传回。
通过消除如将输入数据转储到文件、复制导出的神经网络模型到远程设备、设置设备用户环境、复制输出结果到主机开发环境等手动工作,RPC 极大地提高了开发效率。
此外,由于编译后的神经网络模型只有执行部分在远程设备上运行,所有其他部分都在主机开发环境中运行,因此可以使用任何 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 代理只需在主机上运行,例如开发服务器或个人电脑,它们无需依赖于任何环境或设备机器。因此,安装 Apache TVM 后,根据官方文档 https://tvm.apache.org/docs/install/index.html 所述,设置它们的唯一工作就是在相应的机器上执行以下命令。
RPC Tracker
$ python3 -m tvm.exec.rpc_tracker --host RPC_TRACKER_IP --port 9190 --port-end 9191
RPC Proxy
$ 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 运行时和其他库。请先安装 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
$ sed -i "s|USE_MICRO.*)|USE_MICRO 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
,就可以解决问题。