博客專欄

EEPW首頁(yè) > 博客 > 地平線與英偉達(dá)工具鏈 PTQ 工具功能參數(shù)對(duì)比與實(shí)操

地平線與英偉達(dá)工具鏈 PTQ 工具功能參數(shù)對(duì)比與實(shí)操

發(fā)布人:地平線開發(fā)者 時(shí)間:2024-10-27 來源:工程師 發(fā)布文章
1.理論簡(jiǎn)介

在閱讀本文之前,希望大家對(duì) PTQ(Post-Training Quantization) 訓(xùn)練后量化有一定的了解~

地平線 OpenExplorer 和 NVIDIA TensorRT 是兩家公司為適配自己的硬件而開發(fā)的算法工具鏈,它們各自具有獨(dú)特的特點(diǎn)和優(yōu)勢(shì)。分開看的時(shí)候,網(wǎng)上有很多資料,但卻沒找到將他們放在一起對(duì)比的文章,本文從 PTQ 通路進(jìn)行對(duì)比介紹。

  • OpenExplorer 中文名為天工開物,是地平線發(fā)布的算法開發(fā)平臺(tái),主要包括模型編譯優(yōu)化工具集、算法倉(cāng)庫(kù)和應(yīng)用開發(fā) SDK 三大功能模塊。

  • TensorRT 是 NVIDIA 高性能深度學(xué)習(xí)的推理 SDK,包含深度學(xué)習(xí)推理優(yōu)化器和運(yùn)行時(shí)環(huán)境,可加速深度學(xué)習(xí)推理應(yīng)用。利用 Pytorch、TensorFlow 等 DL 框架訓(xùn)練好的模型,通過模型轉(zhuǎn)換編譯生成可以在各家硬件上運(yùn)行的格式(TensorRT xxx.engine/xxx.trt 或地平線 xxx.hbm/xxx.bin),提升這個(gè)模型在各家硬件(英偉達(dá) GPU、地平線 BPU)上運(yùn)行的速度。

為了讓深度學(xué)習(xí)模型高效的在自家硬件上運(yùn)行起來,他們都會(huì)做很多優(yōu)化,包括但不限于 量化、數(shù)據(jù)壓縮、算子替換、算子拆分、算子融合等等優(yōu)化措施,下面以兩家 show 出來的算子融合為例來看一下:

  • 英偉達(dá)宣傳材料中的圖片

英偉達(dá)

  • 地平線宣傳材料中的圖片

地平線

量化方案:兩家都是對(duì)稱均勻量化,weight 是 per channel,feature 為 per tensor,地平線在有限條件下支持 feature per channel 量化

  • TensorRT 在進(jìn)行 PTQ 量化(默認(rèn)進(jìn)行 fp32 不量化) 時(shí),會(huì)在優(yōu)化網(wǎng)絡(luò)的時(shí)候嘗試 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,對(duì)應(yīng)的 onnx 算子:https://github.com/onnx/onnx-tensorrt/blob/main/docs/operators.md

  • 地平線 OpenExplorer 默認(rèn)使用 int8 量化,部分節(jié)點(diǎn) BPU 支持 int16、int32,可以通過 node_info、run_on_bpu、run_on_cpu 參數(shù)控制量化精度以及運(yùn)行的器件。

硬件相關(guān)

  • TensorRT 的核心在于對(duì)模型算子的優(yōu)化(算子合并、量化、利用 GPU 特性選擇特定核函數(shù)等策略),通過 tensorRT 能夠在 NVIDIA 系列 GPU 上獲得最好的性能,因此 tensorRT 的模型,需要在目標(biāo) GPU 上實(shí)際運(yùn)行的方式選擇最優(yōu)算法和配置,也就是說 tensorRT 生成的模型只能在特定條件下運(yùn)行(編譯的 trt 版本、cuda 版本、編譯時(shí)的 GPU 型號(hào)),不同硬件之間的優(yōu)化是不能共享的。但是從 tensorrt8.6 開始,–hardwareCompatibilityLevel 參數(shù)可以允許用戶在不同的架構(gòu)上構(gòu)建和運(yùn)行模型,可能會(huì)帶來一些性能損失(視情況而定,5%左右,若某個(gè)大的優(yōu)化只支持特定架構(gòu),性能損失會(huì)比較大);

  • OpenExplorer 進(jìn)行 PTQ 量化時(shí)需要指定參數(shù) march,指定產(chǎn)出混合異構(gòu)模型需要支持的平臺(tái)架構(gòu),針對(duì)不同硬件,地平線提供的 OpenExplorer 是不同的,當(dāng)然,本質(zhì)上用到的幾個(gè) whl 包是相同的。

2.參數(shù)對(duì)比解讀
  • NVIDIA

    • trtexec 工具提供的參數(shù)總體上可以分為:Model Options、Build Options、Inference Options、Reporting Options、System Options,最常用到的是前三個(gè);


  • Horizon 征程 5

    • hb_mapper 工具提供的參數(shù)總體上可以分為:模型參數(shù)組、輸入信息參數(shù)組、校準(zhǔn)參數(shù)組、編譯參數(shù)組、自定義算子參數(shù)組;

    • hrt_model_exec 工具提供模型信息查看、模型推理、模型性能評(píng)測(cè)三組參數(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 工具類似功能的。


2.1 模型轉(zhuǎn)換編譯(構(gòu)建)

trtexec 工具常用參數(shù)與 J5 hb_mapper/J6 hb_compile 工具對(duì)比

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)容

2.1.2 Build Options
--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)輸入形狀的范圍常見值
--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è)參數(shù)均必須配置,可以配置為一樣的
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 對(duì)應(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     # 兩個(gè)輸入
-outputIOFormats=fp16:chw,fp16:chw     # 兩個(gè)輸出
  • J5 hb_mapper 相關(guān)參數(shù)有:

    • node_info # 配置節(jié)點(diǎn)輸入/輸出精度

    • input_type_rt # 板端模型輸入數(shù)據(jù)格式 nv12/rgb/featuremap 等

    • input_layout_rt # 板端輸入數(shù)據(jù)排布 NCHW/NHWC

    • 輸出排布和 onnx 保持一致

    • input_type_train # 原始浮點(diǎn)模型的輸入數(shù)據(jù)類型 rgb/bgr 等

    • input_layout_train # 原始浮點(diǎn)模型的輸入數(shù)據(jù)排布 NCHW/NHWC


  • J6 hb_compile 相比于 J5 hb_mapper 的差異:

    • 取消 input_layout_rt,板端輸入 layout 與原始浮點(diǎn)輸入數(shù)據(jù)排布相同

    • 增加 quant_config 參數(shù),支持對(duì)模型算子計(jì)算精度進(jìn)行精細(xì)化配置


NVIDIA 的 tensor core 和地平線的 BPU core 都是 HWC 排布的,HWC 數(shù)據(jù)排布是為了編譯優(yōu)化模型性能,雖然 CHW 和 HWC 排布均支持,但內(nèi)部會(huì)進(jìn)行轉(zhuǎn)換。 針對(duì)網(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)記生成的這個(gè)engine是refittable,即可以指定稍后更新其權(quán)重
  • hb_mapper 與 hb_compile 中無相關(guān)參數(shù)

允許用戶在運(yùn)行時(shí)重新適配(refit)TensorRT 引擎的權(quán)重。對(duì)于需要在推理過程中動(dòng)態(tài)更新模型權(quán)重的場(chǎng)景比較有用,例如在模型部署后需要根據(jù)新數(shù)據(jù)進(jìn)行微調(diào),強(qiáng)化學(xué)習(xí)中或在保留相同結(jié)構(gòu)的同時(shí)重新訓(xùn)練模型時(shí),權(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 中無對(duì)應(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é)點(diǎn)配置運(yùn)行在 CPU 上

  • J5 hb_mapper 支持 int8 和 int16 以及尾部 conv 節(jié)點(diǎn) int32 量化,可以通過 node_info 或 run_on_bpu 參數(shù)進(jìn)行配置

  • J6 hb_compile 通過 node_info 和 quant_config,支持對(duì)模型算子計(jì)算精度進(jìn)行精細(xì)化配置

TF32 是英偉達(dá)提出的代替 FP32 的單精度浮點(diǎn)格式,TF32 采用與半精度( FP16 )數(shù)學(xué)相同的 10 位尾數(shù)位精度,這樣的精度水平遠(yuǎn)高于 AI 工作負(fù)載的精度要求。同時(shí), TF32 采用與 FP32 相同的 8 位指數(shù)位,能夠支持與其相同的數(shù)字范圍。 注意:NV 不論配置哪一項(xiàng),fp32 都是會(huì)使用的。舉例:配置–fp16,網(wǎng)絡(luò)會(huì)使用 fp16+fp32;配置–int8 和–fp16,網(wǎng)絡(luò)會(huì)使用 int8+fp16+fp32

--best   # fp32+fp16+int8 同時(shí)使用,找一個(gè)速度最快的
  • 類似于 hb_mapper/hb_compile --fast-perf 功能,主要用于性能評(píng)測(cè)

--precisionConstraints=spec    # 精度限制spec ::= "none" | "obey" | "prefer",(default = none),聯(lián)合下面兩個(gè)參數(shù)使用
        # none = 無限制        
        # prefer = 優(yōu)先滿足--layerPrecisions/--layerOutputTypes設(shè)置的精度限制,不滿足可回退到默認(rèn)精度        
        # obey =   優(yōu)先滿足--layerPrecisions/--layerOutputTypes設(shè)置的精度限制,不滿足報(bào)錯(cuò)退出--layerPrecisions=spec  # 控制per-layer的運(yùn)算精度,僅在precisionConstraints為obey或prefer時(shí)有效. (default = none)        
        # spec::= layerName:precision        
        # precision::= "fp32"|"fp16"|"int32"|"int8"--layerOutputTypes=spec # 控制per-layer的輸出精度(類型),僅在precisionConstraints為obey或prefer時(shí)有效. (default = none)        
        # spec::= layerName:type        
        # type ::= "fp32"|"fp16"|"int32"|"int8"
  • 類似于 J5 hb_mapper run_on_bpu、run_on_cpu、node_info 三個(gè)參數(shù)的功能,支持對(duì)模型算子計(jì)算精度進(jìn)行精細(xì)化配置

  • 類似于 J6 hb_compile node_info 和 quant_config,支持對(duì)模型算子計(jì)算精度進(jìn)行精細(xì)化配置

--layerDeviceTypes=spec    # 指定layer運(yùn)行的器件
         # 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ù)存儲(chǔ)類型


下面針對(duì)校準(zhǔn)數(shù)據(jù)進(jìn)行一些更詳細(xì)的介紹:

trtexec 工具支持送入校準(zhǔn)數(shù)據(jù),在校準(zhǔn)過程中,可以使用 --calib 選項(xiàng)來指定一個(gè)包含校準(zhǔn)數(shù)據(jù)的文件。這個(gè)文件通常是一個(gè)緩存文件,包含了模型在特定輸入數(shù)據(jù)集上運(yùn)行時(shí)的激活值的統(tǒng)計(jì)信息。

trtexec --onnx=model.onnx --int8 --calib=calibrationCacheFile.cache

trtexec 工具本身不提供校準(zhǔn)數(shù)據(jù)的生成功能,需要事先通過其他方式(例如使用 TensorRT 的 API trt.IInt8EntropyCalibrator2)生成,包含了用于量化的統(tǒng)計(jì)信息。

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)器使用整個(gè)激活分布范圍來確定比例因子。推薦用于 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)計(jì)信息。

地平線校準(zhǔn)方法(校準(zhǔn)發(fā)生在融合之后)

  • default default 是一個(gè)自動(dòng)搜索的策略,會(huì)嘗試從系列校準(zhǔn)量化參數(shù)中獲得一個(gè)相對(duì)效果較好的組合。

  • mix mix 是一個(gè)集成多種校準(zhǔn)方法的搜索策略,能夠自動(dòng)確定量化敏感節(jié)點(diǎn),并在節(jié)點(diǎn)粒度上從不同的校準(zhǔn)方法中挑選出最佳方法, 最終構(gòu)建一個(gè)融合了多種校準(zhǔn)方法優(yōu)勢(shì)的組合校準(zhǔn)方式。

  • kl KL 校準(zhǔn)方法是借鑒了 TensorRT 提出的解決方案 , 使用 KL 熵值來遍歷每個(gè)量化層的數(shù)據(jù)分布,通過尋找最低的 KL 熵值,來確定閾值。 這種方法會(huì)導(dǎo)致較多的數(shù)據(jù)飽和和更小的數(shù)據(jù)量化粒度,在一些數(shù)據(jù)分布比較集中的模型中擁有著比 max 校準(zhǔn)方法更好的效果。

  • max max 校準(zhǔn)方法是在校準(zhǔn)過程中,自動(dòng)選擇量化層中的最大值作為閾值。 這種方法會(huì)導(dǎo)致數(shù)據(jù)量化粒度較大,但也會(huì)帶來比 KL 方法更少的飽和點(diǎn)數(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)建時(shí)間
  • hb_mapper/hb_compile 默認(rèn)啟用 cache 功能

通過時(shí)序緩存保存構(gòu)建階段的 Layer 分析信息(特定于目標(biāo)設(shè)備、CUDA 版本、TensorRT 版本),如果有其他層具備相同的輸入/輸出張量配合和層參數(shù),則 TensorRT 構(gòu)建器會(huì)跳過分析并重用緩存結(jié)果。

--builderOptimizationLevel      # 構(gòu)建時(shí)編譯優(yōu)化等級(jí),范圍是0~5,默認(rèn)是3
     # Higher level allows TensorRT to spend more building time for more optimization options.      # 日志中默認(rèn)時(shí)該參數(shù)顯示-1,其他整數(shù)也都可以配置,從官方手冊(cè)看,3比較穩(wěn)定,性能也ok,4和5可能會(huì)構(gòu)建失敗。
  • J5 hb_mapper 相關(guān)參數(shù):optimize_level,范圍是 O0~O3,默認(rèn)是 O0,耗時(shí)評(píng)測(cè)時(shí)需要配置為 O3

  • J6 hb_compile 相關(guān)參數(shù):optimize_level,范圍是 O0~O2,默認(rèn)是 O0,耗時(shí)評(píng)測(cè)時(shí)需要配置為 O2

下圖是 trtexec 關(guān)于優(yōu)化等級(jí)對(duì)于構(gòu)建 build 耗時(shí)與延遲 latency 耗時(shí),hb_mapper/hb_compile 也是類似的情況。


--versionCompatible    # 標(biāo)記engine是軟件版本兼容的(相同OS、只支持顯式batch)
  • hb_mapper/hb_compile 無相關(guān)參數(shù),默認(rèn)兼容,跨多個(gè)版本時(shí)可能會(huì)存在不兼容的情況

--hardwareCompatibilityLevel=mode  # 生成的engine模型可以兼容其他的GPU架構(gòu) (default = none)
                                    # 硬件兼容等級(jí): 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)建引擎時(shí)可以使用的最大輔助流(auxiliary streams)數(shù)量
        # 輔助流是 CUDA 流的一種,用于在網(wǎng)絡(luò)中的不同層之間實(shí)現(xiàn)并行計(jì)算,特別是某些層可以獨(dú)立執(zhí)行時(shí),可能提高整體的推理性能。        
        # 默認(rèn)根據(jù)網(wǎng)絡(luò)的結(jié)構(gòu)和可用的 GPU 資源自動(dòng)決定使用多少輔助流。        
        # 使用輔助流可能會(huì)增加 GPU 內(nèi)存的使用,因?yàn)槊總€(gè)流都需要自己的內(nèi)存副本。
  • hb_mapper/hb_compile 相關(guān)參數(shù):

    • compile_mode # 編譯策略選擇

    • balance_factor # balance 編譯策略時(shí)的比例系數(shù)

stream 是 CUDA 中為了實(shí)現(xiàn)多個(gè) kernel 同時(shí)在 GPU 上運(yùn)行,實(shí)現(xiàn)對(duì) GPU 資源劃分,利用流水線的方法提高 GPU 吞吐率的機(jī)制。 并行化操作,地平線會(huì)在模型編譯時(shí)由編譯器完成。

2.2 模型推理(評(píng)測(cè)) Inference Options

trtexec 工具常用參數(shù)與 hrt_model_exec 工具(J5、J6 都有這個(gè)工具)對(duì)比

--loadEngine=<file>        # 加載序列化后的引擎文件
  • hrt_model_exec 相關(guān)參數(shù):

    • model_file # 模型文件路徑

    • model_name # 指定模型中某個(gè)模型的名稱,針對(duì)打包模型,用的較少

--shapes=spec     # 針對(duì)動(dòng)態(tài)shape,推理時(shí)使用的shape
# 多輸入時(shí)示例
--shapes=input0:1x3x256x256, input1:1x3x128x128
  • 征程 5 尚不支持動(dòng)態(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      # 運(yùn)行最少 N 次推理 ,default = 10
  • hrt_model_exec 相關(guān)參數(shù):frame_count

--warmUp=N    # 性能測(cè)試時(shí)執(zhí)行 N 毫秒的 warmup,default = 200
  • hrt_model_exec 不支持 warmup,采用多幀推理獲取平均值的方式,可以很大程度上規(guī)避第一幀多出來的耗時(shí)

--duration=N    # 最少運(yùn)行 N 秒 (default = 3),配置為-1會(huì)一直執(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實(shí)例化N個(gè)engine,測(cè)試多流執(zhí)行時(shí)是否提速 (default = 1),以前是--streams
  • J5 hrt_model_exec 無相關(guān)參數(shù),沒有多流的概念

在 CUDA 中,流(stream)是一種執(zhí)行模型,它允許開發(fā)者將多個(gè)計(jì)算任務(wù)(如內(nèi)核執(zhí)行、內(nèi)存拷貝等)組織成隊(duì)列,由 GPU 異步執(zhí)行。使用多個(gè)流可以提高 GPU 利用率,因?yàn)楫?dāng)一個(gè)流的任務(wù)等待內(nèi)存拷貝或其他非計(jì)算密集型操作時(shí),GPU 可以切換到另一個(gè)流執(zhí)行計(jì)算密集型任務(wù)。infStreams 與 CUDA 流的概念直接相關(guān),它影響的是模型推理任務(wù)在 GPU 上的并行執(zhí)行。 maxAuxStreams 是 TensorRT 內(nèi)部用于優(yōu)化網(wǎng)絡(luò)層執(zhí)行的機(jī)制,它允許 TensorRT 在內(nèi)部使用多個(gè)流來并行化可以并行化的層。 兩者之間的關(guān)系在于它們都旨在通過并行化策略來提高 GPU 上的推理性能,但它們作用的層面和具體實(shí)現(xiàn)方式不同。

--exposeDMA    # Serialize DMA transfers to and from device (default = disabled)
    # 默認(rèn)動(dòng)態(tài)內(nèi)存分配:TensorRT 會(huì)在執(zhí)行推理時(shí)動(dòng)態(tài)地分配和釋放內(nèi)存,用于存儲(chǔ)中間層的激活值等。
    # DMA:直接內(nèi)存訪問,允許硬件設(shè)備直接在內(nèi)存中讀寫數(shù)據(jù),繞過CPU,從而減少CPU負(fù)載和提高數(shù)據(jù)傳輸效率。
    # 在某些情況下,使用 DMA 可能會(huì)增加程序的復(fù)雜性,因?yàn)樗枰_管理內(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è)備,用于什么場(chǎng)景呢?
    # 猜測(cè):禁用數(shù)據(jù)在主機(jī)和設(shè)備之間的傳輸,方便分析模型計(jì)算部分耗時(shí)?沒有H2D/D2H(Host/Device)數(shù)據(jù)傳輸可能會(huì)存在GPU利用率
  • hrt_model_exec 無相關(guān)參數(shù)

--useManagedMemory    # Use managed memory instead of separate host and device allocations (default = disabled).
    # 猜測(cè):使用托管內(nèi)存(Managed Memory),允許 CPU 和 GPU 同時(shí)訪問而不需要顯式的數(shù)據(jù)傳輸,提高數(shù)據(jù)共享的效率
  • hrt_model_exec 無相關(guān)參數(shù)

--useSpinWait    #  主動(dòng)同步 GPU 事件。 此選項(xiàng)可能會(huì)減少同步時(shí)間,但會(huì)增加 CPU 使用率和功率(default = disabled)
  • hrt_model_exec 無相關(guān)參數(shù)

啟用這個(gè)參數(shù)時(shí),TensorRT 在等待 GPU 計(jì)算完成時(shí)使用自旋等待(spin wait)策略,而不是阻塞等待(block wait)。

  • 阻塞等待:在默認(rèn)情況下,當(dāng) TensorRT 引擎執(zhí)行推理任務(wù)時(shí),如果 GPU 計(jì)算尚未完成,它會(huì)掛起(阻塞)當(dāng)前線程,直到 GPU 計(jì)算完成并返回結(jié)果。這種等待方式可能會(huì)導(dǎo)致線程在等待期間不執(zhí)行任何操作,從而影響整體的 CPU 利用率和系統(tǒng)性能。

  • 自旋等待:?jiǎn)⒂?--useSpinWait 參數(shù)后,TensorRT 會(huì)采用自旋等待策略。在這種模式下,線程會(huì)循環(huán)檢查 GPU 計(jì)算是否完成,而不是掛起。自旋等待可以減少線程掛起和恢復(fù)的開銷,從而在某些情況下,例如 GPU 計(jì)算時(shí)間與 CPU 處理時(shí)間相比 較短的情況下。通過減少線程掛起的頻率,可以提高 CPU 的利用率,從而可能提升整體的系統(tǒng)性能。

  • GPU 計(jì)算時(shí)間不穩(wěn)定或較短時(shí),自旋等待可以減少線程上下文切換的開銷,并保持 CPU 核心的活躍狀態(tài)。然而,自旋等待也可能導(dǎo)致 CPU 資源的過度使用,特別是在 GPU 計(jì)算時(shí)間較長(zhǎng)的情況下,因此需要根據(jù)具體的應(yīng)用場(chǎng)景和硬件配置來權(quán)衡是否使用這個(gè)參數(shù)。

--threads       # 啟用多線程以驅(qū)動(dòng)具有獨(dú)立線程的引擎 or 加速refitting  (default = disabled)
  • hrt_model_exec 相關(guān)參數(shù):thread_num

"stream(流)"和"thread(線程)"是兩個(gè)不同的概念,用于處理并發(fā)和數(shù)據(jù)流的情況。

  1. 線程(Thread): 線程是計(jì)算機(jī)程序中執(zhí)行的最小單位,也是進(jìn)程的一部分。一個(gè)進(jìn)程可以包含多個(gè)線程,它們共享進(jìn)程的資源,如內(nèi)存空間、文件句柄等。線程可以并行執(zhí)行,使得程序能夠同時(shí)處理多個(gè)任務(wù)。線程之間可以共享數(shù)據(jù),但也需要考慮同步和互斥問題,以避免競(jìng)爭(zhēng)條件和數(shù)據(jù)損壞。

  2. 流(Stream): 流是一種數(shù)據(jù)傳輸?shù)某橄蟾拍?,通常用于輸入和輸出操作。在?jì)算機(jī)編程中,流用于處理數(shù)據(jù)的連續(xù)流動(dòng),如文件讀寫、網(wǎng)絡(luò)通信等。流可以是字節(jié)流(以字節(jié)為單位處理數(shù)據(jù))或字符流(以字符為單位處理數(shù)據(jù))。流的一個(gè)常見特性是按順序處理數(shù)據(jù),不需要一次性將所有數(shù)據(jù)加載到內(nèi)存中。 總之,線程是一種用于實(shí)現(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í)行推理時(shí)使用 CUDA 圖(CUDA Graph)。CUDA 圖是一種 CUDA 編程技術(shù),它允許開發(fā)者創(chuàng)建一個(gè)或多個(gè) CUDA 內(nèi)核及其內(nèi)存依賴關(guān)系的靜態(tài)表示,這可以提高執(zhí)行效率和性能。 CUDA 圖的優(yōu)勢(shì)

  • 性能提升:通過使用 CUDA 圖,可以減少運(yùn)行時(shí)的開銷,因?yàn)樗鼈冊(cè)试S預(yù)編譯一組 CUDA 操作,從而減少每次執(zhí)行操作時(shí)的啟動(dòng)延遲。

  • 重用性:一旦創(chuàng)建了 CUDA 圖,它可以被重用于多個(gè)推理請(qǐng)求,這使得它特別適合于高吞吐量和低延遲的應(yīng)用場(chǎng)景。

  • 并行化:CUDA 圖可以并行執(zhí)行多個(gè)節(jié)點(diǎn),這有助于提高 GPU 的利用率和整體的推理性能。 使用場(chǎng)景

  • 高并發(fā)推理:在需要處理大量并發(fā)推理請(qǐng)求的場(chǎng)景中,使用 --useCudaGraph 可以提高處理速度和效率

  --timeDeserialize    # 測(cè)量序列化引擎文件(.engine)的反序列化時(shí)間
  • hrt_model_exec 會(huì)在終端中打印 加載 板端模型 的時(shí)間

  • 反序列化時(shí)間:–timeDeserialize 參數(shù)會(huì)讓 trtexec 測(cè)量將序列化的 TensorRT 引擎文件加載到 GPU 內(nèi)存中所需的時(shí)間。

  • 性能分析:通過測(cè)量反序列化時(shí)間,開發(fā)者可以了解模型加載階段的性能瓶頸,并探索減少模型加載時(shí)間的方法。

--timeRefit    # Time the amount of time it takes to refit the engine before inference.
  • hrt_model_exec 無相關(guān)參數(shù)

猜測(cè):重新適配(refitting)是指在模型轉(zhuǎn)換為 TensorRT 引擎后,根據(jù)新的權(quán)重或校準(zhǔn)數(shù)據(jù)更新引擎的過程,比如將模型的權(quán)重從一種精度轉(zhuǎn)換為另一種精度,或者根據(jù)新的校準(zhǔn)數(shù)據(jù)調(diào)整量化參數(shù)。

--separateProfileRun    # 控制性能分析和推理測(cè)試的執(zhí)行方式,配置它時(shí),二者會(huì)分開進(jìn)行(兩次)
  • 類似于 hb_mapper/hb_compile 中 debug 參數(shù),debug 默認(rèn)配置為 True,編譯后會(huì)在 html 靜態(tài)性能評(píng)估文件中增加逐層的信息打印,可以幫助分析性能瓶頸。該參數(shù)開啟后不會(huì)影響模型的推理性能,但會(huì)極少量地增加模型文件大小。

trtexec 使用該參數(shù),一次用于收集性能分析數(shù)據(jù)的運(yùn)行,另一次用于計(jì)算性能基準(zhǔn)測(cè)試的運(yùn)行,提高分析/測(cè)試的準(zhǔn)確性。

--skipInference    # 只構(gòu)建engine,不推理engine進(jìn)行性能測(cè)試(default = disabled),以前是--buildOnly
  • 地平線 模型構(gòu)建與模型推理/性能評(píng)測(cè)是分開的,無相關(guān)參數(shù)

--persistentCacheRatio # Set the persistentCacheLimit in ratio, 0.5 represent half of max persistent L2 size,默認(rèn)是0
  • 地平線無相關(guān)參數(shù)

  • 緩存管理:–persistentCacheRatio 參數(shù)用于控制 TensorRT 引擎在執(zhí)行推理時(shí)分配給持久化緩存的內(nèi)存比例

  • 性能優(yōu)化:合理設(shè)置緩存比例可以提高模型的推理性能,尤其是在處理大型模型或復(fù)雜網(wǎng)絡(luò)結(jié)構(gòu)時(shí)

  • 內(nèi)存使用:增加持久化緩存的比例可能會(huì)減少內(nèi)存占用,但也可能導(dǎo)致緩存溢出

  • TensorRT 會(huì)自動(dòng)管理緩存,因此手動(dòng)設(shè)置–persistentCacheRatio 不是必須的。只有需要精細(xì)控制內(nèi)存使用或優(yōu)化性能時(shí)才會(huì)用到

2.3 報(bào)告選項(xiàng) Reporting Options
--verbose    # 使用詳細(xì)的日志輸出信息(default = false)
  • 地平線無相關(guān)參數(shù)

日志中增加很多信息,類似于:[08/09/2024-17:18:51] [V] [TRT] Registered plugin creator - ::BatchedNMSDynamic_TRT version 1

--avgRuns=N    # 指定在性能測(cè)試中連續(xù)執(zhí)行推理的次數(shù),以計(jì)算平均性能指標(biāo)(default = 10)
  • 類似于 hrt_model_exec 中 frame_count 參數(shù)

為了減少偶然因素對(duì)性能測(cè)試結(jié)果的影響,通過多次運(yùn)行推理并取平均值來提供一個(gè)更加穩(wěn)定和可靠的性能度量。

--percentile=P1,P2,P3,...    # 指定在性能測(cè)試中報(bào)告的執(zhí)行時(shí)間百分比,0<=P_i<=100 (default = 90,95,99%)
  • hrt_model_exec 中無相關(guān)參數(shù)

設(shè)置 --percentile=99,trtexec 將會(huì)報(bào)告第 99 百分位的執(zhí)行時(shí)間,這意味著在 100 次推理中,有 99 次的執(zhí)行時(shí)間會(huì)小于或等于報(bào)告的值,而只有 1 次的執(zhí)行時(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 中無相關(guān)參數(shù)

--dumpLayerInfo             # 打印 engine 的每層信息v (default = disabled)
--exportLayerInfo=<file>    # 將 engine 的 layer 打印信息存儲(chǔ)下來,xxx.json (default = disabled)
 # Example: --exportLayerInfo=layer.json --profilingVerbosity=detailed
  • hb_mapper/hb_compile 默認(rèn)會(huì)在日志中打印層的信息

--dumpProfile               # 打印每一層的 profile 信息 (default = disabled)
--exportProfile=<file>      # 將 profile 打印信息存儲(chǔ)下來,xxx.json (default = disabled)
  • hb_mapper/hb_compile 默認(rèn)開啟 debug 參數(shù)后,會(huì)在轉(zhuǎn)換編譯過程中生成 html 文件,其中有類似的層耗時(shí)信息

  • 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 打印信息存儲(chǔ)下來,xxx.json (default = disabled)
--exportProfile=<file>      將 profile 打印信息存儲(chǔ)下來,xxx.json (default = disabled)
  • 類似于 J5 hrt_model_exec 中 dump_intermediate、enable_dump、dump_format 等

--exportTimes=<file>        # 將各個(gè)層的執(zhí)行時(shí)間存儲(chǔ)下來 (default = disabled)
  • hrt_model_exec 無相關(guān)參數(shù)

2.4 系統(tǒng)選項(xiàng) System Options
--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ù)

# 加載插件,實(shí)現(xiàn)自定義算子的編譯工作,區(qū)分動(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中,結(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)行介紹,歡迎大家自行探索。

各家的工具都會(huì)針對(duì)自己的硬件或特性設(shè)計(jì)針對(duì)性的參數(shù),只要滿足開發(fā)者需要的功能即可,例如地平線工具鏈的一些參數(shù),有一些就沒介紹到。

這么多參數(shù)其實(shí)并不是都會(huì)用到,大家根據(jù)自己的需求選擇性使用即可。

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

# -----------------------------------#
#   導(dǎo)出靜態(tài)ONNX
# -----------------------------------#
def model_convert_static_onnx(model, dummy_input, output_path):
    input_names = ["input1"]        # 導(dǎo)出的ONNX模型輸入節(jié)點(diǎn)名稱
    output_names = ["output1"]      # 導(dǎo)出的ONNX模型輸出節(jié)點(diǎn)名稱
    torch.onnx.export(
        model,
        dummy_input,
        output_path,
        verbose=False,          # 如果指定為True,在導(dǎo)出的ONNX中會(huì)有詳細(xì)的導(dǎo)出過程信息description
        opset_version=11,       # J5目前僅支持為 10 or 11
        input_names=input_names,
        output_names=output_names,
    )

# -------------------------------------------------------------------------#
#   導(dǎo)出動(dòng)態(tài)ONNX
#   dynamic_axes 參數(shù)中可以只指定輸入動(dòng)態(tài),因?yàn)檩敵鼍S度會(huì)根據(jù)模型的結(jié)構(gòu)自動(dòng)推出來。
#   一般場(chǎng)景,只做 batch 維度動(dòng)態(tài)
# -------------------------------------------------------------------------#
def model_convert_dynamic_onnx(model, dummy_input, output_path):
    input_names = ["input1"]        # 導(dǎo)出的ONNX模型輸入節(jié)點(diǎn)名稱
    output_names = ["output1"]      # 導(dǎo)出的ONNX模型輸出節(jié)點(diǎn)名稱
    torch.onnx.export(
        model,
        dummy_input,
        output_path,
        verbose=False,          # 如果指定為True,在導(dǎo)出的ONNX中會(huì)有詳細(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)

上述代碼運(yùn)行后,會(huì)生成一個(gè) static.onnx,接下來就可以使用這個(gè) onnx 啦。

3.2 性能評(píng)測(cè)實(shí)測(cè)

實(shí)操的方向不同,使用的命令和腳本也會(huì)有差異,本文重點(diǎn)在對(duì)比兩家工具鏈的 PTQ 功能參數(shù)對(duì)比介紹上,因此只選擇一個(gè)性能評(píng)測(cè)方向進(jìn)行實(shí)操演示。

  • 英偉達(dá) trtexec

構(gòu)建用于性能評(píng)測(cè)的 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

會(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
  • 地平線 hb_compile 與 hrt_model_exec

轉(zhuǎn)換編譯用于性能評(píng)測(cè)的 hbm

hb_compile --fast-perf --model static.onnx --march nash-m

會(huì)生成 static.hbm,以及一些日志


在板端評(píng)測(cè)性能數(shù)據(jù)

hrt_model_exec perf --model_file static.hbm --thread_num 8 --frame_count 400 --internal_use


由于時(shí)間原因,就先到這兒了,下次再聊~


*博客內(nèi)容為網(wǎng)友個(gè)人發(fā)布,僅代表博主個(gè)人觀點(diǎn),如有侵權(quán)請(qǐng)聯(lián)系工作人員刪除。



關(guān)鍵詞: 算法 自動(dòng)駕駛

相關(guān)推薦

技術(shù)專區(qū)

關(guān)閉