Skip to content

alex-spacemit/InferBridge

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

InferBridge

通过 TCP 将模型推理请求转发到远端硬件加速后端(SpacemiT ONNXRuntime / RKNN / ONNX Runtime)的轻量级推理代理。


1. 特性

  • 跨平台客户端:Python / C++ 客户端均兼容 x86_64 / ARM / RISC-V
  • 异构推理后端:支持 RK3566/3588 RKNN NPU、SpacemiT K3 EP、通用 ONNX Runtime
  • 轻量级协议:msgpack + 自定义二进制帧,单连接全双工
  • 模型缓存去重:客户端自动计算 MD5,服务端缓存已上传模型,避免重复传输(启动时扫描 .cache/,运行时动态维护)
  • 零依赖部署:Server 仅依赖 msgpack-c + 对应 backend SDK,无需额外运行时

2. 架构

Client (Python / C++)  ──TCP──▶  inferbridge-server (C++)  ──▶  Backend
                                                                  ├── SpacemiT ORT (SpacemiT K3)
                                                                  ├── RKNN (RK3568/3588)
                                                                  └── ORT  (标准 ONNX Runtime)
组件 路径 说明
Server server/ C++17 TCP 服务,接收帧、调度后端推理
SpacemiT ORT 后端 server/src/backend/spacemit_ort_backend.cpp 适配 SpacemiT K3 EP + ONNX Runtime
RKNN 后端 server/src/backend/rknn_backend.cpp 适配 RK3568/3588 NPU
ORT 后端 server/src/backend/ort_backend.cpp 标准 ONNX Runtime(CPU/CUDA)
Python 客户端 client/python/ inferbridge 包,API 仿 onnxruntime
C++ 客户端 client/cpp/ 头文件库,include/inferbridge.h
示例 examples/ Python 和 C++ 使用示例

3. 协议

所有通信使用自定义二进制帧封装 msgpack payload。

3.1 帧格式

┌─────────┬────────┬──────────────┐
│  Magic  │  Type  │   Length     │
│  4 byte │ 1 byte │  4 byte BE   │
├─────────┴────────┴──────────────┤
│      Msgpack Payload (N bytes)   │
└──────────────────────────────────┘
字节偏移 长度 内容
0–3 4 Magic: IBRG (0x49 0x42 0x52 0x47)
4 1 MsgType(见下表)
5–8 4 payload 长度(大端 uint32)
9+ N msgpack 编码的 payload

3.2 消息类型

类型 方向 主要 payload 字段
CACHE_LIST_REQ 0x07 C→S {} (空 map)
CACHE_LIST_RESP 0x08 S→C md5s (string[])
LOAD_MODEL_REQ 0x01 C→S md5 (string), model_data (bin, 可选), backend (string), session_options (map)
LOAD_MODEL_RESP 0x02 S→C inputs (TensorMeta[]), outputs (TensorMeta[])
INFER_REQ 0x03 C→S inputs (name→ndarray map)
INFER_RESP 0x04 S→C outputs (ndarray[])
UNLOAD_REQ 0x05 C→S {} (空 map)
UNLOAD_RESP 0x06 S→C {} (空 map)
ERROR_RESP 0xFF S→C message (string)

模型缓存去重流程

  1. 客户端连接后先发送 CACHE_LIST_REQ,服务端返回已缓存的模型 MD5 集合
  2. 客户端计算本地模型文件的 MD5
  3. 若 MD5 已在缓存中 → LOAD_MODEL_REQ 只发 md5 字段(无 model_data
  4. 否则 → 发送完整 model_data 二进制,服务端计算 MD5 后存入 .cache/<md5>.onnx 并加入缓存集合

多次加载同一模型时,第二次起会自动跳过文件传输。

3.3 ndarray 在 msgpack 中的格式

编码为 map,字段:

{
  "__ndarray__": true,
  "dtype": "float32",     # numpy dtype 字符串
  "shape": [1, 3, 640, 640],
  "data": b'\x00\x01...'  # msgpack bin
}

4. 快速开始

4.1 依赖

Server(编译时)

  • CMake 3.16+
  • g++ / clang(C++17)

Python 客户端

pip install msgpack numpy
pip install ./client/python

4.2 编译 Server

SpacemiT K3 后端(交叉编译 riscv64)

bash scripts/build_server.sh --spacemit-ort ${path-to-spacemit-ort} --build-dir build/server/spacemit-ort

RKNN 后端( 交叉编译 RK3568/3588)

# download aarch64 gcc
wget https://armkeil.blob.core.windows.net/developer/files/downloads/gnu/15.2.rel1/binrel/arm-gnu-toolchain-15.2.rel1-x86_64-aarch64-none-linux-gnu.tar.xz

tar -xf arm-gnu-toolchain-15.2.rel1-x86_64-aarch64-none-linux-gnu.tar.xz
# download rknn sdk first
# https://console.zbox.filez.com/l/I00fc3
# https://github.com/airockchip/rknn-toolkit2
bash scripts/build_server.sh --rknn ${path-to-rknn-sdk}  --build-dir build/server/rknn

标准 ONNX Runtime 后端(x86_64 / ARM)

# 1. 下载 ONNX Runtime(或使用已有版本)
wget https://github.com/microsoft/onnxruntime/releases/download/v1.27.0/onnxruntime-linux-x64-1.27.0.tgz
tar xf onnxruntime-linux-x64-1.27.0.tgz

# 2. 编译
./scripts/build_server.sh --ort ${path-to-ort} --build-dir build/server/ort

4.3 启动 Server

# 方式 1:使用脚本(默认 0.0.0.0:50051)
bash scripts/run_server.sh

# 方式 2:直接运行二进制
./build/inferbridge-server --host 0.0.0.0 --port 50051

# 启动日志示例:
# [inferbridge] cache: 3 model(s) pre-loaded from .cache/
# [inferbridge] listening on 0.0.0.0:50051

服务端会在首次启动时自动扫描 .cache/ 目录下已有的 *.onnx 模型(文件名即 MD5),后续客户端上传的模型也会按 MD5 缓存到该目录。

4.4 Python 客户端示例

标准 ONNX Runtime backend

import inferbridge as ib
import numpy as np

sess = ib.InferenceSession(
    "/path/to/model.onnx",
    providers=["InferBridge"],
    provider_options=[{
        "host": "localhost",
        "port": 50051,
        "session_options": {
            "backend": "ort",
            "intra_op_num_threads": "4",  # 0=auto
            "inter_op_num_threads": "1",  # 0=auto
        }
    }]
)

inp = sess.get_inputs()[0]
dummy = np.zeros([1, 3, 224, 224], dtype=np.float32)
outputs = sess.run(None, {inp.name: dummy})
print(outputs[0].shape, outputs[0].dtype)

SpacemiT ORT backend

import inferbridge as ib
import numpy as np

# SpacemiT ORT backend
sess = ib.InferenceSession(
    "/path/to/resnet50.onnx",
    providers=["InferBridge"],
    provider_options=[{
        "host": "192.168.1.100",  # K3 板 IP
        "port": 50051,
        "session_options": {
            "backend": "spacemit_ort",
            "intra_op_num_threads": "1",
            "USE_SPACEMIT_EP": "1",
            "SPACEMIT_EP_INTRA_THREAD_NUM": "4",
        }
    }]
)

# 首次加载会上传模型,再次加载同一模型会自动跳过传输
inp = sess.get_inputs()[0]
dummy = np.zeros([1, 3, 224, 224], dtype=np.float32)
outputs = sess.run(None, {inp.name: dummy})
print(outputs[0].shape, outputs[0].dtype)

4.5 C++ 客户端示例

#include "inferbridge.h"

inferbridge::ProviderOptions opts;
opts.host = "192.168.1.100";
opts.port = 50051;
opts.session_options["backend"] = "spacemit_ort";
opts.session_options["intra_op_num_threads"] = "1";
opts.session_options["USE_SPACEMIT_EP"] = "1";

inferbridge::InferenceSession sess("/path/to/model.onnx", {"InferBridge"}, {opts});
auto outputs = sess.run({}, {{inp.name, tensor}});

5. Backend 选项

通过 session_options 传入,键值均为字符串。

5.1 RKNN Backend

字段 说明 示例
target_platform 目标平台 rk3566 / rk3568 / rk3588
core_mask NPU 核心掩码(位运算) "7" (三核全开) / "1" (单核)

5.2 ORT Backend(标准 ONNX Runtime)

字段 说明 示例
backend 后端名称(固定) "ort"
intra_op_num_threads 算子内并行线程数 "4" (0=auto)
inter_op_num_threads 算子间并行线程数 "1" (0=auto)

5.3 SpacemiT ORT Backend

字段 说明 示例
backend 后端名称(固定) "spacemit_ort"
intra_op_num_threads 算子内并行线程数 "1"
USE_SPACEMIT_EP 启用 SpacemiT EP "1"
SPACEMIT_EP_INTRA_THREAD_NUM EP 内部线程数 "4"

6. 示例脚本

路径 说明
examples/python/ort_infer.py 标准 ONNX Runtime 推理示例(支持 --shape 回退)
examples/python/spacemit_ort_infer.py SpacemiT K3 专用示例
examples/python/rknn_infer.py RKNN NPU 推理示例(RK3566/3568/3588,已在 RK3588 实测)
examples/python/yolo_infer.py YOLO 检测示例(含前后处理)
examples/cpp/yolo_infer.cpp C++ YOLO 端到端示例

运行示例

# Python(第二次加载会跳过模型传输)
cd examples/python
python ort_infer.py /share/models/resnet50.onnx --host 10.3.91.75 --shape 1,3,224,224

# C++
cd examples/cpp
cmake -B build
cmake --build build
./build/yolo_infer /path/to/yolo.onnx /path/to/image.jpg

7. 故障排查

7.1 客户端报错 TypeError: data type 'unknown' not understood

原因:服务端返回的输出 dtype 未被识别(通常因 ONNX Runtime 版本不匹配或量化模型 type info 损坏)

解决

  • 检查服务端日志中的 output dtype=unknown(N),记下类型 ID N
  • server/src/backend/spacemit_ort_backend.cpport_type_to_str 中补充该类型映射
  • 重新编译部署

7.2 客户端报错 Invalid rank for input: Got: 0 Expected: 4

原因:量化模型的 input shape 在 graph 中未声明,客户端构造了 rank-0 标量

解决

  • 使用 --shape 1,3,224,224 显式指定输入形状(见 examples/python/ort_infer.py
  • 或用 onnx.shape_inference 工具修复模型的 value_info

7.3 服务端日志 error -> connection closed

正常行为:客户端断开时会打印该日志,但在最新版本中已改为仅在真正错误时打印,正常断开只显示 client disconnected fd=N

7.4 模型重复传输、缓存未生效

检查

  1. 服务端启动日志中是否有 cache: N model(s) pre-loaded
  2. .cache/ 目录是否存在且可写
  3. 两次加载的模型文件 MD5 是否完全一致(可用 md5sum 验证)

8. 日志示例

服务端启动与首次加载

[inferbridge] cache: 2 model(s) pre-loaded from .cache/
[inferbridge] listening on 0.0.0.0:50051
[inferbridge] client connected fd=9
[inferbridge] load_model fd=9 backend=spacemit_ort size=26053357
[inferbridge]   input  name=input_0 dtype=float32 shape=[1,3,224,224]
[inferbridge]   output name=output_0 dtype=float32 shape=[1,1000]
[inferbridge] model loaded -> .cache/b658383597ba8589d25ea8dd3df7e93d.onnx
[inferbridge] infer  fd=9 input[0] name=input_0 dtype=float32 shape=[1,3,224,224]
[inferbridge] infer  fd=9 done, 1 output(s)
[inferbridge] model unloaded fd=9
[inferbridge] client disconnected fd=9

第二次加载(命中缓存)

[inferbridge] client connected fd=10
[inferbridge] load_model fd=10 backend=spacemit_ort md5=b658383597ba8589d25ea8dd3df7e93d (cached)
[inferbridge]   input  name=input_0 dtype=float32 shape=[1,3,224,224]
[inferbridge]   output name=output_0 dtype=float32 shape=[1,1000]
[inferbridge] model loaded -> .cache/b658383597ba8589d25ea8dd3df7e93d.onnx
[inferbridge] infer  fd=10 input[0] name=input_0 dtype=float32 shape=[1,3,224,224]
[inferbridge] infer  fd=10 done, 1 output(s)

注意第二次加载没有 size=...(未传输模型数据),直接显示 (cached)


9. 开发与贡献

9.1 添加新的 Backend

  1. server/src/backend/ 创建 xxx_backend.{h,cpp}
  2. 继承 BackendBase 并实现 load() / infer() / unload()
  3. backend/backend.cppcreate_backend() 中注册
  4. 更新 server/CMakeLists.txt 添加编译选项 -DWITH_XXX=ON

9.2 协议扩展

若需新增消息类型(如批量推理、异步通知):

  1. protocol.h 中添加 MsgType 枚举值
  2. server.cpphandle_client() 中实现处理逻辑
  3. 同步更新客户端 session.py / session.cpp 中的常量定义

10. License

MIT License. See LICENSE for details.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors