全局性能调优器是NVIDIA TensorRT 11.1中发布的自动性能调优功能。它通过为不同模型定制不同的优化方案,在TensorRT中实现了自动化、精度感知且端到端的性能调优。更多详情请参阅本系列第一部分,该部分介绍了调度(深度学习编译器优化中的关键阶段)相关的挑战、TensorRT中开发全局性能调优器功能的动机,以及为形式化该问题而建立的数学理论。
本文作为第二部分,沿用第一部分中的符号体系,描述全局性能调优器功能的算法与架构设计,并提供使用该功能的最佳实践。
TensorRT中全局性能调优器功能的设计
全局性能调优器的核心设计理念是将可替代的调度策略表示为显式且可搜索的构建路径。编译器开关(也称为编译器标志或编译器选项)是一种配置设置,用于改变编译流程的某个方面,例如启发式策略、融合决策或代码生成路径。
每个可调开关都暴露出有限的取值集合,并具有一个默认值。构建路径是开关取值的具体组合。当未指定构建路径时,TensorRT使用默认构建路径,此时每个开关都保持其默认值。
假设有一个由三个处理阶段\(f_1\)、\(f_2\)、\(f_3\)组成的编译器。假设一个二值开关\(p\)控制\(f_2\)所使用的融合策略:当\(p=1\)时,该阶段应用水平融合;当\(p=0\)时,应用垂直融合:
\(f_2(s) = \begin{cases} f'_2(s), & \text{若 } p = 1 \ f''_2(s), & \text{若 } p = 0 \end{cases}\)
\(p\)的两个可能取值定义了两条构建路径,因此针对输入模型\(m\)产生了一个大小为2的候选解集:
\(B(m) = \{f_3(f'_2(f_1(m))),\, f_3(f''_2(f_1(m)))\}\)
两种融合策略都不是普遍更优的。水平融合可能为某个模型生成更快的引擎,而垂直融合则可能对另一个模型更有效。即便模型本身不变,结果也可能随张量形状和目标GPU的变化而变化。因此,选择更优路径需要构建两个候选引擎,并比较它们的端到端性能。
即使是这个双路径的简单示例,也捕捉到了调优的核心思想:编译器暴露出有意义的调度替代方案,而实测行为决定了哪个完整方案在当前部署场景下表现最佳。
在前文介绍的数学框架中,构建路径是编译器各阶段的组合。而在实现层面,开关赋值的组合为该路径提供了一个简洁的规范。改变一个或多个开关的取值,会改变模型在编译流程可调部分中的流转方式。在这个抽象层面上,一个开关组合可以被视为标识了一条构建路径。
更准确地说,开关组合是一种构建路径规范,而非对最终生成引擎的完整描述。TensorRT仍会执行引擎构建的其余阶段,包括策略选择,并可能根据网络所施加的约束来协调各个开关取值。路径规定的是在可调配置空间内所请求的选择,并不能取代TensorRT构建器的其他部分。
TensorRT围绕两项基本能力暴露出一套简洁的API接口:
应用并检查单一构建路径:一个设置方法用于向构建器配置应用固定路径,相应的获取方法则返回当前指定的路径。
探索可调配置空间:另一个获取方法用于描述可用的开关、其允许取值、默认值及用途。
对应的C++ API如下:
bool IBuilderConfig::setBuildRoute(char const* buildRoute); char const* IBuilderConfig::getBuildRoute() const; char const* IBuilderConfig::getAllBuildRoutes() const;
开发者可以利用这些API实现自定义调优器。trtexec中也提供了一个C++实现示例。
从总体上看,trtexec的调优组织方式如下:
当用户指定输入模型以及类似正则表达式的可调构建路径表达式时,trtexec会依据搜索算法将该表达式展开为一系列具体的构建路径。
随后会有一个循环遍历所有固定构建路径。在每次迭代中,模型会以该单一构建路径进行构建,然后在每次迭代中测量精度和性能。
在构建过程中崩溃或在推理阶段精度不达标的所有构建路径都会被排除在最佳引擎选择之外。最终会选出实测端到端性能最优的引擎。
TensorRT 11.1提供了200多个可用于调优的开关。更多详情请参阅全局性能调优器文档。Python版本的相同实现将在Torch-TensorRT 2.15中发布。
探索多条构建路径的实际价值可以在端到端测量结果中体现出来。图1比较了默认TensorRT构建路径(每个工作负载均归一化为1.0倍)与调优过程中发现的最佳合格路径。在这十个评估的工作负载中,调优带来了1.14倍到1.63倍的性能提升。
各工作负载之间的差异正是这一结果的关键所在。不同模型展现出不同的优化机会,因此不能期望单一的默认调度策略对每个模型都能实现最佳性能。全局性能调优器会探索不同的构建路径,排除不满足精度要求的候选方案,并在目标环境中选出实测速度最快的合格引擎。
案例研究:性能与精度的纠缠问题
全局性能调优器功能应用了精度感知调优,因此性能与精度纠缠的问题理论上可以得到完美解决。来看以下思想实验。
对于如图2所示的一个简单神经网络模型,假设存在一些开关,用于控制是否强制某些中间表示(IR)使用低精度内核,这相当于通过在算子之间插入Q/DQ节点,将计算图转换为量化图。TensorRT 11.0及后续版本不再支持此做法,因为弱类型API已被弃用。
该模型有四处可以插入Q/DQ节点。假设这些位置由四个不同的二值开关控制。深度学习编译器便能提供2的4次方(即16种)解决方案组成的解空间。显而易见,在这16个候选方案中,性能最佳的方案是插入了全部量化操作的方案。然而,该方案可能无法满足精度要求。不插入任何Q/DQ的方案精度最佳,但性能最差。性能与精度相互纠缠,使得人们难以判断在16个候选方案中间的哪个方案能够同时满足两方面要求。
回顾一下,构建器算法这一概念中包含一个精度容差。解开精度与性能纠缠的关键,是定义一个清晰的精度可接受边界,而该边界需要以用户提供的精度指标和数据集作为基准值作为支撑。
TensorRT中的全局性能调优器功能也设有一个精度容差阈值\(ε\)。考虑以下三种情形:
\(ε = 0\):在本思想实验所假设的精确匹配指标下,候选输出必须与参考输出逐位完全一致。如果每一次低精度替换都至少改变了一个被评估的输出,那么只有未进行任何低精度替换的那一个方案仍符合资格。
\(ε = +\infty\):精度要求不施加任何实际限制,因此全部16个候选方案均符合资格。更一般地说,设想理论解空间中存在一个"空"方案,它不执行任何有用的计算,因此运行时间为零。一个仅以性能为导向的优化器可能会选择这个毫无意义的方案。这一极限情形说明了为何性能本身不能作为充分的优化目标。
\(ε\)从\(+\infty\)逐渐变化到0:随着精度要求逐渐趋严,符合资格的候选集合会不断收缩——例如,从16个方案,依次缩减到14个、13个、8个、7个,最终缩减到1个。这些数字仅作示意,实际的转变节点取决于具体模型、参考数据以及所选的精度指标。在\(ε\)的每个取值下,选择算法都会在仍符合资格的已评估候选方案中,选出实测速度最快的方案。
全局性能调优器并未消除这种底层的权衡关系,而是在优化过程中将性能与精度的角色分离开来:
精度决定哪些候选方案是可接受的
性能则对这些可接受的候选方案进行排序
作为开发者,你可以为自己的应用定义合适的精度边界,而调优则会在该边界内,从已评估的构建路径中找出性能最佳的那一个。其结果是一个系统化的约束优化过程,而非在速度与精度之间临时做出的取舍。
这一思想实验与任何TensorRT版本所暴露的具体编译器开关无关,其目的在于展示更广泛的设计原则:当调度选择被表示为可调构建路径、且精度被视为显式约束条件时,性能与精度的优化就变得可测量、可复现,并由具体应用定义。
全局性能调优器的最佳实践
全局性能调优器使构建路径的探索变得系统化,但有效的调优仍然得益于周密的搜索策略。当表达式包含众多可变开关时,可能的组合数量会迅速增长。以下实践有助于让调优过程保持可控、可复现,并与目标应用保持一致。
根据搜索空间选择搜索策略
将默认构建路径作为基准,用于与其他路径进行比较。对于广泛的初始扫描,快速搜索会评估默认路径,并逐一改变单个开关。对于较大的表达式,混合搜索往往是一个实用的起点:它先执行快速扫描,然后探索那些带来改善的开关取值组合。应将全量搜索保留给经过精心挑选的少量开关组,因为它会评估这些开关的完整笛卡尔积。
在开始快速或全量扫描之前,--dryRun可以显示具体的路径列表,而无需构建引擎。这为在正式投入完整调优运行之前检查搜索空间提供了一种快捷方式。由于混合搜索的第二阶段依赖于第一阶段收集的测量数据,因此不支持使用--dryRun。
将调优视为一个渐进且可恢复的过程
当指定了--tuningCacheFile时,每次完成的迭代都会连同其构建路径、测得的GPU时间以及精度结果一起被记录下来。如果扫描过程中断或达到时间限制,之后可以使用--continue从记录的状态继续进行。借助这种方式,可以将调优作为一个渐进式实验来开展,而不必要求每次搜索都在一次不间断的运行中完成。
当特定路径出现故障或精度回退需要深入调查时,--saveAllEngines可以保留每次迭代生成的引擎。由于该选项会消耗大量存储空间,它更适用于调试,而非日常调优。
使调优流程适配具体应用
提供的输入和参考输出应当代表对应用而言真正重要的部署条件。一个在某个狭窄数据集上满足精度要求的引擎,仅仅是针对该数据集、该指标和该阈值进行了验证。应纳入具有代表性的输入、重要的边界情形,以及能反映应用实际质量要求的精度指标。
trtexec的实现提供了一个可直接使用的调优器,但它并非唯一可行的调优策略。构建器配置相关的API暴露了可用的开关,并支持应用单个构建路径,而开源的trtexec实现则展示了如何组织周边的调优循环。开发者可以构建针对特定应用的搜索策略,引入启发式过滤机制,或在比较候选方案性能之前应用应用层级的精度验证。
将以往结果视为参考,而非普遍适用的解决方案
最佳构建路径与搜索时所使用的具体模型、输入形状、目标GPU、软件栈以及TensorRT和调优器版本密切相关。某个在一次部署中带来改善的路径,在另一次部署中可能毫无效果,甚至导致性能回退。在更改模型、形状、目标硬件、CUDA或驱动环境,或相关TensorRT版本后,应重新进行调优。
此前调优实验所积累的经验仍然具有价值。如果某些开关在一类相关工作负载中反复带来改善,这些经验可以为未来的搜索空间设计提供指导,并帮助优先考虑有潜力的候选方案。此类启发式经验应当作为聚焦探索方向的先验信息,而每一条最终路径都应在其实际部署环境中进行验证和测量。
开始使用TensorRT中的全局性能调优器功能
全局性能调优器功能将TensorRT性能优化,从依赖单一通用调度策略,转变为评估针对特定模型的多种替代方案。可定制的构建路径解决了性能定制化的问题,使不同模型和部署环境能够探索不同的调度决策。
将候选方案生成与路径搜索分离开来,提供了一条摆脱启发式规则泛滥的可扩展路径,减少了将每一个针对特定模型的发现都硬编码为又一条特例规则的必要性。精度感知调优同样解决了性能与精度相互纠缠的问题。精度决定哪些候选引擎是可接受的,而端到端性能则决定哪个可接受的引擎胜出。因此,最终选出的结果,是针对目标模型和环境所评估的构建路径中,实测速度最快的合格引擎。
TensorRT提供了出色的基础方案,编译器开关提供了受控的变化,而调优则为每个用户找出最佳的可接受变化方案。这种从通用预测转向针对特定模型实证的转变,正是全局性能调优器功能将TensorRT优化扩展至超越单一构建路径的方式。
如需上手使用,请查阅全局性能调优器文档以及开源的trtexec实现。你可以用自己的精度验证算法、搜索策略,或优先选择有潜力构建路径的启发式方法来扩展该示例。若需要更精细的控制,可以使用TensorRT构建路径相关API从零开始构建调优器,根据应用的精度要求和性能目标,定制候选方案的探索与验证方式。
