地平线与英伟达工具链 PTQ 工具功能参数对比与实操
1.理论简介
在阅读本文之前,希望大家对 PTQ(Post-Training Quantization) 训练后量化有一定的了解~
地平线 OpenExplorer 和 NVIDIA TensorRT 是两家公司为适配自己的硬件而开发的算法工具链,它们各自具有独特的特点和优势。分开看的时候,网上有很多资料,但却没找到将他们放在一起对比的文章,本文从 PTQ 通路进行对比介绍。
OpenExplorer 中文名为天工开物,是地平线发布的算法开发平台,主要包括模型编译优化工具集、算法仓库和应用开发 SDK 三大功能模块。
TensorRT 是 NVIDIA 高性能深度学习的推理 SDK,包含深度学习推理优化器和运行时环境,可加速深度学习推理应用。利用 Pytorch、TensorFlow 等 DL 框架训练好的模型,通过模型转换编译生成可以在各家硬件上运行的格式(TensorRT xxx.engine/xxx.trt 或地平线 xxx.hbm/xxx.bin),提升这个模型在各家硬件(英伟达 GPU、地平线 BPU)上运行的速度。
为了让深度学习模型高效的在自家硬件上运行起来,他们都会做很多优化,包括但不限于 量化、数据压缩、算子替换、算子拆分、算子融合等等优化措施,下面以两家 show 出来的算子融合为例来看一下:
英伟达宣传材料中的图片
地平线宣传材料中的图片
量化方案:两家都是对称均匀量化,weight 是 per channel,feature 为 per tensor,地平线在有限条件下支持 feature per channel 量化
TensorRT 在进行 PTQ 量化(默认进行 fp32 不量化) 时,会在优化网络的时候尝试 int8 精度,假设某一层在 int8 精度下速度优于默认精度(fp32),则优先使用 int8。支持的数据类型与精度:https://docs.nvidia.com/deeplearning/tensorrt/support-matrix/index.html#layers-precision-matrix,对应的 onnx 算子:https://github.com/onnx/onnx-tensorrt/blob/main/docs/operators.md
地平线 OpenExplorer 默认使用 int8 量化,部分节点 BPU 支持 int16、int32,可以通过 node_info、run_on_bpu、run_on_cpu 参数控制量化精度以及运行的器件。
硬件相关:
TensorRT 的核心在于对模型算子的优化(算子合并、量化、利用 GPU 特性选择特定核函数等策略),通过 tensorRT 能够在 NVIDIA 系列 GPU 上获得最好的性能,因此 tensorRT 的模型,需要在目标 GPU 上实际运行的方式选择最优算法和配置,也就是说 tensorRT 生成的模型只能在特定条件下运行(编译的 trt 版本、cuda 版本、编译时的 GPU 型号),不同硬件之间的优化是不能共享的。但是从 tensorrt8.6 开始,–hardwareCompatibilityLevel 参数可以允许用户在不同的架构上构建和运行模型,可能会带来一些性能损失(视情况而定,5%左右,若某个大的优化只支持特定架构,性能损失会比较大);
OpenExplorer 进行 PTQ 量化时需要指定参数 march,指定产出混合异构模型需要支持的平台架构,针对不同硬件,地平线提供的 OpenExplorer 是不同的,当然,本质上用到的几个 whl 包是相同的。
2.参数对比解读
NVIDIA
trtexec 工具提供的参数总体上可以分为:Model Options、Build Options、Inference Options、Reporting Options、System Options,最常用到的是前三个;
Horizon 征程 5
hb_mapper 工具提供的参数总体上可以分为:模型参数组、输入信息参数组、校准参数组、编译参数组、自定义算子参数组;
hrt_model_exec 工具提供模型信息查看、模型推理、模型性能评测三组参数;
Horizon 征程 6
hb_compile 等效于 hb_mapper 工具,新增了一些功能参数,用法上稍有不同
同样使用 hrt_model_exec 工具 可以粗暴理解为:NVIDIA trtexec = Horizon J5 hb_mapper/J6 hb_compile + hrt_model_exec 本文将以 NVIDIA trtexec 工具(TensorRT-8.6.1)为核心,看看地平线 J5 OpenExplorer1.1.68 和 J6 OpenExplorer3.0.17 是如何提供与 trtexec 工具类似功能的。
2.1 模型转换编译(构建)
trtexec 工具常用参数与 J5 hb_mapper/J6 hb_compile 工具对比
2.1.1 Model Options
J5 hb_mapper: –model
J6 hb_compile:–model NVIDIA 支持 ONNX、Caffe、UFF,Horizon 支持 ONNX、Caffe,但均主流支持 ONNX,本文仅介绍 ONNX 相关内容
2.1.2 Build Options
征程 5 不支持动态 shape
征程 6 支持动态 shape,hb_compile 对应参数:待完善
J5 hb_mapper 相关参数有:
node_info # 配置节点输入/输出精度
input_type_rt # 板端模型输入数据格式 nv12/rgb/featuremap 等
input_layout_rt # 板端输入数据排布 NCHW/NHWC
输出排布和 onnx 保持一致
input_type_train # 原始浮点模型的输入数据类型 rgb/bgr 等
input_layout_train # 原始浮点模型的输入数据排布 NCHW/NHWC
J6 hb_compile 相比于 J5 hb_mapper 的差异:
取消 input_layout_rt,板端输入 layout 与原始浮点输入数据排布相同
增加 quant_config 参数,支持对模型算子计算精度进行精细化配置
NVIDIA 的 tensor core 和地平线的 BPU core 都是 HWC 排布的,HWC 数据排布是为了编译优化模型性能,虽然 CHW 和 HWC 排布均支持,但内部会进行转换。 针对网络输入/输出数据排布,允许用户进行一些控制
J5 hb_mapper 以及 J6 hb_compile 不需要配置类似参数
类似于 hb_mapper/hb_compile 中 advice 和 debug 参数
hb_mapper 与 hb_compile 中无相关参数
允许用户在运行时重新适配(refit)TensorRT 引擎的权重。对于需要在推理过程中动态更新模型权重的场景比较有用,例如在模型部署后需要根据新数据进行微调,强化学习中或在保留相同结构的同时重新训练模型时,权重更新是使用 Refitter(C++、Python)接口执行的。
hb_mapper 与 hb_compile 中无对应参数,默认支持稀疏化
J5 hb_mapper 不支持 TF32/fp16/fp8、可以通过 run_on_cpu 或 node_info 将节点配置运行在 CPU 上
J5 hb_mapper 支持 int8 和 int16 以及尾部 conv 节点 int32 量化,可以通过 node_info 或 run_on_bpu 参数进行配置
J6 hb_compile 通过 node_info 和 quant_config,支持对模型算子计算精度进行精细化配置
TF32 是英伟达提出的代替 FP32 的单精度浮点格式,TF32 采用与半精度( FP16 )数学相同的 10 位尾数位精度,这样的精度水平远高于 AI 工作负载的精度要求。同时, TF32 采用与 FP32 相同的 8 位指数位,能够支持与其相同的数字范围。 注意:NV 不论配置哪一项,fp32 都是会使用的。举例:配置–fp16,网络会使用 fp16+fp32;配置–int8 和–fp16,网络会使用 int8+fp16+fp32
类似于 hb_mapper/hb_compile --fast-perf 功能,主要用于性能评测
类似于 J5 hb_mapper run_on_bpu、run_on_cpu、node_info 三个参数的功能,支持对模型算子计算精度进行精细化配置
类似于 J6 hb_compile node_info 和 quant_config,支持对模型算子计算精度进行精细化配置
类似于 hb_mapper/hb_compile node_info、run_on_cpu、run_on_bpu 参数的功能
类似于 hb_mapper/hb_compile 中如下参数:
cal_data_dir # 模型校准使用的样本存放目录
cal_data_type # 指定校准数据的数据存储类型
下面针对校准数据进行一些更详细的介绍:
trtexec 工具支持送入校准数据,在校准过程中,可以使用 --calib 选项来指定一个包含校准数据的文件。这个文件通常是一个缓存文件,包含了模型在特定输入数据集上运行时的激活值的统计信息。
trtexec 工具本身不提供校准数据的生成功能,需要事先通过其他方式(例如使用 TensorRT 的 API trt.IInt8EntropyCalibrator2)生成,包含了用于量化的统计信息。
NVIDIA 校准方法
IInt8EntropyCalibrator2 熵标定选择张量的尺度因子来优化量子化张量的信息论内容,通常可以抑制分布中的异常值。目前推荐的熵校准器。默认情况下,校准发生在层融合之前,适用于 cnn 类型的网络。
IInt8EntropyCalibrator 最原始的熵校准器,目前已不推荐使用。默认情况下,校准发生在层融合之后。
IInt8MinMaxCalibrator 该校准器使用整个激活分布范围来确定比例因子。推荐用于 NLP 任务的模型中。默认情况下,校准发生在层融合之前。
IInt8LegacyCalibrator 该校准器需要用户进行参数化,默认情况下校准发生在层融合之后,不推荐使用。
hb_mapper/hb_compile 工具支持送入校准数据(cal_data_dir),且在模型转换编译过程中选择校准方法(cal_data_type)。与 NVDIA 不同的是,地平线校准数据仅是满足模型输入的图像/数据文件,并不包含校准信息。
hb_mapper/hb_compile 工具本身不提供校准数据的生成功能,需要事先通过其他方式(例如使用 numpy)生成,不包含用于量化的统计信息。
地平线校准方法(校准发生在融合之后)
default default 是一个自动搜索的策略,会尝试从系列校准量化参数中获得一个相对效果较好的组合。
mix mix 是一个集成多种校准方法的搜索策略,能够自动确定量化敏感节点,并在节点粒度上从不同的校准方法中挑选出最佳方法, 最终构建一个融合了多种校准方法优势的组合校准方式。
kl KL 校准方法是借鉴了 TensorRT 提出的解决方案 , 使用 KL 熵值来遍历每个量化层的数据分布,通过寻找最低的 KL 熵值,来确定阈值。 这种方法会导致较多的数据饱和和更小的数据量化粒度,在一些数据分布比较集中的模型中拥有着比 max 校准方法更好的效果。
max max 校准方法是在校准过程中,自动选择量化层中的最大值作为阈值。 这种方法会导致数据量化粒度较大,但也会带来比 KL 方法更少的饱和点数量,适用于那些数据分布比较离散的神经网络模型。
类似于 hb_mapper/hb_compile 中如下参数:
working_dir # 模型转换输出结果的存放目录
output_model_file_prefix # 指定转换产出物名称前缀
hb_mapper/hb_compile 默认启用 cache 功能
通过时序缓存保存构建阶段的 Layer 分析信息(特定于目标设备、CUDA 版本、TensorRT 版本),如果有其他层具备相同的输入/输出张量配合和层参数,则 TensorRT 构建器会跳过分析并重用缓存结果。
J5 hb_mapper 相关参数:optimize_level,范围是 O0~O3,默认是 O0,耗时评测时需要配置为 O3
J6 hb_compile 相关参数:optimize_level,范围是 O0~O2,默认是 O0,耗时评测时需要配置为 O2
下图是 trtexec 关于优化等级对于构建 build 耗时与延迟 latency 耗时,hb_mapper/hb_compile 也是类似的情况。
hb_mapper/hb_compile 无相关参数,默认兼容,跨多个版本时可能会存在不兼容的情况
hb_mapper/hb_compile 无相关参数,通过 march 指定架构即可,例如 征程 5 的 bayes、征程 6 的 nash-e/m 等
hb_mapper/hb_compile 相关参数:
compile_mode # 编译策略选择
balance_factor # balance 编译策略时的比例系数
stream 是 CUDA 中为了实现多个 kernel 同时在 GPU 上运行,实现对 GPU 资源划分,利用流水线的方法提高 GPU 吞吐率的机制。 并行化操作,地平线会在模型编译时由编译器完成。
2.2 模型推理(评测) Inference Options
trtexec 工具常用参数与 hrt_model_exec 工具(J5、J6 都有这个工具)对比
hrt_model_exec 相关参数:
model_file # 模型文件路径
model_name # 指定模型中某个模型的名称,针对打包模型,用的较少
征程 5 尚不支持动态 shape、征程 6 待呈现
hrt_model_exec 相关参数:input_file
hrt_model_exec 相关参数:frame_count
hrt_model_exec 不支持 warmup,采用多帧推理获取平均值的方式,可以很大程度上规避第一帧多出来的耗时
hrt_model_exec 相关参数:perf_time
hrt_model_exec 无相关参数
J5 hrt_model_exec 无相关参数,没有多流的概念
在 CUDA 中,流(stream)是一种执行模型,它允许开发者将多个计算任务(如内核执行、内存拷贝等)组织成队列,由 GPU 异步执行。使用多个流可以提高 GPU 利用率,因为当一个流的任务等待内存拷贝或其他非计算密集型操作时,GPU 可以切换到另一个流执行计算密集型任务。infStreams 与 CUDA 流的概念直接相关,它影响的是模型推理任务在 GPU 上的并行执行。 maxAuxStreams 是 TensorRT 内部用于优化网络层执行的机制,它允许 TensorRT 在内部使用多个流来并行化可以并行化的层。 两者之间的关系在于它们都旨在通过并行化策略来提高 GPU 上的推理性能,但它们作用的层面和具体实现方式不同。
hrt_model_exec 无相关参数
地平线 CPU 和 BPU 是共享内存的
hrt_model_exec 无相关参数
hrt_model_exec 无相关参数
hrt_model_exec 无相关参数
启用这个参数时,TensorRT 在等待 GPU 计算完成时使用自旋等待(spin wait)策略,而不是阻塞等待(block wait)。
阻塞等待:在默认情况下,当 TensorRT 引擎执行推理任务时,如果 GPU 计算尚未完成,它会挂起(阻塞)当前线程,直到 GPU 计算完成并返回结果。这种等待方式可能会导致线程在等待期间不执行任何操作,从而影响整体的 CPU 利用率和系统性能。
自旋等待:启用 --useSpinWait 参数后,TensorRT 会采用自旋等待策略。在这种模式下,线程会循环检查 GPU 计算是否完成,而不是挂起。自旋等待可以减少线程挂起和恢复的开销,从而在某些情况下,例如 GPU 计算时间与 CPU 处理时间相比 较短的情况下。通过减少线程挂起的频率,可以提高 CPU 的利用率,从而可能提升整体的系统性能。
GPU 计算时间不稳定或较短时,自旋等待可以减少线程上下文切换的开销,并保持 CPU 核心的活跃状态。然而,自旋等待也可能导致 CPU 资源的过度使用,特别是在 GPU 计算时间较长的情况下,因此需要根据具体的应用场景和硬件配置来权衡是否使用这个参数。
hrt_model_exec 相关参数:thread_num
"stream(流)"和"thread(线程)"是两个不同的概念,用于处理并发和数据流的情况。
线程(Thread): 线程是计算机程序中执行的最小单位,也是进程的一部分。一个进程可以包含多个线程,它们共享进程的资源,如内存空间、文件句柄等。线程可以并行执行,使得程序能够同时处理多个任务。线程之间可以共享数据,但也需要考虑同步和互斥问题,以避免竞争条件和数据损坏。
流(Stream): 流是一种数据传输的抽象概念,通常用于输入和输出操作。在计算机编程中,流用于处理数据的连续流动,如文件读写、网络通信等。流可以是字节流(以字节为单位处理数据)或字符流(以字符为单位处理数据)。流的一个常见特性是按顺序处理数据,不需要一次性将所有数据加载到内存中。 总之,线程是一种用于实现并发执行的机制,而流是一种用于处理数据传输的抽象概念。
hrt_model_exec 无相关参数
useCudaGraph 参数允许用户指示 TensorRT 在执行推理时使用 CUDA 图(CUDA Graph)。CUDA 图是一种 CUDA 编程技术,它允许开发者创建一个或多个 CUDA 内核及其内存依赖关系的静态表示,这可以提高执行效率和性能。 CUDA 图的优势
性能提升:通过使用 CUDA 图,可以减少运行时的开销,因为它们允许预编译一组 CUDA 操作,从而减少每次执行操作时的启动延迟。
重用性:一旦创建了 CUDA 图,它可以被重用于多个推理请求,这使得它特别适合于高吞吐量和低延迟的应用场景。
并行化:CUDA 图可以并行执行多个节点,这有助于提高 GPU 的利用率和整体的推理性能。 使用场景
高并发推理:在需要处理大量并发推理请求的场景中,使用 --useCudaGraph 可以提高处理速度和效率
hrt_model_exec 会在终端中打印 加载 板端模型 的时间
反序列化时间:–timeDeserialize 参数会让 trtexec 测量将序列化的 TensorRT 引擎文件加载到 GPU 内存中所需的时间。
性能分析:通过测量反序列化时间,开发者可以了解模型加载阶段的性能瓶颈,并探索减少模型加载时间的方法。
hrt_model_exec 无相关参数
猜测:重新适配(refitting)是指在模型转换为 TensorRT 引擎后,根据新的权重或校准数据更新引擎的过程,比如将模型的权重从一种精度转换为另一种精度,或者根据新的校准数据调整量化参数。
类似于 hb_mapper/hb_compile 中 debug 参数,debug 默认配置为 True,编译后会在 html 静态性能评估文件中增加逐层的信息打印,可以帮助分析性能瓶颈。该参数开启后不会影响模型的推理性能,但会极少量地增加模型文件大小。
trtexec 使用该参数,一次用于收集性能分析数据的运行,另一次用于计算性能基准测试的运行,提高分析/测试的准确性。
地平线 模型构建与模型推理/性能评测是分开的,无相关参数
地平线无相关参数
缓存管理:–persistentCacheRatio 参数用于控制 TensorRT 引擎在执行推理时分配给持久化缓存的内存比例
性能优化:合理设置缓存比例可以提高模型的推理性能,尤其是在处理大型模型或复杂网络结构时
内存使用:增加持久化缓存的比例可能会减少内存占用,但也可能导致缓存溢出
TensorRT 会自动管理缓存,因此手动设置–persistentCacheRatio 不是必须的。只有需要精细控制内存使用或优化性能时才会用到
2.3 报告选项 Reporting Options
地平线无相关参数
日志中增加很多信息,类似于:[08/09/2024-17:18:51] [V] [TRT] Registered plugin creator - ::BatchedNMSDynamic_TRT version 1
类似于 hrt_model_exec 中 frame_count 参数
为了减少偶然因素对性能测试结果的影响,通过多次运行推理并取平均值来提供一个更加稳定和可靠的性能度量。
hrt_model_exec 中无相关参数
设置 --percentile=99,trtexec 将会报告第 99 百分位的执行时间,这意味着在 100 次推理中,有 99 次的执行时间会小于或等于报告的值,而只有 1 次的执行时间会大于这个值。故:0 representing max perf, and 100 representing min perf
hrt_model_exec 中无相关参数
hb_mapper/hb_compile 默认会在日志中打印层的信息
hb_mapper/hb_compile 默认开启 debug 参数后,会在转换编译过程中生成 html 文件,其中有类似的层耗时信息
hrt_model_exec 工具中 profile_path 参数
类似于 J5 hrt_model_exec 中 dump_intermediate、enable_dump、dump_format 等
hrt_model_exec 无相关参数
2.4 系统选项 System Options
类似于 hrt_model_exec 中 core_id 参数
类似于 J5 hb_mapper 中 custom_op_method、op_register_files、custom_op_dir 参数
J6 hb_compile 待确定
看到这儿,trtexec 大部分参数就介绍完成了,还有少量不常用到参数,例如–minTiming、–avgTiming=M、–tempdir、–tempfileControls、–useRuntime=runtime、–leanDLLPath=、–excludeLeanRuntime、–ignoreParsedPluginLibs 未进行介绍,欢迎大家自行探索。
各家的工具都会针对自己的硬件或特性设计针对性的参数,只要满足开发者需要的功能即可,例如地平线工具链的一些参数,有一些就没介绍到。
这么多参数其实并不是都会用到,大家根据自己的需求选择性使用即可。
3.实操演示
3.1 onnx 模型生成
上述代码运行后,会生成一个 static.onnx,接下来就可以使用这个 onnx 啦。
3.2 性能评测实测
实操的方向不同,使用的命令和脚本也会有差异,本文重点在对比两家工具链的 PTQ 功能参数对比介绍上,因此只选择一个性能评测方向进行实操演示。
英伟达 trtexec
构建用于性能评测的 engine,另外性能数据可以一起产出,脚本如下:
会产出 engine 文件:resnet18.engine,以及一些日志,例如:
地平线 hb_compile 与 hrt_model_exec
转换编译用于性能评测的 hbm
会生成 static.hbm,以及一些日志
在板端评测性能数据
由于时间原因,就先到这儿了,下次再聊~
评论