tensorrt 加速 C++ API

本章说明 C++ API 的基本用法,假设您从 ONNX 模型开始。sampleOnnxMNIST更详细地说明了这个用例。
可以通过标头访问 C++ APINvInfer.h,并且在 nvinfer1命名空间。例如,一个简单的应用程序可能以:
#include “NvInfer.h”

using namespace nvinfer1;
TensorRT C++ API 中的接口类以前缀开头我, 例如记录器,构建器, 等等。

CUDA 上下文会在 TensorRT 第一次调用 CUDA 时自动创建,如果在此之前不存在。通常最好在第一次调用 TensorRT 之前自己创建和配置 CUDA 上下文。

为了说明对象的生命周期,本章中的代码不使用智能指针;但是,建议将它们与 TensorRT 接口一起使用。

3.1。构建阶段
要创建构建器,您首先必须实例化记录器 界面。此示例捕获所有警告消息,但忽略信息性消息:
class Logger : public ILogger
{
void log(Severity severity, const char* msg) noexcept override
{
// suppress info-level messages
if (severity <= Severity::kWARNING)
std::cout << msg << std::endl;
}
} logger;
然后,您可以创建构建器的实例:
IBuilder* builder = createInferBuilder(logger);
3.1.1。创建网络定义
创建构建器后,优化模型的第一步是创建网络定义:
uint32_t flag = 1U <<static_cast<uint32_t>
(NetworkDefinitionCreationFlag::kEXPLICIT_BATCH);

INetworkDefinition* network = builder->createNetworkV2(flag);
这kEXPLICIT_BATCH为了使用 ONNX 解析器导入模型,需要标志。有关详细信息,请参阅显式与隐式批处理部分。

3.1.2。使用 ONNX 解析器导入模型
现在,必须从 ONNX 表示中填充网络定义。ONNX 解析器 API 在文件中NvOnnxParser.h, 解析器在 nvonnxparserC++ 命名空间。
#include “NvOnnxParser.h”

using namespace nvonnxparser;
您可以创建一个 ONNX 解析器来填充网络,如下所示:
IParser* parser = createParser(*network, logger);
然后,读取模型文件并处理任何错误。
parser->parseFromFile(modelFile,
static_cast<int32_t>(ILogger::Severity::kWARNING));
for (int32_t i = 0; i < parser.getNbErrors(); ++i)
{
std::cout << parser->getError(i)->desc() << std::endl;
}
TensorRT 网络定义的一个重要方面是它包含指向模型权重的指针,这些指针由构建器复制到优化的引擎中。由于网络是使用解析器创建的,解析器拥有权重占用的内存,因此在构建器运行之前不应删除解析器对象。

3.1.3. 构建引擎
下一步是创建一个构建配置,指定 TensorRT 应该如何优化模型。
IBuilderConfig* config = builder->createBuilderConfig();
这个接口有很多属性,你可以设置这些属性来控制 TensorRT 如何优化网络。一个重要的属性是最大工作空间大小。层实现通常需要一个临时工作空间,并且此参数限制了网络中任何层可以使用的最大大小。如果提供的工作空间不足,TensorRT 可能无法找到层的实现。默认情况下,工作区设置为给定设备的总全局内存大小;必要时限制它,例如,在单个设备上构建多个引擎时。
config->setMemoryPoolLimit(MemoryPoolType::kWORKSPACE, 1U << 20);
一旦指定了配置,就可以构建引擎。
IHostMemory* serializedModel = builder->buildSerializedNetwork(network, config);
由于序列化引擎包含权重的必要副本,因此不再需要解析器、网络定义、构建器配置和构建器,可以安全地删除:
delete parser;
delete network;
delete config;
delete builder;
然后可以将引擎保存到磁盘,并且可以删除它被序列化到的缓冲区。
delete serializedModel
注意:序列化引擎不能跨平台或 TensorRT 版本移植。引擎特定于构建它们的确切 GPU 模型(除了平台和 TensorRT 版本)。
3.2. 反序列化计划
假设您之前已经序列化了一个优化模型并想要执行推理,您必须创建一个运行界面。与构建器一样,运行时需要一个记录器实例:
IRuntime
runtime = createInferRuntime(logger);
将模型读入缓冲区后,可以对其进行反序列化以获取引擎:
ICudaEngine
engine =
runtime->deserializeCudaEngine(modelData, modelSize);
3.3. 执行推理
引擎拥有优化的模型,但要执行推理,我们必须管理中间激活的附加状态。这是使用 执行上下文界面:
IExecutionContext *context = engine->createExecutionContext();
一个引擎可以有多个执行上下文,允许一组权重用于多个重叠的推理任务。(当前的一个例外是使用动态形状时,每个优化配置文件只能有一个执行上下文。)

要执行推理,您必须为输入和输出传递 TensorRT 缓冲区,TensorRT 要求您在指针数组中指定。您可以使用为输入和输出张量提供的名称查询引擎,以在数组中找到正确的位置:
int32_t inputIndex = engine->getBindingIndex(INPUT_NAME);
int32_t outputIndex = engine->getBindingIndex(OUTPUT_NAME);
使用这些索引,设置一个缓冲区数组,指向 GPU 上的输入和输出缓冲区:
void* buffers[2];
buffers[inputIndex] = inputBuffer;
buffers[outputIndex] = outputBuffer;
然后,您可以调用 TensorRT 的 enqueue 方法以使用 CUDA 流异步启动推理:
context->enqueueV2(buffers, stream, nullptr);
这是常见的排队 cudaMemcpyAsync()在内核之前和之后从 GPU 移动数据(如果它还没有的话)。最后的论据 入队V2()是一个可选的 CUDA 事件,当输入缓冲区被消耗时发出信号,并且可以安全地重用它们的内存。

确定内核(以及可能内存()) 是完整的,使用标准的 CUDA 同步机制,例如事件或等待流。

如果您更喜欢同步推理,请使用执行V2方法而不是 入队V2.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值