0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

TensorRT的C++接口解析

星星科技指导员 来源:NVIDIA 作者:Ken He 2022-05-13 15:41 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

本章说明 C++ API 的基本用法,假设您从 ONNX 模型开始。sampleOnnxMNIST更详细地说明了这个用例。

C++ API 可以通过头文件NvInfer.h访问,并且位于nvinfer1命名空间中。例如,一个简单的应用程序可能以:

#include “NvInfer.h”

using namespace nvinfer1;

TensorRT C++ API 中的接口类以前缀I开头,例如ILoggerIBuilder等。

CUDA 上下文会在 TensorRT 第一次调用 CUDA 时自动创建,如果在该点之前不存在。通常最好在第一次调用 TensoRT 之前自己创建和配置 CUDA 上下文。 为了说明对象的生命周期,本章中的代码不使用智能指针;但是,建议将它们与 TensorRT 接口一起使用。

3.1. The Build Phase

要创建构建器,首先需要实例化ILogger接口。此示例捕获所有警告消息,但忽略信息性消息:

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. Creating a Network Definition

创建构建器后,优化模型的第一步是创建网络定义:

uint32_t flag = 1U <
    (NetworkDefinitionCreationFlag::kEXPLICIT_BATCH); 

INetworkDefinition* network = builder->createNetworkV2(flag);

为了使用 ONNX 解析器导入模型,需要kEXPLICIT_BATCH标志。有关详细信息,请参阅显式与隐式批处理部分。

3.1.2. Importing a Model using the ONNX Parser

现在,需要从 ONNX 表示中填充网络定义。 ONNX 解析器 API 位于文件NvOnnxParser.h中,解析器位于nvonnxparserC++ 命名空间中。

#include “NvOnnxParser.h”

using namespace nvonnxparser;

您可以创建一个 ONNX 解析器来填充网络,如下所示:

IParser*  parser = createParser(*network, logger);

然后,读取模型文件并处理任何错误。

parser->parseFromFile(modelFile, 
    static_cast(ILogger::Severity::kWARNING));
for (int32_t i = 0; i < parser.getNbErrors(); ++i)
{
std::cout << parser->getError(i)->desc() << std::endl;
}

TensorRT 网络定义的一个重要方面是它包含指向模型权重的指针,这些指针由构建器复制到优化的引擎中。由于网络是通过解析器创建的,解析器拥有权重占用的内存,因此在构建器运行之前不应删除解析器对象。

3.1.3. Building an Engine

下一步是创建一个构建配置,指定 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. Deserializing a Plan

假设您之前已经序列化了一个优化模型并希望执行推理,您将需要创建一个运行时接口的实例。与构建器一样,运行时需要一个记录器实例:

IRuntime* runtime = createInferRuntime(logger);

假设您已将模型从缓冲区中读取,然后可以对其进行反序列化以获得引擎:

ICudaEngine* engine = 
  runtime->deserializeCudaEngine(modelData, modelSize);

3.3. Performing Inference

引擎拥有优化的模型,但要执行推理,我们需要管理中间激活的额外状态。这是通过ExecutionContext接口完成的:

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 中移动数据(如果数据尚不存在)。enqueueV2()的最后一个参数是一个可选的 CUDA 事件,当输入缓冲区被消耗时发出信号,并且可以安全地重用它们的内存。

要确定内核(可能还有memcpy())何时完成,请使用标准 CUDA 同步机制,例如事件或等待流。

关于作者

Ken He 是 NVIDIA 企业级开发者社区经理 & 高级讲师,拥有多年的 GPU 和人工智能开发经验。自 2017 年加入 NVIDIA 开发者社区以来,完成过上百场培训,帮助上万个开发者了解人工智能和 GPU 编程开发。在计算机视觉,高性能计算领域完成过多个独立项目。并且,在机器人无人机领域,有过丰富的研发经验。对于图像识别,目标的检测与跟踪完成过多种解决方案。曾经参与 GPU 版气象模式GRAPES,是其主要研发者。

审核编辑:郭婷

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 机器人
    +关注

    关注

    213

    文章

    31431

    浏览量

    223599
  • NVIDIA
    +关注

    关注

    14

    文章

    5685

    浏览量

    110111
  • gpu
    gpu
    +关注

    关注

    28

    文章

    5271

    浏览量

    136059
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    C++与lua联合编程

    在现代软件工程的庞大架构中,纯粹的 C++ 与动态脚本语言之间的关系,早已演变成一场精密的“商业分工”。C++ 扮演着提供极致性能的“底层重工业”,而 Lua 则充当着实现灵活业务逻辑的“轻量级
    发表于 04-19 16:27

    C++:const 的空间,常量也能占内存?

    ] != BLACK)): return True return False c++语言5g.FsW.dgdfgsg.cnJIWWQc++语言 c++语言5g.I5y.dgdfgsg.cnJIWWQc++
    发表于 04-16 19:19

    keil实现cc++混合编程

    起因项目中使用到一个开源的模拟IIC的库,封装的比较好,但是是使用c++写的。于是将其移植到自己的项目中,主要有以下三步操作: 在工程选项中 C/C++中去掉勾选 C99 Mode
    发表于 01-26 08:58

    C语言与C++的区别及联系

    C语言和C++到底是什么关系? 首先C++C语言本来就是两种不同的编程语言,但C++确实是对C
    发表于 12-24 07:23

    CC++之间的联系

    1、语法兼容性: C++完全兼容C语言的语法,这意味着任何有效的C语言程序都可以直接在C++编译器下编译通过。 2、底层控制: C++
    发表于 12-11 06:51

    C语言和C++之间的区别是什么

    区别 1、面向对象编程 (OOP): C语言是一种面向过程的语言,它强调的是通过函数将任务分解为一系列步骤进行执行。 C++C语言的基础上扩展了面向对象的特性,支持类(class)、封装、继承
    发表于 12-11 06:23

    C/C++条件编译

    条件编译是一种在编译时根据条件选择性地包含或排除部分代码的处理方法。在 C/C++ 中,条件编译使用预处理指令 #ifdef、#endif、#else 和 #elif 来实现。常用的条件编译指令有
    发表于 12-05 06:21

    C++程序异常的处理机制

    1、什么是异常处理? 有经验的朋友应该知道,在正常的CC++编程过程中难免会碰到程序不按照原本设计运行的情况。 最常见的有除法分母为零,数组越界,内存分配失效、打开相应文件失败等等。 一个程序
    发表于 12-02 07:12

    C/C++代码静态测试工具Perforce QAC 2025.3的新特性

     Perforce Validate 中 QAC 项目的相对/根路径的支持。C++ 分析也得到了增强,增加了用于检测 C++ 并发问题的新检查,并改进了实体名称和实
    的头像 发表于 10-13 18:11 725次阅读
    <b class='flag-5'>C</b>/<b class='flag-5'>C++</b>代码静态测试工具Perforce QAC 2025.3的新特性

    TensorRT-LLM的大规模专家并行架构设计

    之前文章已介绍引入大规模 EP 的初衷,本篇将继续深入介绍 TensorRT-LLM 的大规模专家并行架构设计与创新实现。
    的头像 发表于 09-23 14:42 1370次阅读
    <b class='flag-5'>TensorRT</b>-LLM的大规模专家并行架构设计

    DeepSeek R1 MTP在TensorRT-LLM中的实现与优化

    TensorRT-LLM 在 NVIDIA Blackwell GPU 上创下了 DeepSeek-R1 推理性能的世界纪录,Multi-Token Prediction (MTP) 实现了大幅提速
    的头像 发表于 08-30 15:47 4672次阅读
    DeepSeek R1 MTP在<b class='flag-5'>TensorRT</b>-LLM中的实现与优化

    技能+1!如何在树莓派上使用C++控制GPIO?

    在使用树莓派时,你会发现Python和Scratch是许多任务(包括GPIO编程)中最常用的编程语言。但你知道吗,你也可以使用C++进行GPIO编程,而且这样做还有不少好处。借助WiringPi
    的头像 发表于 08-06 15:33 4488次阅读
    技能+1!如何在树莓派上使用<b class='flag-5'>C++</b>控制GPIO?

    C++ 与 Python:树莓派上哪种语言更优?

    Python是树莓派上的首选编程语言,我们的大部分教程都使用它。然而,C++在物联网项目中同样广受欢迎且功能强大。那么,在树莓派项目中选择哪种语言更合适呢?Python因其简洁性、丰富的库和资源而被
    的头像 发表于 07-24 15:32 1104次阅读
    <b class='flag-5'>C++</b> 与 Python:树莓派上哪种语言更优?

    主流的 MCU 开发语言为什么是 C 而不是 C++

    在单片机的地界儿里,C语言稳坐中军帐,C++想分杯羹?难喽。咱电子工程师天天跟那针尖大的内存空间较劲,C++那些花里胡哨的玩意儿,在这儿真玩不转。先说内存这道坎儿。您当stm32f4的256kRAM
    的头像 发表于 05-21 10:33 1203次阅读
    主流的 MCU 开发语言为什么是 <b class='flag-5'>C</b> 而不是 <b class='flag-5'>C++</b>?

    如何从 Microsoft Visual C++ 应用程序 (CyAPI.h) 访问 CYUSB3014 芯片组的 i2c 接口

    1. 如何从 Microsoft Visual C++ 应用程序 (CyAPI.h) 访问 CYUSB3014 芯片组的 i2c 接口? 我在定制相机中使用 CYUSB3014。 当我开发我的相机
    发表于 05-19 07:21