以下內容根據個人理解整理而成,如有錯誤,歡迎指出,不勝感激。
更新:看了一個NVIDIA關於TensorRT 6的分享視頻,官方建議使用TensorRT的Python接口進行開發,且TensorRT提供的Parser不太好用,建議自己實現API來構建網絡。
0. 寫在前面
本文首先根據TensorRT開發者指南梳理TensorRT的C++接口使用流程,然后基於TensorRT的官方例程“Hello World” For TensorRT來了解其具體使用方式。
1. C++接口使用
由上一篇Blog中的內容可知,模型從導入TensorRT到執行Inference大致經過下面三個階段:
- Network Definition
- Builder
- Engine
這三個階段分別對應着TensorRT中一些重要的類和方法,下面分別來敘述。
ILogger
首先說明一個必須但不是很重要的類ILogger
,它用於記錄一些日志信息。
在編程時,我們需要聲明一個全局的ILogger
對象gLogger,TensorRT中很多方法都需要它作為參數
(貌似需要繼承ILogger
類來編寫自己的Logger類)
IBuilder
IBuilder
類應該算是最重要的一個類,在使用時,首先要使用TensorRT的全局方法createInferBuilder()
來創建一個IBuilder
類指針,然后由該指針調用IBuilder
類方法創建Network和Engine類的指針。
INetworkDefinition
INetworkDefinition
類即為網絡定義,可通過IBuilder
類方法createNetwork()
返回其指針。
ICudaEngine
ICudaEngine
類即為Engine,可通過IBuilder
類方法buildCudaEngine()/buildEngineWithConfig()
返回其指針。
注意,可通過導入模型生成Engine和通過反序列化來加載Engine兩種Engine生成方式。
IParser
IParser
類對應着前文所述的三種不同的解釋器,可根據需要來使用。
IParser
類方法parse()
用於解析並加載模型及參數到TensorRT網絡中(INetworkDefinition
類)
IExecutionContext
Engine的運行需要一個運行時環境,createExecutionContext()
方法為相應的ICudaEngine
生成一個IExecutionContext
類型的運行環境context。
一個簡單的代碼示例如下:
# builder
IBuilder* builder = createInferBuilder(gLogger);
# network
INetworkDefinition* network = builder->createNetwork();
# parser -> load params to network
CaffeParser* parser = createCaffeParser();
const IBlobNameToTensor* blobNameToTensor = parser->parse(args);
# engine
builder->setMaxBatchSize(maxBatchSize);
IBuilderConfig * config = builder->createBuilderConfig();
config->setMaxWorkspaceSize(1 << 20);
ICudaEngine* engine = builder->buildEngineWithConfig(*network, *config);
# serialize engine if necessary
IHostMemory *serializedModel = engine->serialize();
//# 如果是直接反序列化來獲取engine,上述很多步驟都不需要
//IRuntime* runtime = createInferRuntime(gLogger);
//ICudaEngine* engine = runtime->deserializeCudaEngine(modelData, modelSize, nullptr);
# inference
IExecutionContext *context = engine->createExecutionContext();
# 獲取輸入輸出層的索引
int inputIndex = engine->getBindingIndex(INPUT_BLOB_NAME);
int outputIndex = engine->getBindingIndex(OUTPUT_BLOB_NAME);
# 指針指向輸入輸出層在GPU中的存儲位置
void* buffers[2];
buffers[inputIndex] = inputbuffer;
buffers[outputIndex] = outputBuffer;
# 異步執行inference
context->enqueue(batchSize, buffers, stream, nullptr);
# clear
serializedModel->destroy();
parser->destroy();
network->destroy();
config->destroy();
builder->destroy();
...
疑問一:IBuilder配置參數
可根據需要來配置builder,其中比較重要的參數有兩個:
- maxBatchSize: TensorRT的輸入是NHWC格式,maxBatchSize表明了N最大可為多少,如當N=8時,模型一次可處理8張圖片,速度要大於調用8次、每次處理一張圖片的總時間;
- maxWorkspaceSize:每一層算法的運行都需要臨時的存儲空間,該參數限制了每一層能夠使用的最大臨時存儲空間。
疑問二:runtime vs context
在讀開發者指南時,這兩個概念有點亂,這里區分一下:
- runtime: 直接反序列化獲取engine時,需要定義該類,此時不需要前面builder等相關的操作
- context:每一個engine的運行都需要context,一個engine可有多個context
注意一:CUDA context
從開發者指南中可知,盡管可以使用默認的CUDA context而不顯式創建它,但官方不建議這么做,推薦在創建一個runtime或builder對象時,創建並配置自己的CUDA context。
2. “Hello World” For TensorRT
理清上述流程后,該例子的cpp源碼不難理解,具體細節這里不再闡述。