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