TritonServer 框架实践

TritonServer 框架实践

最近,我参与了一个紧急项目,负责将项目中的一个模块利用 TritonServer 框架进行加速,主要通过其 dynamic_batch 特性提升单卡吞吐量。经过一段时间的努力,我在今晚值班时整理了相关经验,与大家分享。

背景介绍

该模块是 embedding 模块,用于将文本表示成浮点数向量,输入为 List[str],输出为 List[List[float]]。服务部署了两种模型,通过输入字段决定使用哪个模型推理。目前线上版本使用 FastAPI 封装,提供 HTTP 接口服务。

由于时间紧迫,且服务协议不能改变,还需要对模型输出进行后处理,而我对 torchonnx 模型不够熟练,因此先直接使用 python backend,后续再研究如何转 tensorrtonnx 进行模型加速。

使用方法

基本用法

首先明确模型的输入输出,然后输出 config.pbtxt 文件。该模型较为简单,配置如下:

name: "model_name"
backend: "python"
input [
  {
    name: "texts"
    data_type: TYPE_STRING
    dims: [ -1 ]
  }
]
output [
  {
    name: "prompt_tokens"
    data_type: TYPE_INT32
    dims: [ 1 ]
  },
  {
    name: "total_tokens"
    data_type: TYPE_INT32
    dims: [ 1 ]
  },
  {
    name: "embedding"
    data_type: TYPE_FP64
    dims: [ -1, 768 ]
  }
]
dynamic_batching {
  preferred_batch_size: [ 4, 8 ]
  max_queue_delay_microseconds: 1000
}

需要注意的是,shape 默认包含第一维度 batch_size。虽然输入 texts 的 shape 是 -1,但输入应为二维,例如:

texts = ["hello", "world"]
data = {
    "id": str(uuid.uuid4()),
    "inputs": [
        {
            "name": "texts",
            "shape": [1, len(texts)],
            "datatype": "BYTES",
            "data": [[texts]],
        }
    ],
}

实际上,将输入改为 [batch_size, 1] 更合理,因为动态尺寸可能导致 Triton 组 batch 时,真实 batch 数失真。但测试发现,使用 [batch_size, 1] 会导致吞吐量下降,推测是因为 Triton 更容易组成小 batch,未能充分发挥 dynamic_batch 特性。

部署时,建议基于 NVIDIA 官方镜像制作服务运行镜像,基于源码编译安装通常不太顺利。

部署参数配置化

由于集群中存在多种 GPU,性能和显存各异,为了充分利用算力资源,需针对不同配置的资源实现不同的部署配置参数。常见的影响算力利用的部署参数包括 instance_groupmax_batch_sizepreferred_batch_size

例如,在 CPU 机器上使用以下配置:

max_batch_size: 8

instance_group [
  {
    count: 1
    kind: KIND_CPU
  }
]

dynamic_batching {
  preferred_batch_size: [ 4, 8 ]
  max_queue_delay_microseconds: 1000
}

在 A10 机器上使用以下配置:

max_batch_size: 8

instance_group [
  {
    count: 2
    kind: KIND_GPU
  }
]

dynamic_batching {
  preferred_batch_size: [ 4, 8 ]
  max_queue_delay_microseconds: 5000
}

这些值需通过压测评估吞吐量和耗时,以确定较优配置。NVIDIA 提供了 perf_analyzer 工具,可通过镜像 nvcr.io/nvidia/tritonserver:<tag>-sdk 获得。

部署时,通过 --model-config-name 指定配置文件名,具体可参考模型配置文档

但在使用过程中,我遇到了 TritonServer 错误:

{"error":"[request id: xxx] expected 0 inputs but got 1 inputs for model 'thenlper_gte-base-zh'. Got input(s) ['texts'], but missing required input(s) ]. Please provide all required input(s)."}

查询模型元数据发现,模型的输入输出为空,配置文件未生效。仔细对比文档后发现,配置文件路径有要求:

  • 每个模型目录下必须有默认的 config.pbtxt
  • 其他配置需放在 ./configs 目录下。

调整配置文件目录后,问题解决。

模型预热

通常,模型需要预热后才能正式上线,否则前几个请求推理较慢。以往我们通过代码预热,但在 TritonServer 中不太方便,且使用 Triton Client 预热会导致线上遥测和监控出现脏数据。TritonServer 自带的模型预热特性解决了这一问题。

具体使用案例可参考预热测试脚本,示例如下:

model_warmup [
  {
    name: "warmup_zero_data"
    batch_size: 1
    inputs: {
      key: "texts"
      value: {
        data_type: TYPE_STRING
        dims: [256]
        zero_data: true
      }
    }
  }
]

与使用 Triton Client 预热相比,模型内部预热更快,且当 instance_group: kind > 1 时,能确保预热到所有实例,避免脏数据。

模型缓存

TritonServer 可根据输入计算哈希值,作为唯一标识缓存响应。以 local 缓存为例,启动时通过 --cache-config local,size=104857600 指定缓存介质,然后在 config.pbtxt 中声明启用缓存:

response_cache {
  enable: true
}

但由于这里使用的是语言模型,缓存命中率较低,因此仅进行了简单调研,未实际应用。

监控

TritonServer 提供了方便的内置监控接口,数据结构可直接接入 Prometheus,详细指南可参考监控文档

其他问题

改造完成后,在独立云主机上测试发现,8 并发下吞吐量提升了 3 倍。但线上部署后,提升仅为 40%。通过控制变量法排除了 CPU 型号和核心数的影响后,经同事建议,尝试比较集群和云主机上的基本矩阵运算性能,发现集群上的矩阵运算速度比云主机慢了 4 倍。

推测可能是 NVIDIA 驱动与 CUDA 不兼容导致的,高版本 CUDA 在低版本驱动上会使用兼容模式。于是,我尝试降低 TritonServer 基础镜像版本至 22.01-py3,将 CUDA 版本降至 11.6,但矩阵计算性能仍较低。随后,我从 SRE 同学那里获取了一台高 NVIDIA 驱动版本的机器,使用 24.06-py3 TritonServer 基础镜像,测试发现矩阵计算性能提高了 4 倍,问题得以解决。后续需协调算力平台人员升级驱动版本以提升 GPU 利用率。

总结

TritonServer 非常适合用于提升线上服务质量,其 dynamic_batch 特性可提升吞吐量,多模型实例可提升算力利用率,模型预热可提升服务稳定性,内置监控则便于开发人员运维。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值