- 编译器
- 深度学习
- 模型优化
【免费下载链接】tvm
Open deep learning compiler stack for cpu, gpu and specialized accelerators
导读
本文以 Apache TVM 官方开发指南 docs/dev/how_to/setup_rpc_system.rst 为核心脉络,系统讲解 TVM RPC(Remote Procedure Call)远程过程调用体系:如何搭建 RPC Tracker 与 RPC Proxy、如何在真实设备上(含交叉编译场景)部署并启动 Python 版 RPC Server、如何验证整个 RPC 系统的连通性,以及常见启动问题的排查方案。读完本文,你将掌握一套"主机端编译模型 + 设备端只负责执行"的远程部署工作流,并能独立搭建支撑多用户、多设备共享的 RPC 基础设施。
RPC 是什么:为什么它是 TVM 部署的关键能力
远程过程调用(RPC)是 Apache TVM 中非常重要且实用的特性。它允许我们在不接触远程设备的情况下,把编译好的神经网络(NN)模型运行在真实硬件上,运行结果会自动通过网络回传到主机端。
传统的嵌入式/设备部署流程通常包含大量手工劳动:
- 把输入数据 dump 到文件;
- 把导出的 NN 模型拷贝到远程设备;
- 在设备上配置用户环境;
- 把输出结果再拷贝回主机开发环境。
RPC 通过将执行与开发解耦,极大地提升了开发效率:
- 只有编译好的 NN 模型的执行部分在远程设备上运行,其余所有环节都在主机开发环境完成,因此可以使用任意 Python 包做预处理(preprocess)与后处理(postprocess);
- 设备端与主机端之间只通过 TCP 网络交换数据与执行指令,开发迭代不必反复拷贝文件。
RPC 最有价值的两类场景
| 场景 | 价值点 |
|---|---|
| 硬件资源受限 | RPC 的队列(queue)与资源管理机制,可以让有限的硬件设备同时服务多个开发者与测试任务,按需执行编译好的 NN 模型,避免设备被某个任务独占 |
| 早期端到端评估 | 除编译好的 NN 模型外,其余部分全部在主机开发环境执行,复杂的预处理/后处理逻辑(如数据增强、解码、指标计算)可以用任何 Python 生态库轻松实现 |
推荐的 RPC 系统架构:Tracker、Proxy 与 Server 的角色分工
Apache TVM RPC 体系由三个组件构成:
- RPC Server(必需):运行在设备机器上,负责实际执行编译好的 NN 模型;
- RPC Proxy(按需):当主机无法直接访问 RPC Server(例如设备在 NAT 之后、没有公网 IP)时,Proxy 负责转发客户端与 Server 之间的消息;
- RPC Tracker(强烈建议):提供队列能力、多 RPC Server 管理、以及通过 key 而非 IP 地址管理 RPC Server等实用特性。
一个 RPC 系统可以没有 Proxy 和 Tracker 也能工作(例如直连场景),但官方强烈建议把 Tracker 加入系统,以获得资源调度与管理能力。
推荐的典型拓扑(对应官方指南中的架构图):假设机器 A 与机器 C、D 之间没有物理连接通道,则在机器 B 上部署 RPC Proxy 作为中转;RPC Tracker 为每个 RPC key 维护一个请求队列。工作流程为:
- 任意时刻,用户可以通过一个RPC key向 Tracker 请求一台 RPC Server;
- 如果存在同 key 的空闲RPC Server,Tracker 立即将该 Server 分配给用户;
- 如果当时没有空闲 Server,请求会被放入该 key 的请求队列,稍后 Tracker 会再次检查并完成分配。
这一机制在源码层面由 python/tvm/rpc/tracker.py 中的PriorityScheduler实现:每个 key 对应一个调度器,内部维护_values(空闲资源池)与_requests(按优先级堆排序的请求队列),通过put/request/remove/summary四个原语完成资源的注册、请求、回收与状态汇总(见 tracker.py#L125-L162)。Tracker 对外暴露的是基于 TCP 的 JSON 消息协议,核心指令包括PING(探活)、PUT(上报资源)、REQUEST(请求资源)、SUMMARY(查询汇总)等(见 tracker.py#L30-L41)。
搭建 RPC Tracker 与 RPC Proxy
一般而言,Tracker 与 Proxy只需运行在主机机器(如开发服务器或 PC)上,它们不依赖设备机器的任何环境。在按官方文档完成 TVM 安装(本仓库对应的安装说明见 docs/install/index.rst 与 docs/install/from_source.rst)后,在相应机器上执行下面的命令即可完成搭建。
启动 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参数的作用:它限定服务在[port, port-end)区间内寻找可用端口,避免服务意外启动在一个出乎意料的端口上,导致其他服务无法正确连接——这一点对自动化测试系统尤为重要。从源码看,Tracker 与 Proxy 启动时都会在range(port, port_end)内逐个尝试bind,遇到EADDRINUSE则继续尝试下一个端口(见 python/tvm/rpc/tracker.py#L407-L417 与 python/tvm/rpc/proxy.py#L525-L535)。
各命令行参数源码级说明
| 参数 | Tracker 默认值 | Proxy 默认值 | 说明(依据 python/tvm/exec/rpc_tracker.py 与 python/tvm/exec/rpc_proxy.py) |
|---|---|---|---|
--host | 0.0.0.0 | 127.0.0.1 | 服务绑定的主机 IP;Tracker 默认监听所有网卡,Proxy 默认只监听本机回环 |
--port | 9190 | 9090 | RPC 主端口 |
--port-end | 9199 | 9199 | 端口搜索区间上限(不含) |
--tracker | — | ""(不启用) | Proxy 以host:port格式上报到 RPC Tracker |
--web-port | — | 8888 | Proxy 的 HTTP/WebSocket 服务端口,用于浏览器端(wasm)RPC 场景 |
--silent | 关闭 | — | 是否以静默模式运行(仅记录 WARN 及以上日志) |
--example-rpc | — | False | 是否启用示例 RPC 模式(附带浏览器示例页面与静态资源) |
补充说明:Proxy 本身基于 Tornado 事件循环实现,若缺少tornado依赖,python/tvm/rpc/proxy.py#L34-L43 会抛出ImportError提示pip install tornado;Tracker 同理(见 python/tvm/rpc/tracker.py#L55-L61)。此外 Proxy 还支持通过--web-port开启 WebSocket 通道,使浏览器(如 web 端的 wasm RPC Server)也能接入同一套 RPC 体系(见 proxy.py#L260-L279)。
搭建 RPC Server
社区中存在多种 RPC Server 实现,例如:
- apps/android_rpc:Android 设备上的 RPC Server;
- apps/cpp_rpc:C++ 实现的 RPC Server(其入口与封装见 apps/cpp_rpc/main.cc);
- apps/ios_rpc:iOS 设备上的 RPC Server。
本节聚焦Python 版本的 RPC Server,其实现位于 python/tvm/exec/rpc_server.py。其他版本 Server 的搭建请参考各自目录下的文档。
RPC Server 需要运行在设备机器上,并且通常会依赖:
- xPU 驱动;
- 带 xPU 支持的增强版 TVM runtime;
- 其他相关动态库。
因此请先配置好依赖组件,例如安装 KMD 驱动、确保所需动态库能在环境变量LD_LIBRARY_PATH中找到。
如果设备机器上可以搭建编译环境(即无需交叉编译),直接按照 docs/install/from_source.rst 的说明编译 TVM runtime,然后跳到下文"启动 RPC Server"一节即可。否则按以下三步走。
第 1 步:交叉编译 TVM Runtime
TVM 使用 CMake 管理编译过程。交叉编译时,CMake 需要一个toolchain 文件来获取目标平台信息。下面是一个针对64 位 ARM CPU + Linux 操作系统设备机器的示例(aarch64-linux-gnu.cmake):
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 仓库根目录下执行类似如下命令,即可交叉编译出 runtime:
$ mkdir cross_build $ cd cross_build $ cp ../cmake/config.cmake ./ # 你或许还需要按需开启其他选项,例如 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 ..请按实际需求在 cmake/config.cmake 中启用其他选项(例如
USE_OPENCL、USE_xPU等),并将 toolchain 文件路径替换为你自己的/YYY/aarch64-linux-gnu.cmake。
第 2 步:打包并部署到设备机器
通过类似下面的命令打包 Python 版 RPC Server:
$ 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 Server
在设备机器上通过类似下面的命令启动 RPC Server,请根据实际环境替换RPC_TRACKER_IP、RPC_TRACKER_PORT、RPC_PROXY_IP、RPC_PROXY_PORT与RPC_KEY:
# 使用 RPC Proxy 时的启动方式: $ python3 -m tvm.exec.rpc_server --host RPC_PROXY_IP --port RPC_PROXY_PORT --through-proxy --key RPC_KEY # 不使用 RPC Proxy(直连 Tracker)时的启动方式: $ python3 -m tvm.exec.rpc_server --tracker RPC_TRACKER_IP:RPC_TRACKER_PORT --key RPC_KEY关键参数语义(依据 python/tvm/exec/rpc_server.py#L56-L95):
| 参数 | 默认值 | 说明 |
|---|---|---|
--host/--port | 0.0.0.0/9090 | Server 绑定地址;使用--through-proxy时,这里实际填的是 Proxy 的地址与端口 |
--through-proxy | 关闭 | 该 Server 是否通过 Proxy 提供服务 |
--tracker | 空 | RPC Tracker 地址,格式host:port(例如10.77.1.234:9190);配置 Tracker 时必须同时提供--key,否则 rpc_server.py#L36-L37 会报错Need key to present type of resource when tracker is available |
--key | 空 | 用于在 Tracker 中标识设备类型的 key |
--port-end | 9199 | 端口搜索区间上限 |
--load-library | 空 | 额外加载的库 |
--no-fork | 关闭(默认 fork) | 使用 spawn 模式替代 fork。源码注释特别提示:运行 ROCM/Metal 时 fork 会引发编译器内部错误,应使用--no-fork启动(见 rpc_server.py#L82-L101) |
--custom-addr | 空 | 上报给 Tracker 的自定义 IP 地址(用于 NAT 等场景) |
验证 RPC 系统
RPC 系统搭建完成后,可通过query_rpc_tracker命令验证 Tracker 上所有可用的 RPC Server 及队列状态:
$ python3 -m tvm.exec.query_rpc_tracker --host RPC_TRACKER_IP --port RPC_TRACKER_PORT例如,如果有 3 个通过 RPC Proxy 连接到 Tracker 的 RPC Server,输出类似:
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 ---------------------------------------输出解读:
- Server List:展示当前已注册到 Tracker 的 Server。这里的
server:proxy[RPC_KEY0,RPC_KEY1,RPC_KEY2]表示通过 Proxy 挂载的 3 个 key,其server-address显示为 Proxy 的地址端口(相关逻辑见 python/tvm/rpc/tracker.py#L362-L373 的summary(),以及 Proxy 上报server:proxy[...]信息的 proxy.py#L396-L400); - Queue Status:按 key 统计的资源队列:
total(历史总数)、free(当前空闲数)、pending(等待中的请求数)。本例中RPC_KEY0有 3 个排队请求、无空闲 Server,说明该 key 下的 Server 正在被占用,请求在队列中等待调度。
该命令本身也很灵活:不传--host/--port时,会回退到环境变量TVM_TRACKER_HOST(默认127.0.0.1)与TVM_TRACKER_PORT(默认9190)(见 python/tvm/exec/query_rpc_tracker.py#L34-L39)。
在测试代码中接入 Tracker
验证连通性后,测试代码通常通过tvm.rpc.connect_tracker连接 Tracker,再用tracker.request(key, priority, session_timeout)按 key 获取远程 Session。仓库中的实际用法(如 tests/python/contrib/test_clml/conftest.py#L35-L36):
tracker = rpc.connect_tracker(rpc_tracker_host, rpc_tracker_port) remote = tracker.request(rpc_device_key, priority=0, session_timeout=600)相关 API 定义位于 python/tvm/rpc/client.py#L542(connect_tracker)与 python/tvm/rpc/client.py#L382(request)。而无需 Tracker 的直连场景则直接使用rpc.connect(host, port, key=...)(见 python/tvm/rpc/client.py#L476),例如 tests/python/contrib/test_coreml_runtime.py#L91 中rpc.connect(proxy_host, proxy_port, key=key)的用法。
Troubleshooting:常见启动问题排查
问题 1:设备机器上缺少numpy,导致 RPC Server 无法启动
RPC Server 依赖的某些 Python 文件会import numpy,而消除该 import 关系很困难;对部分设备来说,交叉编译numpy也非常困难。但实际上TVM runtime 本身并不真正依赖 numpy。
因此一个非常简单的 workaround 是创建一个假的numpy:把下面的内容保存为numpy.py,并放到类似/usr/local/lib/python3.8/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该 dummy 模块只定义 RPC 启动路径上会用到的类型与函数,让 Python 的 import 检查通过即可;实际运行时 runtime 不依赖这些实现。
问题 2:设备机器上缺少cloudpickle,导致 RPC Server 无法启动
cloudpickle是一个纯 Python包,因此最简单的解决办法是:从其他机器把它整体拷贝到设备机器的 site-packages 目录(如/usr/local/lib/python3.8/site-packages),即可解决。
小结
搭建一套可用的 Apache TVM RPC 系统,核心要点可以归纳为:
- 三个组件按需组合:RPC Server 必需,Proxy 解决"无法直连"的网络问题,Tracker 提供按 key 的资源队列与多设备管理能力(强烈建议启用);
- 主机端只管调度与开发:Tracker、Proxy 运行在主机,设备端只部署带 xPU 支持的 runtime 与 Python 版 RPC Server(交叉编译时借助 CMake toolchain 文件);
- 验证是上线前的必要一步:用
query_rpc_tracker检查 Server List 与 Queue Status,再用tvm.rpc.connect_tracker(...).request(key, ...)在测试代码中建立远程 Session; - 两个高频坑有低成本解法:缺 numpy 可用 dummy 模块绕过,缺 cloudpickle 直接拷贝纯 Python 包即可。
RPC 让"主机端做任意复杂的预处理/后处理 + 设备端只执行编译产物"成为现实,是 TVM 在真实硬件上做早期端到端评估、以及硬件资源受限环境下多用户共享设备的关键基础设施。更多 RPC 相关的底层实现(协议、客户端、测试封装)可继续阅读 python/tvm/rpc/client.py、python/tvm/rpc/server.py 与 python/tvm/rpc/testing.py。
- 编译器
- 深度学习
- 模型优化
【免费下载链接】tvm
Open deep learning compiler stack for cpu, gpu and specialized accelerators
相关推荐
Apache RocketMQ Proxy 部署指南:Local 与 Cluster 双模式配置与实战
Apache RocketMQ Proxy 部署指南:Local 与 Cluster 双模式配置与实战 RocketMQ Proxy 是 Apache Rock
消息队列后端微服务流处理Apache RocketMQ Proxy 部署指南:Cluster 与 Local 双模式配置与启动实战
Apache RocketMQ Proxy 部署指南:Cluster 与 Local 双模式配置与启动实战 本文是 Apache RocketMQ 中 Prox
消息队列流处理后端Apache TVM RPC 模块全解析:远程设备执行、Tracker 资源调度与 Proxy 中转架构
Apache TVM RPC 模块全解析:远程设备执行、Tracker 资源调度与 Proxy 中转架构 本篇技术指南以 Apache TVM 的 Python
模型编译深度学习推理引擎
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考