多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

Triton GE Backend 性能调优方法论:面向 NPU 的动态图、静态图与多流并行吞吐优化实战

Triton GE Backend 性能调优方法论:面向 NPU 的动态图、静态图与多流并行吞吐优化实战 Triton GE Backend 性能调优方法论面向 NPU 的动态图、静态图与多流并行吞吐优化实战【免费下载链接】triton-inference-server-ge-backendge-backend基于triton inference server框架实现对接NPU生态快速实现传统CV\NLP等模型的服务化。项目地址: https://gitcode.com/cann/triton-inference-server-ge-backend导读本指南面向使用triton-inference-server-ge-backend下文简称 GE Backend将 ONNX 模型服务化到昇腾 NPU 的开发者系统讲解从能跑到跑得快的完整性能调优路径。文章覆盖小 batch 自动合并、分档/全静态图、锁核、多流并行、自动融合、Float16 精度、ENSEMBLE 流水线等核心手段并结合仓库源码与 CN_CLIP 实战案例给出可复现的配置与预期效果。读完本文你将掌握一套可套用到 CV / NLP 模型的通用调优方法论并能独立使用 Profiling、GE 图 Dump 等工具验证每一步优化是否生效。本文主体内容继承自仓库文档 性能调优方法论并辅以 CN_CLIP模型优化示例、Profiling 工具、DumpGE 工具 与src/源码进行深化。仓库为只读文中所有配置均为查看与运行方式。调优总览先看全景再动手模型性能优化有一整套通用流程不同优化手段的收益来源、适用前提与代价各不相同。先梳理全貌再结合自身模型特征逐项尝试优化手段核心收益适用前提代价/注意小 batch 自动合并Dynamic Batching提高 NPU 利用率、压低空泡高吞吐、多小 batch 并发0 轴为 bs 动态轴、其余轴固定CPU 换 NPU 性能需规划并发度分档模式静态图档位图下沉、消除 CPU 侧动态计算bs 或非 bs 轴可枚举为固定档位dynamicDims 档位过多会拉长编译时间全静态图static_model编译期分配显存、执行下沉 NPU所有输入 shape 完全固定与 dynamic_batching 互斥多流并行 锁核多条 Stream 并发利用 CV 核instance_group.count 1count 过大导致 p99 变大建议 ≤ 8自动融合AutoFuse算子融合减少调度开销特定算子组合场景通过环境变量开启需验证精度Float16 推理显著提升算子执行性能精度可接受必须系统性测试精度ENSEMBLE 流水线消除前/后处理传输 Bound前后处理数据量膨胀明显需 python backend 虚拟环境融合 PASS / 自定义算子替换 GE 图中低效节点性能要求极高的场景开发代价高源码佐证GE Backend 在 model_state.cpp 中维护了npu_ge_config白名单device_ids、device_exec_blocks、instance_exec_blocks、static_model、dump_graph、profiling、dump_data这些配置既可通过模型config.pbtxt的parameters注入也可通过--backend-confignpu_ge,...命令行注入而graph./session./global.前缀的 GE options 则由 SetOptions 解析后统一写入geOption_映射最终在 model_instance_state.cpp 中按graph、session层级传给 GE 构图接口。1. 开启小 batch 自动合并模式Triton Server 支持在高吞吐模式下于用户设置的间隙时间内把多个小 batch 请求合并为大 batch 后再计算从而提高 NPU 利用率。以 CN_CLIP 模型的 image 过程为例在模型config.pbtxt中配置动态 batch 参数dynamic_batching { max_queue_delay_microseconds: 10000 # 等待合并的最大延迟微秒可调整 preferred_batch_size: [4, 8] # 优先合并成这些batch_size可选 }参数含义在 10ms 内若多个 request 可以合并为 4、8 大小的 batch则合并后计算。若所有请求均为 1bs当达到 4bs 时将会执行不会等到 8bs。因此要根据请求情况调整若经过测试 8bs 时吞吐最好则建议preferred_batch_size仅保留8。若请求均为随机 bs则按请求顺序进行拼接达到 4、8 bs 的请求会被合并从而加快计算。理想情况下例如用 8 个并发、每个请求 1bs若 8 个并发恰好在 10ms 内到达 server则被合并成一个 8bs 的推理任务推理完成后再拆分分别响应。前提是处理 8 个 1bs 推理任务比处理 1 个 8bs 任务耗时长才有收益因此填写前需要用户自行测试亲和 batch 大小。⚠️ 注意此模式只支持0 轴为 bs 动态轴、其余轴固定的情况1.1 与实例数、执行流数量的配合由于 8 个请求在理想情况下合并为 1 个推理任务一张 NPU 建议 stream 不大于 8那么最理想情况下 8 个 stream 可处理 8×8 个请求即 64 个并发任务。将config.pbtxt中的count等信息做如下修改instance_group [{ count: 64 } ] parameters: [ { key: device_ids, value: {string_value: 2} } ] parameters: [ { key: device_exec_blocks, value: {string_value: 8} } ]默认情况下count填写 64 会在卡上开相应数量的 stream64 条 stream 显然没必要可通过device_exec_blocks参数限制每张卡上支持的流水数量为 8。在 1 张卡的情况下server 最多支持 64 个请求并发NPU 侧因配置了动态合并参数吞吐率高时会合并成恰好 8 个 stream 可以处理的任务此时达到最优吞吐。源码佐证device_exec_blocks由 ProcessDeviceExecBlocksConfig 从配置中读取std::stoi转为整型存为device_exec_block_也可在模型parameters中通过 ParseParameterValue 解析最终用于控制单卡可并行的执行块数量。实测数据对比仓库文档记录不开启小 batch 动态合并1bs、1 并发Request concurrency: 1 Client: Request count: 3445 Throughput: 191.355 infer/sec Avg latency: 5224 usec (standard deviation 306 usec) p50 latency: 5220 usec p90 latency: 5393 usec p95 latency: 5677 usec p99 latency: 6408 usec Avg HTTP time: 5218 usec (send/recv 110 usec response wait 5108 usec) Server: Request count: 0 Inferences/Second vs. Client Average Batch Latency Concurrency: 1, throughput: 191.355 infer/sec, latency 5224 usec单流 1bs 下性能较差的原因动态图每一节点的 tiling 计算在 CPU 侧计算完成后才能发送至 NPU 执行形成明显 HostBound。开启小 batch 合并1bs、64 并发Request concurrency: 64 Client: Request count: 23940 Throughput: 1329.13 infer/sec Avg latency: 48084 usec (standard deviation 13285 usec) p50 latency: 47319 usec p90 latency: 49055 usec p95 latency: 49707 usec p99 latency: 51560 usec Avg HTTP time: 48075 usec (send/recv 319 usec response wait 47756 usec) Server: Request count: 0 Inferences/Second vs. Client Average Batch Latency Concurrency: 64, throughput: 1329.13 infer/sec, latency 48084 usec吞吐从 191 → 1329 infer/sec提升明显。通过 Profiling 分析采集方法见 Profiling 工具使用可以看出在 64 条请求并发情况下NPU 使用率有明显提升空泡减少明显。该优化也可叠加锁核方式进一步提高 p99 指标。 注意此方法虽吞吐效果明显但在整体考虑资源时需要合理规划。此方法本质是用 CPU 换 NPU 性能目前仅测试单张卡就使用了 64 核若一台服务器有 8 张卡均采用此方式整体性能无法达到 8×单张吞吐。需综合考虑使用场景调整并发度。1.2 使用分档模式小 batch 场景当请求中可以确定bs 最大亲和 bs时如上示例中max_batch_size max[2,4,8]可以尝试通过分档将不同 bs 大小生成静态图从而支持图下沉方式推理进一步提高吞吐。例如preferred_batch_size: [2, 4, 8]理想情况下请求会被合并成 2、4、8 bs 进行推理但在某些时段由于请求数量少无法拼接为 2/4/8 时就可能形成 1、3、5、6、7 bs 长度的请求。所以配置分档时要确保分档能覆盖所有 bs例如用1;2;3;4;5;6;7;8全覆盖。开启方式可通过config.pbtxt或命令行参数生效。如上例子模型有 1 个输入shape 信息为image:[-1,3,224,224]则配置示例为parameters: [ { key: graph.ge.inputShape, value: {string_value: image:-1,3,224,224} } ] parameters: [ { key: graph.ge.dynamicDims, value: {string_value: 1;2;3;4;5;6;7;8} } ] parameters: [ { key: graph.ge.dynamicNodeType, value: {string_value: 1} } ]或通过在命令行后添加参数使能--backend-confignpu_ge,graph.ge.inputShapeimage:-1,3,224,224 \ --backend-confignpu_ge,graph.ge.dynamicDims1;2;3;4;5;6;7;8 \ --backend-confignpu_ge,graph.ge.dynamicNodeType1参数说明graph.ge.inputShape指定输入张量的 shape 描述形如输入名:维1,维2,...-1表示该维为动态档位维。graph.ge.dynamicDims分档档位集合多个档位用;分隔多个输入时各输入的档位用逗号关联详见下节静态图分档场景。graph.ge.dynamicNodeType分档使能标志取1开启。若输入为多个可参考昇腾官方《Ascend Graph 构图接口 options 参数说明》进行详细配置。⚠️ 注意dynamicDims档位数量过多会导致模型编译过程变长。1.3 尝试锁核小 batch 场景小 batch 场景下小模型每条流使用完整核会有较大的启动开销。通过控制每条流使用较小的核数量可以降低启动开销从而进一步提高吞吐率。使能方法同时支持config.pbtxt与命令行参数parameters: [ { key: ge.aicoreNum, value: {string_value: 12|10} } ]或--backend-confignpu_ge,ge.aicoreNum12|10其中12|10代表每条流使用12 个 Cube 核、10 个 Vector 核。不同产品型号昇腾 AI 处理器包含的最大 AICore-CubeCore 与 AICore-VectorCore 数量可从${INSTALL_DIR}/arch-linux/data/platform_config/xxx.ini文件查看。具体值需根据模型本身整体运行占用 CV 核情况进行调整——过大、过小均会影响吞吐可通过调整后做性能测试逐步逼近最优值。2. 动态图转静态图当推理场景不存在动态 batch 场景时可考虑使用静态图进行推理。注意若 bs 始终为 1可优先考虑小 batch 合并优化其吞吐率效果比直接使用本章节的静态图更好。为什么静态图更快ONNX 模型编译完成后默认使用动态图模式。此模式下 input 可能存在动态 shape无法在编译阶段完全计算出每个节点所需显存资源需要根据真实 input 动态计算而计算显存大小的过程在 CPU 侧很可能出现 CPU 侧导致的 HostBound。若 input 可以改为静态 shape则编译阶段可直接计算出每个节点所需显存资源执行过程可下沉至 NPU执行过程中 CPU 不再参与运算从而最大化 NPU 利用率。GE 静态图支持分档场景与全静态场景两种。2.1 分档场景静态图若 input 中非 bs 轴存在动态轴且具体 shape 为固定档位——例如 CN_CLIP 模型 image shape[1,3,-1,1]后两个轴取值只有224,224或336,336——则可通过分档方式将执行图转为静态图parameters: [ { key: graph.ge.inputShape, value: {string_value: image:1,3,-1,-1} } ] parameters: [ { key: graph.ge.dynamicDims, value: {string_value: 224,224;336,336} } ] parameters: [ { key: graph.ge.dynamicNodeType, value: {string_value: 1} } ]或者通过命令行使能--backend-confignpu_ge,graph.ge.inputShapeimage:1,3,-1,-1 \ --backend-confignpu_ge,graph.ge.dynamicDims224,224;336,336 \ --backend-confignpu_ge,graph.ge.dynamicNodeType1此处dynamicDims中224,224;336,336即对inputShape中两个-1维的档位组合进行枚举前维与后维用逗号关联不同档位组用分号分隔。多个输入时的配置方式参考昇腾官方《Ascend Graph 构图接口 options 参数说明》。2.2 全静态场景全静态指模型输入 shape 均为标量完全固定。通过config.pbtxt添加配置parameters: { key: static_model value: {string_value: 1} }或通过启动参数配置--backend-confignpu_ge,static_model1源码佐证static_model属于npu_ge_config白名单ParseCmdlineConfig 与 ParseModelParametersConfig 中在检测到值为1时都会调用SetGeStaticMode(true)开启 GE 静态图模式。参考配置可见 example/resnet/config.pbtxt 中的注释说明——静态图开关与dynamic_batching互斥二者不可同时开启。具体使用哪种分档 or 全静态请自行选择。配置是否生效需要采集 Profiling查看是否所有节点均为 static。采集工具请参考 Profiling 工具使用。关于 shape 配置前提根据 Triton 规范若config.pbtxt中配置了max_batch_size 0则默认 0 轴为 bs 轴配置中无需再写 bs 轴详见 快速入门 附录的 shape 类型支持表。3. 模型结构优化消除冗余计算节点动态图转静态图后某些节点可能导致静态图回退为动态图定位手段就是采集 Profiling。以 CN_CLIP 模型为例用原始 model 直接开启静态图模式后分析 Profiling 结果并结合 Netron判断整图是否可以下沉、哪些节点可以删除。例如优化案例中删除的 Mod 节点目的是让全图下沉。转静态图后某些模型可能存在大片节点变成了固定值但编译器无法自动优化需要人工分析一部分可以通过人工编辑 ONNX 图完成也可以通过辅助工具实现。实践中发现onnxsim可以将一些固定节点向下合并消除如 Mod 这类输入全是固定值的节点从而优化模型。CN_CLIP 通过 onnxsim 工具的优化结果如下可以看出CN_CLIP 经过优化后把 Mod 等固定的计算节点进行了优化比原始图少了很多节点。具体优化过程Netron 定位 Mod 节点、删除固定节点使全图下沉、动态/静态图吞吐对比等详见 CN_CLIP模型优化示例 的动态图转静态图章节。注MindStudio 与 Netron 工具的安装与使用方法见 CN_CLIP模型优化示例附录。4. 多流并行 锁核当用户在config.pbtxt中设置instance_group.count 1时无论动态图模式还是静态图模式均采用多流并行方式执行。建议count值不要过大过大会影响单个推理稳定性造成 p99 变大推荐不要超过 8。动态图模式下CPU 侧需要动态计算每个节点输入的 shape无法实现最大吞吐如果动态图模式下通过调整count无法达到吞吐指标可以尝试转静态图后观察吞吐情况。锁核原理GE 图模式实现了锁核能力即限制某一个 Stream 只使用其中一部分 Vector 核、一部分 Cube 核剩下的资源可以让其他 Stream 使用。算子在启动 core 的过程中使用的 core 越多消耗越大对小模型、特别是小 shape 场景用整个核存在资源浪费。若能合理切分让多条流同时使用 CV 核反而比只让一个 Stream 使用效果更好。常用锁核参数C|V有12|10、7|10等这些均为经验值也可以根据实际模型测试找到最佳锁核值。本框架锁核功能通过如下命令参数开启--backend-confignpu_ge,ge.aicoreNum12|10注芯片型号不同对应的 CV 核数也不一样。CV 核数不能大于本芯片的 CV 核数具体芯片 CV 核数请参考昇腾官方《Ascend Graph 构图接口 options 参数说明》。参考配置见 example/resnet/config.pbtxt 中的ge.aicoreNum注释示例。5. 自动融合AutoFuse自动融合在某些场景下会有性能收益使用方法简单通过环境变量即可激活目前已包含在 CANN 版本中。正式环境变量使用样例1. 功能控制export AUTOFUSE_FLAGS--enable_autofusetrue;--autofuse_disable_passreduce,concat,slice;--autofuse_enable_passtranspose;--autofuse_att_algorithmxx2. DFX 控制export AUTOFUSE_DFX_FLAGS--att_accuracy_level1;--att_profilingtrue;--autofuse_enable_dump_orign_graphtrue--enable_autofusetrue总开关开启自动融合。--autofuse_disable_passreduce,concat,slice禁用指定融合 Pass避免对特定算子类型融合。--autofuse_enable_passtranspose仅对指定 Pass 使能融合。--autofuse_att_algorithmxx指定融合算法。DFX 相关参数用于精度校验att_accuracy_level、融合过程 Profilingatt_profiling以及原始图 Dumpautofuse_enable_dump_orign_graph。具体如何使用请参考昇腾官方《AutoFuse 自动融合》文档。6. 尝试使用 Float16 推理若使用 ONNX 转 OM 进行推理默认 ATC 会使用 Float16 进行优化而当前框架为保证精度默认使用图原始精度origin进行推理。若使用 Float16 推理可显著提高推理性能。使能 Float16 后要系统性测试精度是否达标若精度满足要求则可保留。使能方法支持config.pbtxt与命令行参数parameters: [ { key: session.ge.exec.precision_mode_v2, value: {string_value: fp16} } ]或--backend-confignpu_ge,session.ge.exec.precision_mode_v2fp16源码佐证session.前缀的配置会被 SetOptions 归类到geOption_[session]映射中最终在 model_instance_state.cpp 以session层级 options 传入 GE与graph.前缀参数共同构成完整的 GE 构图 options 体系。详细参数说明参考昇腾官方《Ascend Graph 构图接口 options 参数说明》。7. ENSEMBLE用流水线消除传输 Bound在执行模型推理的过程中可能出现预处理后的数据量大于原始数据量的情况增加数据耗时甚至导致传输 Bound进而影响端到端吞吐率。Triton Inference Server 原生支持 Python backend 与 Ensemble 能力可将多个不同模型后端可以不同串联成一条推理流水线。通过将模型的前后处理封装成 Python model可以降低传输数据量避免传输 Bound 情况的出现。其改造示意如下以 CN_CLIP 为例原始图片输入RGB 彩色图约 147KB转为 Numpy 数组后字节数增至 588KB约 4 倍。通过 Ensemble 将预处理PNG 解码 → resize → 归一化下沉到服务端 Python model 后客户端只需传原始图片即可直接得到最终特征/识别结果。CN_CLIP 实测吞吐从 836.831 infer/sec 提升到 847.627 infer/sec而在 Yolo11 优化案例中前处理将原始 jpg约 500KB放大为[1,3,3600,3600]的 Tensor约 74MB导致传输 Bound改造后吞吐从约 30 qps 提升至约 90 qps提升约 200%。详细数据与完整的 preprocess Python model 代码、ensembleconfig.pbtxt见 CN_CLIP模型优化示例 的 ENSEMBLE 章节。实战提示Python Model 虚拟环境建议使用 conda-pack 打包为xxx.tar.gz并在config.pbtxt中通过EXECUTION_ENV_PATH参数支持$$TRITON_MODEL_DIRECTORY宏表示模型仓路径指定依赖环境Python 版本需与 GE Backend 内置版本一致当前为 3.10打包方法见 CN_CLIP模型优化示例附录。8. 融合 PASS / 自定义算子高阶手段在所有优化手段都使用后如果性能仍不达标就需要具体分析耗时长算子或者看哪些算子能融合。这类方法的代价较高打开具体的模型图分析哪些算子执行过程可以融合通过编写融合算子以及融合 Pass替换 GE 图中某些节点从而优化整网性能。此类方法一般在性能要求比较高的模型中进行或者因当前 NPU 存在短板例如对 int64 支持不佳等场景需要手工编写新的算子进行全局替换进行优化。相关文档可参考昇腾官方《自定义 Pass 开发》。进行此类分析时通常需要 Dump GE 图查看各阶段图结构可参考 DumpGE 工具使用——框架已集成 GE 图 Dump 功能通过config.pbtxt中dump_graph: 1或--backend-confignpu_ge,dump_graph1开启默认在工作目录生成dump_graph文件夹按{主进程号}_{卡号}生成不同阶段的 GE 图可用 Netron 打开 pbtxt 文件分析。实战案例CN_CLIP 全流程优化以上方法论在仓库中以 CN_CLIP模型优化示例 完整落地覆盖模型导出 → 动态图转静态图 → 模型优化 → 多流并行锁核 → ENSEMBLE全流程关键结论如下模型导出修改model.py的 forward 返回值去掉无 shape 的logit_scale.exp()用torch.onnx.export导出导出动态图时设置dynamic_axes导出静态图时不设置该参数。动态图转静态图动态图下 Profiling 可见大量空泡HostBound通过定位op_summary_xxx.csv发现 Mod 算子导致图下沉截断结合 Netron 确认其为固定值计算删除 Mod 节点后全图下沉为 static单 Instance 静态图吞吐约为动态图的2.6 倍。多流并行 锁核通过--backend-confignpu_ge,ge.aicoreNum12|10限制每条流 12 个 Cube、10 个 Vector8 流并行情况下整体吞吐又提升约35%。ENSEMBLE删去 text 分支固定 batch1 后构建 preprocess Python model 与clip_ensemble调度配置吞吐从 836.831 提升至 847.627 infer/sec数据量变化不大时收益有限对前后处理数据膨胀明显的模型收益巨大。案例中涉及的性能指标均为仓库文档记录的实测数据实际效果会随模型、硬件型号、并发与精度配置变化请以自测为准。配套工具与验证闭环性能调优不是配完即止每一步优化都需要可验证的观测手段形成闭环工具用途开启方式文档Profiling采集算子执行情况、判断是否 static、定位 HostBound 与空泡config.pbtxt中profiling: true/dynamic或--backend-confignpu_ge,profilingdynamic数据用msprof --exporton --output./导出Profiling 工具使用DumpGEDump 各阶段 GE 图分析图下沉与节点优化空间config.pbtxt中dump_graph: 1或--backend-confignpu_ge,dump_graph1DumpGE 工具使用Netron / MindStudio Insight查看 ONNX/GE 图结构、分析 Profiling 结果下载安装后打开对应文件CN_CLIP模型优化示例附录onnxsim常量折叠、合并固定节点pip install onnxsim onnxsim 旧模型 新模型快速入门源码佐证框架的 Profiling 集成位于 SetProfiling通过设置PROFILING_MODE与PROFILING_OPTIONS环境变量含training_trace、task_trace、aicpu、aic_metricsPipeUtilization、runtime_api等选项控制 msprof 采集并在日志中打印主进程 PID 供动态采集连接使用。Profiling 的两种模式中true为普通采集启动即采集进程退出后整理打包dynamic为动态采集msprof 待命通过msprof --dynamicon ... --pid{主进程ID}连接主进程用start/stop/quit控制采集节奏。此外example/resnet/config.pbtxt 提供了一份带完整注释的参考配置集中展示了dynamic_batching、static_model、profiling、ge.aicoreNum、device_ids等参数的正确写法与互斥关系静态图与动态 batch 合并互斥可作为新模型调优的起点模板。结语性能调优是一个测量 → 配置 → 再测量的迭代过程先通过 Profiling 建立基线明确瓶颈是 HostBound、传输 Bound 还是 NPU 利用率不足再按本文顺序逐项尝试小 batch 合并、分档/静态图、锁核、多流并行、自动融合、Float16 与 ENSEMBLE最后用 Profiling 验证每一步是否真正生效。对于追求极致性能的模型还可借助 GE 图 Dump 与融合 Pass 做更深度的算子级优化。将这套方法论套用到自己的 CV/NLP 模型上即可系统性地把服务化吞吐调整至最优。【免费下载链接】triton-inference-server-ge-backendge-backend基于triton inference server框架实现对接NPU生态快速实现传统CV\NLP等模型的服务化。项目地址: https://gitcode.com/cann/triton-inference-server-ge-backend创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表