地平線與英偉達(dá)工具鏈 PTQ 工具功能參數(shù)對比與實操
在閱讀本文之前,希望大家對 PTQ(Post-Training Quantization) 訓(xùn)練后量化有一定的了解~
地平線 OpenExplorer 和 NVIDIA TensorRT 是兩家公司為適配自己的硬件而開發(fā)的算法工具鏈,它們各自具有獨特的特點和優(yōu)勢。分開看的時候,網(wǎng)上有很多資料,但卻沒找到將他們放在一起對比的文章,本文從 PTQ 通路進(jìn)行對比介紹。
OpenExplorer 中文名為天工開物,是地平線發(fā)布的算法開發(fā)平臺,主要包括模型編譯優(yōu)化工具集、算法倉庫和應(yīng)用開發(fā) SDK 三大功能模塊。
TensorRT 是 NVIDIA 高性能深度學(xué)習(xí)的推理 SDK,包含深度學(xué)習(xí)推理優(yōu)化器和運行時環(huán)境,可加速深度學(xué)習(xí)推理應(yīng)用。利用 Pytorch、TensorFlow 等 DL 框架訓(xùn)練好的模型,通過模型轉(zhuǎn)換編譯生成可以在各家硬件上運行的格式(TensorRT xxx.engine/xxx.trt 或地平線 xxx.hbm/xxx.bin),提升這個模型在各家硬件(英偉達(dá) GPU、地平線 BPU)上運行的速度。
為了讓深度學(xué)習(xí)模型高效的在自家硬件上運行起來,他們都會做很多優(yōu)化,包括但不限于 量化、數(shù)據(jù)壓縮、算子替換、算子拆分、算子融合等等優(yōu)化措施,下面以兩家 show 出來的算子融合為例來看一下:
地平線宣傳材料中的圖片
量化方案:兩家都是對稱均勻量化,weight 是 per channel,feature 為 per tensor,地平線在有限條件下支持 feature per channel 量化
TensorRT 在進(jìn)行 PTQ 量化(默認(rèn)進(jìn)行 fp32 不量化) 時,會在優(yōu)化網(wǎng)絡(luò)的時候嘗試 int8 精度,假設(shè)某一層在 int8 精度下速度優(yōu)于默認(rèn)精度(fp32),則優(yōu)先使用 int8。支持的數(shù)據(jù)類型與精度:https://docs.nvidia.com/deeplearning/tensorrt/support-matrix/index.html#layers-precision-matrix,對應(yīng)的 onnx 算子:https://github.com/onnx/onnx-tensorrt/blob/main/docs/operators.md
地平線 OpenExplorer 默認(rèn)使用 int8 量化,部分節(jié)點 BPU 支持 int16、int32,可以通過 node_info、run_on_bpu、run_on_cpu 參數(shù)控制量化精度以及運行的器件。
硬件相關(guān):
TensorRT 的核心在于對模型算子的優(yōu)化(算子合并、量化、利用 GPU 特性選擇特定核函數(shù)等策略),通過 tensorRT 能夠在 NVIDIA 系列 GPU 上獲得最好的性能,因此 tensorRT 的模型,需要在目標(biāo) GPU 上實際運行的方式選擇最優(yōu)算法和配置,也就是說 tensorRT 生成的模型只能在特定條件下運行(編譯的 trt 版本、cuda 版本、編譯時的 GPU 型號),不同硬件之間的優(yōu)化是不能共享的。但是從 tensorrt8.6 開始,–hardwareCompatibilityLevel 參數(shù)可以允許用戶在不同的架構(gòu)上構(gòu)建和運行模型,可能會帶來一些性能損失(視情況而定,5%左右,若某個大的優(yōu)化只支持特定架構(gòu),性能損失會比較大);
OpenExplorer 進(jìn)行 PTQ 量化時需要指定參數(shù) march,指定產(chǎn)出混合異構(gòu)模型需要支持的平臺架構(gòu),針對不同硬件,地平線提供的 OpenExplorer 是不同的,當(dāng)然,本質(zhì)上用到的幾個 whl 包是相同的。
NVIDIA
trtexec 工具提供的參數(shù)總體上可以分為:Model Options、Build Options、Inference Options、Reporting Options、System Options,最常用到的是前三個;
Horizon 征程 5
hb_mapper 工具提供的參數(shù)總體上可以分為:模型參數(shù)組、輸入信息參數(shù)組、校準(zhǔn)參數(shù)組、編譯參數(shù)組、自定義算子參數(shù)組;
hrt_model_exec 工具提供模型信息查看、模型推理、模型性能評測三組參數(shù);
Horizon 征程 6
hb_compile 等效于 hb_mapper 工具,新增了一些功能參數(shù),用法上稍有不同
同樣使用 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 工具類似功能的。
trtexec 工具常用參數(shù)與 J5 hb_mapper/J6 hb_compile 工具對比
2.1.1 Model Options--onnx=<file> ONNX model # 指定 onnx model 的路徑
J5 hb_mapper: –model
J6 hb_compile:–model NVIDIA 支持 ONNX、Caffe、UFF,Horizon 支持 ONNX、Caffe,但均主流支持 ONNX,本文僅介紹 ONNX 相關(guān)內(nèi)容
--minShapes=spec Build with dynamic shapes using a profile with the min shapes provided # 指定動態(tài)輸入形狀的范圍最小值
--optShapes=spec Build with dynamic shapes using a profile with the opt shapes provided # 指定動態(tài)輸入形狀的范圍常見值
--maxShapes=spec Build with dynamic shapes using a profile with the max shapes provided # 指定動態(tài)輸入形狀的范圍最大值
動態(tài)shape時,三個參數(shù)均必須配置,可以配置為一樣的
Example
--minShapes=input0:1x3x224x224
--optShapes=input0:1x3x224x224
--maxShapes=input0:16x3x224x224
多輸入時 spec:
input0:1x3x256x256,input1:1x3x128x128
征程 5 不支持動態(tài) shape
征程 6 支持動態(tài) shape,hb_compile 對應(yīng)參數(shù):待完善
# 輸入/輸出 數(shù)據(jù)精度和排布 --inputIOFormats=spec Type and format of each of the input tensors (default = all inputs in fp32:chw) --outputIOFormats=spec Type and format of each of the output tensors (default = all outputs in fp32:chw) # 精度可選:fp32、fp16、int32、int8 # 排布可選chw、hwc、chw16等 # Example --inputIOFormats=fp32:chw,fp32:chw # 兩個輸入 -outputIOFormats=fp16:chw,fp16:chw # 兩個輸出
J5 hb_mapper 相關(guān)參數(shù)有:
node_info # 配置節(jié)點輸入/輸出精度
input_type_rt # 板端模型輸入數(shù)據(jù)格式 nv12/rgb/featuremap 等
input_layout_rt # 板端輸入數(shù)據(jù)排布 NCHW/NHWC
輸出排布和 onnx 保持一致
input_type_train # 原始浮點模型的輸入數(shù)據(jù)類型 rgb/bgr 等
input_layout_train # 原始浮點模型的輸入數(shù)據(jù)排布 NCHW/NHWC
J6 hb_compile 相比于 J5 hb_mapper 的差異:
取消 input_layout_rt,板端輸入 layout 與原始浮點輸入數(shù)據(jù)排布相同
增加 quant_config 參數(shù),支持對模型算子計算精度進(jìn)行精細(xì)化配置
NVIDIA 的 tensor core 和地平線的 BPU core 都是 HWC 排布的,HWC 數(shù)據(jù)排布是為了編譯優(yōu)化模型性能,雖然 CHW 和 HWC 排布均支持,但內(nèi)部會進(jìn)行轉(zhuǎn)換。 針對網(wǎng)絡(luò)輸入/輸出數(shù)據(jù)排布,允許用戶進(jìn)行一些控制
--workspace=N # Set workspace size in MiB,可融進(jìn)memPoolSize參數(shù)中
--memPoolSize=poolspec # Specify the size constraints of the designated memory pool(s) in MiB.
Example: --memPoolSize=workspace:1024.5,dlaSRAM:256
J5 hb_mapper 以及 J6 hb_compile 不需要配置類似參數(shù)
--profilingVerbosity=mode # Specify profiling verbosity. mode ::= layer_names_only|detailed|none (default = layer_names_only), 打印信息的詳細(xì)程度
Example: --profilingVerbosity=detailed
# 構(gòu)建期間保留更多逐層信息
類似于 hb_mapper/hb_compile 中 advice 和 debug 參數(shù)
--refit # 標(biāo)記生成的這個engine是refittable,即可以指定稍后更新其權(quán)重
hb_mapper 與 hb_compile 中無相關(guān)參數(shù)
允許用戶在運行時重新適配(refit)TensorRT 引擎的權(quán)重。對于需要在推理過程中動態(tài)更新模型權(quán)重的場景比較有用,例如在模型部署后需要根據(jù)新數(shù)據(jù)進(jìn)行微調(diào),強(qiáng)化學(xué)習(xí)中或在保留相同結(jié)構(gòu)的同時重新訓(xùn)練模型時,權(quán)重更新是使用 Refitter(C++、Python)接口執(zhí)行的。
--sparsity=spec # Control sparsity (default = disabled),spec ::= "disable", "enable", "force" # disable = 不稀疏 # enable = 權(quán)重滿足稀疏規(guī)則才稀疏 # force = 強(qiáng)制稀疏
hb_mapper 與 hb_compile 中無對應(yīng)參數(shù),默認(rèn)支持稀疏化
--noTF32 # 禁用TF32精度,(default is to enable tf32, in addition to fp32)
--fp16 # 使能fp16精度
--fp8 # 使能fp8精度
--int8 # 使能int8量化精度
J5 hb_mapper 不支持 TF32/fp16/fp8、可以通過 run_on_cpu 或 node_info 將節(jié)點配置運行在 CPU 上
J5 hb_mapper 支持 int8 和 int16 以及尾部 conv 節(jié)點 int32 量化,可以通過 node_info 或 run_on_bpu 參數(shù)進(jìn)行配置
J6 hb_compile 通過 node_info 和 quant_config,支持對模型算子計算精度進(jìn)行精細(xì)化配置
TF32 是英偉達(dá)提出的代替 FP32 的單精度浮點格式,TF32 采用與半精度( FP16 )數(shù)學(xué)相同的 10 位尾數(shù)位精度,這樣的精度水平遠(yuǎn)高于 AI 工作負(fù)載的精度要求。同時, TF32 采用與 FP32 相同的 8 位指數(shù)位,能夠支持與其相同的數(shù)字范圍。 注意:NV 不論配置哪一項,fp32 都是會使用的。舉例:配置–fp16,網(wǎng)絡(luò)會使用 fp16+fp32;配置–int8 和–fp16,網(wǎng)絡(luò)會使用 int8+fp16+fp32
--best # fp32+fp16+int8 同時使用,找一個速度最快的
類似于 hb_mapper/hb_compile --fast-perf 功能,主要用于性能評測
--precisionConstraints=spec # 精度限制spec ::= "none" | "obey" | "prefer",(default = none),聯(lián)合下面兩個參數(shù)使用 # none = 無限制 # prefer = 優(yōu)先滿足--layerPrecisions/--layerOutputTypes設(shè)置的精度限制,不滿足可回退到默認(rèn)精度 # obey = 優(yōu)先滿足--layerPrecisions/--layerOutputTypes設(shè)置的精度限制,不滿足報錯退出--layerPrecisions=spec # 控制per-layer的運算精度,僅在precisionConstraints為obey或prefer時有效. (default = none) # spec::= layerName:precision # precision::= "fp32"|"fp16"|"int32"|"int8"--layerOutputTypes=spec # 控制per-layer的輸出精度(類型),僅在precisionConstraints為obey或prefer時有效. (default = none) # spec::= layerName:type # type ::= "fp32"|"fp16"|"int32"|"int8"
類似于 J5 hb_mapper run_on_bpu、run_on_cpu、node_info 三個參數(shù)的功能,支持對模型算子計算精度進(jìn)行精細(xì)化配置
類似于 J6 hb_compile node_info 和 quant_config,支持對模型算子計算精度進(jìn)行精細(xì)化配置
--layerDeviceTypes=spec # 指定layer運行的器件 # spec ::= layerName:deviceType # deviceType ::= "GPU"|"DLA"
類似于 hb_mapper/hb_compile node_info、run_on_cpu、run_on_bpu 參數(shù)的功能
--calib # 指定int8校準(zhǔn)緩存文件
類似于 hb_mapper/hb_compile 中如下參數(shù):
cal_data_dir # 模型校準(zhǔn)使用的樣本存放目錄
cal_data_type # 指定校準(zhǔn)數(shù)據(jù)的數(shù)據(jù)存儲類型
下面針對校準(zhǔn)數(shù)據(jù)進(jìn)行一些更詳細(xì)的介紹:
trtexec 工具支持送入校準(zhǔn)數(shù)據(jù),在校準(zhǔn)過程中,可以使用 --calib 選項來指定一個包含校準(zhǔn)數(shù)據(jù)的文件。這個文件通常是一個緩存文件,包含了模型在特定輸入數(shù)據(jù)集上運行時的激活值的統(tǒng)計信息。
trtexec --onnx=model.onnx --int8 --calib=calibrationCacheFile.cache
trtexec 工具本身不提供校準(zhǔn)數(shù)據(jù)的生成功能,需要事先通過其他方式(例如使用 TensorRT 的 API trt.IInt8EntropyCalibrator2)生成,包含了用于量化的統(tǒng)計信息。
NVIDIA 校準(zhǔn)方法
IInt8EntropyCalibrator2 熵標(biāo)定選擇張量的尺度因子來優(yōu)化量子化張量的信息論內(nèi)容,通??梢砸种品植贾械漠惓V?。目前推薦的熵校準(zhǔn)器。默認(rèn)情況下,校準(zhǔn)發(fā)生在層融合之前,適用于 cnn 類型的網(wǎng)絡(luò)。
IInt8EntropyCalibrator 最原始的熵校準(zhǔn)器,目前已不推薦使用。默認(rèn)情況下,校準(zhǔn)發(fā)生在層融合之后。
IInt8MinMaxCalibrator 該校準(zhǔn)器使用整個激活分布范圍來確定比例因子。推薦用于 NLP 任務(wù)的模型中。默認(rèn)情況下,校準(zhǔn)發(fā)生在層融合之前。
IInt8LegacyCalibrator 該校準(zhǔn)器需要用戶進(jìn)行參數(shù)化,默認(rèn)情況下校準(zhǔn)發(fā)生在層融合之后,不推薦使用。
hb_mapper/hb_compile 工具支持送入校準(zhǔn)數(shù)據(jù)(cal_data_dir),且在模型轉(zhuǎn)換編譯過程中選擇校準(zhǔn)方法(cal_data_type)。與 NVDIA 不同的是,地平線校準(zhǔn)數(shù)據(jù)僅是滿足模型輸入的圖像/數(shù)據(jù)文件,并不包含校準(zhǔn)信息。
hb_mapper/hb_compile 工具本身不提供校準(zhǔn)數(shù)據(jù)的生成功能,需要事先通過其他方式(例如使用 numpy)生成,不包含用于量化的統(tǒng)計信息。
地平線校準(zhǔn)方法(校準(zhǔn)發(fā)生在融合之后)
default default 是一個自動搜索的策略,會嘗試從系列校準(zhǔn)量化參數(shù)中獲得一個相對效果較好的組合。
mix mix 是一個集成多種校準(zhǔn)方法的搜索策略,能夠自動確定量化敏感節(jié)點,并在節(jié)點粒度上從不同的校準(zhǔn)方法中挑選出最佳方法, 最終構(gòu)建一個融合了多種校準(zhǔn)方法優(yōu)勢的組合校準(zhǔn)方式。
kl KL 校準(zhǔn)方法是借鑒了 TensorRT 提出的解決方案 , 使用 KL 熵值來遍歷每個量化層的數(shù)據(jù)分布,通過尋找最低的 KL 熵值,來確定閾值。 這種方法會導(dǎo)致較多的數(shù)據(jù)飽和和更小的數(shù)據(jù)量化粒度,在一些數(shù)據(jù)分布比較集中的模型中擁有著比 max 校準(zhǔn)方法更好的效果。
max max 校準(zhǔn)方法是在校準(zhǔn)過程中,自動選擇量化層中的最大值作為閾值。 這種方法會導(dǎo)致數(shù)據(jù)量化粒度較大,但也會帶來比 KL 方法更少的飽和點數(shù)量,適用于那些數(shù)據(jù)分布比較離散的神經(jīng)網(wǎng)絡(luò)模型。
--saveEngine # 保存序列化后的引擎文件,常見后綴名有 model.engine,model.trt
類似于 hb_mapper/hb_compile 中如下參數(shù):
working_dir # 模型轉(zhuǎn)換輸出結(jié)果的存放目錄
output_model_file_prefix # 指定轉(zhuǎn)換產(chǎn)出物名稱前綴
--timingCacheFile=<file> # 保存/加載序列化的 global timing cache,減少構(gòu)建時間
hb_mapper/hb_compile 默認(rèn)啟用 cache 功能
通過時序緩存保存構(gòu)建階段的 Layer 分析信息(特定于目標(biāo)設(shè)備、CUDA 版本、TensorRT 版本),如果有其他層具備相同的輸入/輸出張量配合和層參數(shù),則 TensorRT 構(gòu)建器會跳過分析并重用緩存結(jié)果。
--builderOptimizationLevel # 構(gòu)建時編譯優(yōu)化等級,范圍是0~5,默認(rèn)是3 # Higher level allows TensorRT to spend more building time for more optimization options. # 日志中默認(rèn)時該參數(shù)顯示-1,其他整數(shù)也都可以配置,從官方手冊看,3比較穩(wěn)定,性能也ok,4和5可能會構(gòu)建失敗。
J5 hb_mapper 相關(guān)參數(shù):optimize_level,范圍是 O0~O3,默認(rèn)是 O0,耗時評測時需要配置為 O3
J6 hb_compile 相關(guān)參數(shù):optimize_level,范圍是 O0~O2,默認(rèn)是 O0,耗時評測時需要配置為 O2
下圖是 trtexec 關(guān)于優(yōu)化等級對于構(gòu)建 build 耗時與延遲 latency 耗時,hb_mapper/hb_compile 也是類似的情況。
--versionCompatible # 標(biāo)記engine是軟件版本兼容的(相同OS、只支持顯式batch)
hb_mapper/hb_compile 無相關(guān)參數(shù),默認(rèn)兼容,跨多個版本時可能會存在不兼容的情況
--hardwareCompatibilityLevel=mode # 生成的engine模型可以兼容其他的GPU架構(gòu) (default = none) # 硬件兼容等級: mode ::= "none" | "ampere+" # none = no compatibility # ampere+ = compatible with Ampere and newer GPUs
hb_mapper/hb_compile 無相關(guān)參數(shù),通過 march 指定架構(gòu)即可,例如 征程 5 的 bayes、征程 6 的 nash-e/m 等
--maxAuxStreams=N # Set maximum number of auxiliary streams per inference stream that TRT is allowed to use to run kernels in parallel if the network contains ops that can run in parallel, with the cost of more memory usage. # 指定 TensorRT 在構(gòu)建引擎時可以使用的最大輔助流(auxiliary streams)數(shù)量 # 輔助流是 CUDA 流的一種,用于在網(wǎng)絡(luò)中的不同層之間實現(xiàn)并行計算,特別是某些層可以獨立執(zhí)行時,可能提高整體的推理性能。 # 默認(rèn)根據(jù)網(wǎng)絡(luò)的結(jié)構(gòu)和可用的 GPU 資源自動決定使用多少輔助流。 # 使用輔助流可能會增加 GPU 內(nèi)存的使用,因為每個流都需要自己的內(nèi)存副本。
hb_mapper/hb_compile 相關(guān)參數(shù):
compile_mode # 編譯策略選擇
balance_factor # balance 編譯策略時的比例系數(shù)
2.2 模型推理(評測) Inference Optionsstream 是 CUDA 中為了實現(xiàn)多個 kernel 同時在 GPU 上運行,實現(xiàn)對 GPU 資源劃分,利用流水線的方法提高 GPU 吞吐率的機(jī)制。 并行化操作,地平線會在模型編譯時由編譯器完成。
trtexec 工具常用參數(shù)與 hrt_model_exec 工具(J5、J6 都有這個工具)對比
--loadEngine=<file> # 加載序列化后的引擎文件
hrt_model_exec 相關(guān)參數(shù):
model_file # 模型文件路徑
model_name # 指定模型中某個模型的名稱,針對打包模型,用的較少
--shapes=spec # 針對動態(tài)shape,推理時使用的shape # 多輸入時示例 --shapes=input0:1x3x256x256, input1:1x3x128x128
征程 5 尚不支持動態(tài) shape、征程 6 待呈現(xiàn)
--loadInputs=spec # 加載輸入數(shù)據(jù),默認(rèn)使用隨機(jī)值,主要用于debug engine推理結(jié)果是否和 pytorch 一致 # spec ::= name:file # 輸入的 binary 通過 numpy 導(dǎo)出即可
hrt_model_exec 相關(guān)參數(shù):input_file
--iterations=N # 運行最少 N 次推理 ,default = 10
hrt_model_exec 相關(guān)參數(shù):frame_count
--warmUp=N # 性能測試時執(zhí)行 N 毫秒的 warmup,default = 200
hrt_model_exec 不支持 warmup,采用多幀推理獲取平均值的方式,可以很大程度上規(guī)避第一幀多出來的耗時
--duration=N # 最少運行 N 秒 (default = 3),配置為-1會一直執(zhí)行
hrt_model_exec 相關(guān)參數(shù):perf_time
--sleepTime=N # 推理前延遲 N 毫秒(between launch and compute),默認(rèn)N=0 --idleTime=N # 兩次連續(xù)推理之間空閑 N 毫秒,默認(rèn)N=0
hrt_model_exec 無相關(guān)參數(shù)
--infStreams=N # Instantiate實例化N個engine,測試多流執(zhí)行時是否提速 (default = 1),以前是--streams
J5 hrt_model_exec 無相關(guān)參數(shù),沒有多流的概念
在 CUDA 中,流(stream)是一種執(zhí)行模型,它允許開發(fā)者將多個計算任務(wù)(如內(nèi)核執(zhí)行、內(nèi)存拷貝等)組織成隊列,由 GPU 異步執(zhí)行。使用多個流可以提高 GPU 利用率,因為當(dāng)一個流的任務(wù)等待內(nèi)存拷貝或其他非計算密集型操作時,GPU 可以切換到另一個流執(zhí)行計算密集型任務(wù)。infStreams 與 CUDA 流的概念直接相關(guān),它影響的是模型推理任務(wù)在 GPU 上的并行執(zhí)行。 maxAuxStreams 是 TensorRT 內(nèi)部用于優(yōu)化網(wǎng)絡(luò)層執(zhí)行的機(jī)制,它允許 TensorRT 在內(nèi)部使用多個流來并行化可以并行化的層。 兩者之間的關(guān)系在于它們都旨在通過并行化策略來提高 GPU 上的推理性能,但它們作用的層面和具體實現(xiàn)方式不同。
--exposeDMA # Serialize DMA transfers to and from device (default = disabled) # 默認(rèn)動態(tài)內(nèi)存分配:TensorRT 會在執(zhí)行推理時動態(tài)地分配和釋放內(nèi)存,用于存儲中間層的激活值等。 # DMA:直接內(nèi)存訪問,允許硬件設(shè)備直接在內(nèi)存中讀寫數(shù)據(jù),繞過CPU,從而減少CPU負(fù)載和提高數(shù)據(jù)傳輸效率。 # 在某些情況下,使用 DMA 可能會增加程序的復(fù)雜性,因為它需要正確管理內(nèi)存的分配和釋放
hrt_model_exec 無相關(guān)參數(shù)
地平線 CPU 和 BPU 是共享內(nèi)存的
--noDataTransfers # Disable DMA transfers to and from device (default = enabled) # 勿將數(shù)據(jù)傳入和傳出設(shè)備,用于什么場景呢? # 猜測:禁用數(shù)據(jù)在主機(jī)和設(shè)備之間的傳輸,方便分析模型計算部分耗時?沒有H2D/D2H(Host/Device)數(shù)據(jù)傳輸可能會存在GPU利用率
hrt_model_exec 無相關(guān)參數(shù)
--useManagedMemory # Use managed memory instead of separate host and device allocations (default = disabled). # 猜測:使用托管內(nèi)存(Managed Memory),允許 CPU 和 GPU 同時訪問而不需要顯式的數(shù)據(jù)傳輸,提高數(shù)據(jù)共享的效率
hrt_model_exec 無相關(guān)參數(shù)
--useSpinWait # 主動同步 GPU 事件。 此選項可能會減少同步時間,但會增加 CPU 使用率和功率(default = disabled)
hrt_model_exec 無相關(guān)參數(shù)
啟用這個參數(shù)時,TensorRT 在等待 GPU 計算完成時使用自旋等待(spin wait)策略,而不是阻塞等待(block wait)。
阻塞等待:在默認(rèn)情況下,當(dāng) TensorRT 引擎執(zhí)行推理任務(wù)時,如果 GPU 計算尚未完成,它會掛起(阻塞)當(dāng)前線程,直到 GPU 計算完成并返回結(jié)果。這種等待方式可能會導(dǎo)致線程在等待期間不執(zhí)行任何操作,從而影響整體的 CPU 利用率和系統(tǒng)性能。
自旋等待:啟用 --useSpinWait 參數(shù)后,TensorRT 會采用自旋等待策略。在這種模式下,線程會循環(huán)檢查 GPU 計算是否完成,而不是掛起。自旋等待可以減少線程掛起和恢復(fù)的開銷,從而在某些情況下,例如 GPU 計算時間與 CPU 處理時間相比 較短的情況下。通過減少線程掛起的頻率,可以提高 CPU 的利用率,從而可能提升整體的系統(tǒng)性能。
GPU 計算時間不穩(wěn)定或較短時,自旋等待可以減少線程上下文切換的開銷,并保持 CPU 核心的活躍狀態(tài)。然而,自旋等待也可能導(dǎo)致 CPU 資源的過度使用,特別是在 GPU 計算時間較長的情況下,因此需要根據(jù)具體的應(yīng)用場景和硬件配置來權(quán)衡是否使用這個參數(shù)。
--threads # 啟用多線程以驅(qū)動具有獨立線程的引擎 or 加速refitting (default = disabled)
hrt_model_exec 相關(guān)參數(shù):thread_num
"stream(流)"和"thread(線程)"是兩個不同的概念,用于處理并發(fā)和數(shù)據(jù)流的情況。
線程(Thread): 線程是計算機(jī)程序中執(zhí)行的最小單位,也是進(jìn)程的一部分。一個進(jìn)程可以包含多個線程,它們共享進(jìn)程的資源,如內(nèi)存空間、文件句柄等。線程可以并行執(zhí)行,使得程序能夠同時處理多個任務(wù)。線程之間可以共享數(shù)據(jù),但也需要考慮同步和互斥問題,以避免競爭條件和數(shù)據(jù)損壞。
流(Stream): 流是一種數(shù)據(jù)傳輸?shù)某橄蟾拍?,通常用于輸入和輸出操作。在計算機(jī)編程中,流用于處理數(shù)據(jù)的連續(xù)流動,如文件讀寫、網(wǎng)絡(luò)通信等。流可以是字節(jié)流(以字節(jié)為單位處理數(shù)據(jù))或字符流(以字符為單位處理數(shù)據(jù))。流的一個常見特性是按順序處理數(shù)據(jù),不需要一次性將所有數(shù)據(jù)加載到內(nèi)存中。 總之,線程是一種用于實現(xiàn)并發(fā)執(zhí)行的機(jī)制,而流是一種用于處理數(shù)據(jù)傳輸?shù)某橄蟾拍睢?/span>
--useCudaGraph # Use CUDA graph to capture engine execution and then launch inference (default = disabled)
hrt_model_exec 無相關(guān)參數(shù)
useCudaGraph 參數(shù)允許用戶指示 TensorRT 在執(zhí)行推理時使用 CUDA 圖(CUDA Graph)。CUDA 圖是一種 CUDA 編程技術(shù),它允許開發(fā)者創(chuàng)建一個或多個 CUDA 內(nèi)核及其內(nèi)存依賴關(guān)系的靜態(tài)表示,這可以提高執(zhí)行效率和性能。 CUDA 圖的優(yōu)勢
性能提升:通過使用 CUDA 圖,可以減少運行時的開銷,因為它們允許預(yù)編譯一組 CUDA 操作,從而減少每次執(zhí)行操作時的啟動延遲。
重用性:一旦創(chuàng)建了 CUDA 圖,它可以被重用于多個推理請求,這使得它特別適合于高吞吐量和低延遲的應(yīng)用場景。
并行化:CUDA 圖可以并行執(zhí)行多個節(jié)點,這有助于提高 GPU 的利用率和整體的推理性能。 使用場景
高并發(fā)推理:在需要處理大量并發(fā)推理請求的場景中,使用 --useCudaGraph 可以提高處理速度和效率
--timeDeserialize # 測量序列化引擎文件(.engine)的反序列化時間
hrt_model_exec 會在終端中打印 加載 板端模型 的時間
反序列化時間:–timeDeserialize 參數(shù)會讓 trtexec 測量將序列化的 TensorRT 引擎文件加載到 GPU 內(nèi)存中所需的時間。
性能分析:通過測量反序列化時間,開發(fā)者可以了解模型加載階段的性能瓶頸,并探索減少模型加載時間的方法。
--timeRefit # Time the amount of time it takes to refit the engine before inference.
hrt_model_exec 無相關(guān)參數(shù)
猜測:重新適配(refitting)是指在模型轉(zhuǎn)換為 TensorRT 引擎后,根據(jù)新的權(quán)重或校準(zhǔn)數(shù)據(jù)更新引擎的過程,比如將模型的權(quán)重從一種精度轉(zhuǎn)換為另一種精度,或者根據(jù)新的校準(zhǔn)數(shù)據(jù)調(diào)整量化參數(shù)。
--separateProfileRun # 控制性能分析和推理測試的執(zhí)行方式,配置它時,二者會分開進(jìn)行(兩次)
類似于 hb_mapper/hb_compile 中 debug 參數(shù),debug 默認(rèn)配置為 True,編譯后會在 html 靜態(tài)性能評估文件中增加逐層的信息打印,可以幫助分析性能瓶頸。該參數(shù)開啟后不會影響模型的推理性能,但會極少量地增加模型文件大小。
trtexec 使用該參數(shù),一次用于收集性能分析數(shù)據(jù)的運行,另一次用于計算性能基準(zhǔn)測試的運行,提高分析/測試的準(zhǔn)確性。
--skipInference # 只構(gòu)建engine,不推理engine進(jìn)行性能測試(default = disabled),以前是--buildOnly
地平線 模型構(gòu)建與模型推理/性能評測是分開的,無相關(guān)參數(shù)
--persistentCacheRatio # Set the persistentCacheLimit in ratio, 0.5 represent half of max persistent L2 size,默認(rèn)是0
地平線無相關(guān)參數(shù)
2.3 報告選項 Reporting Options
緩存管理:–persistentCacheRatio 參數(shù)用于控制 TensorRT 引擎在執(zhí)行推理時分配給持久化緩存的內(nèi)存比例
性能優(yōu)化:合理設(shè)置緩存比例可以提高模型的推理性能,尤其是在處理大型模型或復(fù)雜網(wǎng)絡(luò)結(jié)構(gòu)時
內(nèi)存使用:增加持久化緩存的比例可能會減少內(nèi)存占用,但也可能導(dǎo)致緩存溢出
TensorRT 會自動管理緩存,因此手動設(shè)置–persistentCacheRatio 不是必須的。只有需要精細(xì)控制內(nèi)存使用或優(yōu)化性能時才會用到
--verbose # 使用詳細(xì)的日志輸出信息(default = false)
地平線無相關(guān)參數(shù)
日志中增加很多信息,類似于:[08/09/2024-17:18:51] [V] [TRT] Registered plugin creator - ::BatchedNMSDynamic_TRT version 1
--avgRuns=N # 指定在性能測試中連續(xù)執(zhí)行推理的次數(shù),以計算平均性能指標(biāo)(default = 10)
類似于 hrt_model_exec 中 frame_count 參數(shù)
為了減少偶然因素對性能測試結(jié)果的影響,通過多次運行推理并取平均值來提供一個更加穩(wěn)定和可靠的性能度量。
--percentile=P1,P2,P3,... # 指定在性能測試中報告的執(zhí)行時間百分比,0<=P_i<=100 (default = 90,95,99%)
hrt_model_exec 中無相關(guān)參數(shù)
設(shè)置 --percentile=99,trtexec 將會報告第 99 百分位的執(zhí)行時間,這意味著在 100 次推理中,有 99 次的執(zhí)行時間會小于或等于報告的值,而只有 1 次的執(zhí)行時間會大于這個值。故:0 representing max perf, and 100 representing min perf
--dumpRefit # Print the refittable layers and weights from a refittable engine
hrt_model_exec 中無相關(guān)參數(shù)
--dumpLayerInfo # 打印 engine 的每層信息v (default = disabled) --exportLayerInfo=<file> # 將 engine 的 layer 打印信息存儲下來,xxx.json (default = disabled) # Example: --exportLayerInfo=layer.json --profilingVerbosity=detailed
hb_mapper/hb_compile 默認(rèn)會在日志中打印層的信息
--dumpProfile # 打印每一層的 profile 信息 (default = disabled) --exportProfile=<file> # 將 profile 打印信息存儲下來,xxx.json (default = disabled)
hb_mapper/hb_compile 默認(rèn)開啟 debug 參數(shù)后,會在轉(zhuǎn)換編譯過程中生成 html 文件,其中有類似的層耗時信息
hrt_model_exec 工具中 profile_path 參數(shù)
--dumpOutput 將推理結(jié)果直接打印出來 (default = disabled) --dumpRawBindingsToFile 將 input/output tensor(s) of the last inference iteration to file(default = disabled) --exportOutput=<file> 將 ouput 打印信息存儲下來,xxx.json (default = disabled) --exportProfile=<file> 將 profile 打印信息存儲下來,xxx.json (default = disabled)
類似于 J5 hrt_model_exec 中 dump_intermediate、enable_dump、dump_format 等
--exportTimes=<file> # 將各個層的執(zhí)行時間存儲下來 (default = disabled)
hrt_model_exec 無相關(guān)參數(shù)
--device=N # Select cuda device N (default = 0),選擇執(zhí)行的GPU --useDLACore=N # Select DLA core N for layers that support DLA (default = none),使用較少
類似于 hrt_model_exec 中 core_id 參數(shù)
# 加載插件,實現(xiàn)自定義算子的編譯工作,區(qū)分動/靜態(tài)插件,替代以前的--plugins --staticPlugins Plugin library (.so) to load statically (can be specified multiple times) --dynamicPlugins Plugin library (.so) to load dynamically and may be serialized with the engine if they are included in --setPluginsToSerialize (can be specified multiple times) # 允許將插件序列化進(jìn)engine中,結(jié)合--dynamicPlugins參數(shù)使用 --setPluginsToSerialize Plugin library (.so) to be serialized with the engine (can be specified multiple times)
類似于 J5 hb_mapper 中 custom_op_method、op_register_files、custom_op_dir 參數(shù)
J6 hb_compile 待確定
看到這兒,trtexec 大部分參數(shù)就介紹完成了,還有少量不常用到參數(shù),例如–minTiming、–avgTiming=M、–tempdir、–tempfileControls、–useRuntime=runtime、–leanDLLPath=<file>、–excludeLeanRuntime、–ignoreParsedPluginLibs 未進(jìn)行介紹,歡迎大家自行探索。
各家的工具都會針對自己的硬件或特性設(shè)計針對性的參數(shù),只要滿足開發(fā)者需要的功能即可,例如地平線工具鏈的一些參數(shù),有一些就沒介紹到。
這么多參數(shù)其實并不是都會用到,大家根據(jù)自己的需求選擇性使用即可。
3.實操演示3.1 onnx 模型生成import torch.nn as nn import torch import numpy as np import onnxruntime class MyNet(nn.Module): def __init__(self, num_classes=10): super(MyNet, self).__init__() self.features = nn.Sequential( nn.Conv2d(3, 32, kernel_size=3, stride=1, padding=1), # input[3, 28, 28] output[32, 28, 28] nn.BatchNorm2d(32), nn.ReLU(inplace=True), nn.Conv2d(32, 64, kernel_size=3, stride=2, padding=1), # output[64, 14, 14] nn.BatchNorm2d(64), nn.ReLU(inplace=True), nn.MaxPool2d(kernel_size=2) # output[64, 7, 7] ) self.fc = nn.Linear(64 * 7 * 7, num_classes) def forward(self, x): x = self.features(x) x = torch.flatten(x, start_dim=1) x = self.fc(x) return x # -----------------------------------# # 導(dǎo)出靜態(tài)ONNX # -----------------------------------# def model_convert_static_onnx(model, dummy_input, output_path): input_names = ["input1"] # 導(dǎo)出的ONNX模型輸入節(jié)點名稱 output_names = ["output1"] # 導(dǎo)出的ONNX模型輸出節(jié)點名稱 torch.onnx.export( model, dummy_input, output_path, verbose=False, # 如果指定為True,在導(dǎo)出的ONNX中會有詳細(xì)的導(dǎo)出過程信息description opset_version=11, # J5目前僅支持為 10 or 11 input_names=input_names, output_names=output_names, ) # -------------------------------------------------------------------------# # 導(dǎo)出動態(tài)ONNX # dynamic_axes 參數(shù)中可以只指定輸入動態(tài),因為輸出維度會根據(jù)模型的結(jié)構(gòu)自動推出來。 # 一般場景,只做 batch 維度動態(tài) # -------------------------------------------------------------------------# def model_convert_dynamic_onnx(model, dummy_input, output_path): input_names = ["input1"] # 導(dǎo)出的ONNX模型輸入節(jié)點名稱 output_names = ["output1"] # 導(dǎo)出的ONNX模型輸出節(jié)點名稱 torch.onnx.export( model, dummy_input, output_path, verbose=False, # 如果指定為True,在導(dǎo)出的ONNX中會有詳細(xì)的導(dǎo)出過程信息description opset_version=11, # J5目前僅支持為 10 or 11 input_names=input_names, output_names=output_names, dynamic_axes={"input1": {0: "batch"}, "output1":{0: "batch"}} ) if __name__ == '__main__': torch.manual_seed(1) model = MyNet() # 將模型轉(zhuǎn)成 eval 模式 model.eval() # 網(wǎng)絡(luò)輸入 input_shape = (28, 28) dummy_input = torch.randn(1, 3, input_shape[0], input_shape[1]) # torch推理 with torch.no_grad(): torch_output = model(dummy_input) print("torch_output:", torch_output) # 導(dǎo)出靜態(tài)ONNX模型 output_static_path = './static.onnx' model_convert_static_onnx(model, dummy_input, output_static_path) print("model export static onnx finsh.") static_sess = onnxruntime.InferenceSession(output_static_path) static_output = static_sess.run(None, {"input1": dummy_input.numpy()}) print("static_output: ", static_output)
上述代碼運行后,會生成一個 static.onnx,接下來就可以使用這個 onnx 啦。
3.2 性能評測實測實操的方向不同,使用的命令和腳本也會有差異,本文重點在對比兩家工具鏈的 PTQ 功能參數(shù)對比介紹上,因此只選擇一個性能評測方向進(jìn)行實操演示。
英偉達(dá) trtexec
構(gòu)建用于性能評測的 engine,另外性能數(shù)據(jù)可以一起產(chǎn)出,腳本如下:
trtexec \ --onnx=static.onnx \ --saveEngine=static.engine \ --useCudaGraph \ --noDataTransfers \ --useSpinWait \ --infStreams=8 \ --maxAuxStreams=8 \ --builderOptimizationLevel=5 \ --threads \ --best \ --verbose \ --profilingVerbosity=detailed \ --dumpProfile \ --dumpLayerInfo \ --separateProfileRun \ --avgRuns=100 \ --iterations=1000 >1.log 2>&1
會產(chǎn)出 engine 文件:resnet18.engine,以及一些日志,例如:
[08/09/2024-14:11:17] [I] === Performance summary === [08/09/2024-14:11:17] [I] Throughput: 21241.3 qps [08/09/2024-14:11:17] [I] Latency: min = 0.0292969 ms, max = 4.11438 ms, mean = 0.036173 ms, median = 0.03479 ms, percentile(90%) = 0.0389404 ms, percentile(95%) = 0.0422974 ms, percentile(99%) = 0.0679932 ms [08/09/2024-14:11:17] [I] Enqueue Time: min = 0.0141602 ms, max = 4.099 ms, mean = 0.0175454 ms, median = 0.017334 ms, percentile(90%) = 0.0184326 ms, percentile(95%) = 0.0209961 ms, percentile(99%) = 0.0261841 ms [08/09/2024-14:11:17] [I] H2D Latency: min = 0.00366211 ms, max = 4.05176 ms, mean = 0.0064459 ms, median = 0.00561523 ms, percentile(90%) = 0.00682068 ms, percentile(95%) = 0.00720215 ms, percentile(99%) = 0.0361328 ms [08/09/2024-14:11:17] [I] GPU Compute Time: min = 0.0214844 ms, max = 4.10327 ms, mean = 0.024971 ms, median = 0.0244141 ms, percentile(90%) = 0.0274658 ms, percentile(95%) = 0.0285645 ms, percentile(99%) = 0.0317383 ms [08/09/2024-14:11:17] [I] D2H Latency: min = 0.00268555 ms, max = 0.0428467 ms, mean = 0.0047562 ms, median = 0.00415039 ms, percentile(90%) = 0.0065918 ms, percentile(95%) = 0.00695801 ms, percentile(99%) = 0.0113525 ms [08/09/2024-14:11:17] [I] Total Host Walltime: 3.00005 s [08/09/2024-14:11:17] [I] Total GPU Compute Time: 1.59128 s
地平線 hb_compile 與 hrt_model_exec
轉(zhuǎn)換編譯用于性能評測的 hbm
hb_compile --fast-perf --model static.onnx --march nash-m
會生成 static.hbm,以及一些日志
在板端評測性能數(shù)據(jù)
hrt_model_exec perf --model_file static.hbm --thread_num 8 --frame_count 400 --internal_use
*博客內(nèi)容為網(wǎng)友個人發(fā)布,僅代表博主個人觀點,如有侵權(quán)請聯(lián)系工作人員刪除。