前言

NVIDIA的Triton Inference Server是推理服务的事实标准——做模型服务化、多模型调度、动态batching,用Triton准没错。但Triton原生只支持NVIDIA GPU(CUDA),昇腾NPU想接入Triton生态,中间需要一个"翻译层"。

triton-inference-server-ge-backend就是这层翻译。它把Triton的请求调度和CANN的GE(Graph Engine)图执行引擎对接起来,让Triton能调度昇腾NPU上的模型推理,和调度GPU模型一样简单。

这篇会从设计理念讲起,拆解GE Backend的三层架构,最后搭一个完整的Triton+昇腾NPU推理服务。

设计理念:Triton不跑CUDA,跑GE

Triton的标准推理流程是这样的:

HTTP/gRPC请求 → Triton调度 → CUDA Backend → GPU执行 → 返回结果

昇腾NPU没有CUDA,但有GE——图编译引擎。GE把模型编译成NPU可执行的离线模型(.om),然后通过CANN Runtime在NPU上执行。所以思路很自然:把CUDA Backend换成GE Backend:

HTTP/gRPC请求 → Triton调度 → GE Backend → CANN Runtime → NPU执行 → 返回结果

GE Backend的核心设计理念是对Triton透明——Triton不关心后端是CUDA还是GE,只要Backend实现标准的TRITONBACKEND_ModelInstanceExecute接口就行。GE Backend就是这个接口的昇腾实现。

三层架构拆解

GE Backend分三层,每层职责清晰:

第1层:Triton C API接口层

这层是GE Backend的"门面",负责和Triton核心框架通信:

  • ModelInstance:对应Triton的一个模型实例,管理模型的加载/卸载/执行生命周期
  • 请求队列:接收Triton调度过来的推理请求,按batch组织
  • 响应回传:推理完成后,把结果返回给Triton,Triton再返回给客户端

关键接口:

// Triton Backend标准接口——GE Backend要实现这三个核心函数
TRITONSERVER_Error* TRITONBACKEND_ModelInstanceExecute(
    TRITONBACKEND_ModelInstance* instance,
    TRITONBACKEND_Request** requests,
    uint32_t request_count);

第2层:GE Backend适配层

这层是核心的"翻译"逻辑,把Triton的Tensor格式转成CANN的格式:

  • Tensor转换:Triton的Tensor(CPU/GPU内存)→ CANN的aclTensor(NPU内存)
  • 内存管理:管理NPU显存的分配和释放,支持zero-copy(如果输入已在NPU上)
  • Batch合并:把多个请求的输入合并成一个batch,提高NPU利用率

第3层:CANN Runtime执行层

这层直接调用CANN的AscendCL API执行推理:

  • 模型加载:用aclmdlLoadFromFile加载.om离线模型
  • 推理执行:用aclmdlExecute执行模型
  • 结果获取:从NPU取回推理结果

数据流转全链路

一个完整的推理请求,从客户端到NPU再返回,经过以下步骤:

1. 客户端发HTTP/gRPC请求
   → POST /v2/models/resnet/infer
   → body: {"inputs": [{"name": "input", "data": [...]}]}

2. Triton核心收到请求,路由到GE Backend
   → 根据model配置找到backend类型="ge"
   → 调用ModelInstanceExecute()

3. GE Backend适配层做Tensor转换
   → 解析请求中的input tensor
   → 分配NPU显存,用aclrtMemcpy把数据从CPU搬到NPU

4. CANN Runtime执行推理
   → aclmdlExecute()执行.om模型
   → 输出tensor在NPU显存中

5. GE Backend适配层做结果转换
   → 用aclrtMemcpy把输出从NPU搬回CPU
   → 封装为Triton的Response对象

6. Triton返回结果给客户端
   → {"outputs": [{"name": "output", "data": [...]}]}

代码实战:搭建Triton+昇腾NPU推理服务

步骤1:模型准备(ONNX → .om)

# 先用ATC把ONNX转为CANN离线模型
atc \
    --model=resnet50.onnx \
    --framework=5 \
    --output=/models/resnet50/1/model \
    --soc_version=Ascend910 \
    --input_shape="input:1,3,224,224" \
    --output_type=FP16

# 模型目录结构(Triton要求的格式)
# /models/resnet50/
#   ├── config.pbtxt        # 模型配置
#   └── 1/
#       └── model            # .om文件(不带扩展名)

步骤2:Triton模型配置

# config.pbtxt - Triton模型配置文件
name: "resnet50"
platform: "ge_backend"          # 指定GE Backend
max_batch_size: 32

input [
  {
    name: "input"               # 要和ATC转换时的input_names一致
    data_type: TYPE_FP16
    dims: [3, 224, 224]         # NCHW格式,不含batch维度
  }
]

output [
  {
    name: "output"              # 要和ATC转换时的output_names一致
    data_type: TYPE_FP16
    dims: [1000]                # 1000类分类
  }
]

# GE Backend特有配置
backend_parameters {
  key: "soc_version"
  value: { string_value: "Ascend910" }
}

instance_group [
  {
    count: 1                    # 1个模型实例
    kind: KIND_NPU              # 运行在NPU上
  }
]

dynamic_batching {
  preferred_batch_size: [8, 16, 32]  # 动态batching
  max_queue_delay_microseconds: 1000  # 最多等1ms凑batch
}

代码讲解platform: "ge_backend"是关键配置,告诉Triton用GE Backend而非CUDA Backend。instance_groupkind: KIND_NPU指定实例运行在NPU上。dynamic_batching配置让Triton自动把并发请求凑成batch,提高吞吐——比如8个并发请求各自发1张图,Triton会自动凑成batch=8一次推理,比逐个推理快3-4倍。

步骤3:启动Triton+GE Backend

# 启动Triton Server,加载GE Backend
tritonserver \
    --model-repository=/models \
    --backend-dir=/usr/local/triton/backends \
    --log-verbose=1

# 预期日志输出
# I0525 10:00:00 model_repository_manager.cc:XXX] loading: resnet50
# I0525 10:00:01 ge_backend.cc:XXX] Loading .om model from /models/resnet50/1/model
# I0525 10:00:02 ge_backend.cc:XXX] Model loaded, device=0
# I0525 10:00:02 server.cc:XXX] Triton server is ready

步骤4:客户端调用

import tritonclient.http as http_client
import numpy as np

# 创建Triton客户端
client = http_client.InferenceServerClient(url="localhost:8000")

# 准备输入
input_data = np.random.randn(1, 3, 224, 224).astype(np.float16)

# 构造推理请求
inputs = [http_client.InferInput("input", [1, 3, 224, 224], "FP16")]
inputs[0].set_data_from_numpy(input_data)

outputs = [http_client.InferRequestedOutput("output")]

# 执行推理
result = client.infer("resnet50", inputs, outputs)
output = result.as_numpy("output")
print(f"分类结果: Top1 = {np.argmax(output)}")

踩坑实录

坑1:模型没ATC转换,直接放ONNX进Triton

现象:Triton启动时报错Failed to load model: model file is not .om format

原因:GE Backend只认.om离线模型,不支持ONNX/PyTorch等框架格式。CUDA Backend能直接跑ONNX(通过TensorRT),但GE Backend没有这个能力。

解决:先用ATC转换为.om,再放进模型仓库。

# 错误:直接放ONNX
cp resnet50.onnx /models/resnet50/1/model  # 报错

# 正确:ATC转换后再放
atc --model=resnet50.onnx --framework=5 --output=resnet50 --soc_version=Ascend910
cp resnet50.om /models/resnet50/1/model

坑2:多模型实例分配到同一张NPU卡,显存溢出

现象:同时部署3个模型,每个模型1个实例,都分配到NPU:0,总显存超限。

原因:默认情况下所有实例都分配到device 0,没有自动负载均衡。

解决:在config.pbtxt里手动指定不同实例的device。

# 错误:3个模型都跑在NPU:0
instance_group [{ count: 1, kind: KIND_NPU }]

# 正确:指定不同device
instance_group [
  { count: 1, kind: KIND_NPU, device: 0 }  # 模型1在NPU:0
]

# 另一个模型的配置
instance_group [
  { count: 1, kind: KIND_NPU, device: 1 }  # 模型2在NPU:1
]

坑3:动态batching时输入shape不一致

现象:两个请求分别发batch=1和batch=4,Triton合并batch时报错。

原因:.om模型编译时input_shape是固定的(如1,3,224,224),动态batch要求模型支持可变batch维度。

解决:ATC转换时指定动态batch范围。

# 错误:固定batch=1
atc --input_shape="input:1,3,224,224"

# 正确:支持batch 1-32
atc --input_shape="input:-1,3,224,224" --dynamic_dims="1,2,4,8,16,32"

性能对比数据

测试环境:Ascend 910 × 2,Triton 2.40,CANN 8.0。

配置 QPS P99延迟(ms) 吞吐提升
单请求串行(无batching) 45 22 1x
动态batching(max_batch=8) 180 45 4x
动态batching(max_batch=32) 520 62 11.5x
2卡并行(各1实例) 980 65 21.8x

结尾

triton-inference-server-ge-backend是NVIDIA Triton对接昇腾NPU的桥梁,住在第3层编译层,用三层架构(Triton接口层→GE适配层→CANN Runtime层)实现了Triton调度和昇腾NPU执行的透明对接。

如果在昇腾NPU上做推理服务化,强烈建议用Triton+GE Backend。实测下来,动态batching+2卡并行,QPS比单请求串行高21倍

昇腾CANN的推理服务生态还在持续完善。如果在用的过程中遇到啥问题,欢迎去AtomGit上的昇腾CANN开源社区逛逛,里面有一手资料和活跃社区。

社区链接

https://atomgit.com/cann/triton-inference-server-ge-backend

Logo

鲲鹏昇腾开发者社区是面向全社会开放的“联接全球计算开发者,聚合华为+生态”的社区,内容涵盖鲲鹏、昇腾资源,帮助开发者快速获取所需的知识、经验、软件、工具、算力,支撑开发者易学、好用、成功,成为核心开发者。

更多推荐