当前位置: 首页 > news >正文

8.2.tensorRT高级(3)封装系列-内存管理的封装,内存的复用

目录

    • 前言
    • 1. 内存管理封装
    • 2. 补充知识
    • 总结

前言

杜老师推出的 tensorRT从零起步高性能部署 课程,之前有看过一遍,但是没有做笔记,很多东西也忘了。这次重新撸一遍,顺便记记笔记。

本次课程学习 tensorRT 高级-内存管理的封装,内存的复用

课程大纲可看下面的思维导图

在这里插入图片描述

1. 内存管理封装

这节课程我们学习 memory 的封装,使得内存分配复制自动管理,避免手动管理的繁琐

我们可以回顾下之前的分类器、检测器案例代码,假设我们为输入分配了一个 input_data_host 的空间,对应的往往我们也会在 input_data_device 上分配一块同样大小的内存空间;对于 output 也是类似,因此引发我们的思考,我们完全可以将这两个对应的内存打包在一起,方便我们的管理

我们来看代码,

mix-memory.hpp

#ifndef MEMORY_HPP
#define MEMORY_HPP#include <stddef.h>#define CURRENT_DEVICE_ID   -1class MixMemory {
public:MixMemory(int device_id = CURRENT_DEVICE_ID);MixMemory(void* cpu, size_t cpu_size, void* gpu, size_t gpu_size, int device_id = CURRENT_DEVICE_ID);virtual ~MixMemory();void* gpu(size_t size);void* cpu(size_t size);template<typename _T>_T* gpu(size_t size){ return (_T*)gpu(size * sizeof(_T)); }template<typename _T>_T* cpu(size_t size){ return (_T*)cpu(size * sizeof(_T)); };void release_gpu();void release_cpu();void release_all();// 是否属于我自己分配的gpu/cpuinline bool owner_gpu() const{return owner_gpu_;}inline bool owner_cpu() const{return owner_cpu_;}inline size_t cpu_size() const{return cpu_size_;}inline size_t gpu_size() const{return gpu_size_;}inline int device_id() const{return device_id_;}inline void* gpu() const { return gpu_; }// Pinned Memoryinline void* cpu() const { return cpu_; }template<typename _T>inline _T* gpu() const { return (_T*)gpu_; }// Pinned Memorytemplate<typename _T>inline _T* cpu() const { return (_T*)cpu_; }void reference_data(void* cpu, size_t cpu_size, void* gpu, size_t gpu_size, int device_id = CURRENT_DEVICE_ID);private:void* cpu_ = nullptr;size_t cpu_size_ = 0;bool owner_cpu_ = true;int device_id_ = 0;void* gpu_ = nullptr;size_t gpu_size_ = 0;bool owner_gpu_ = true;
};#endif // MEMORY_HPP

mix-memory.cpp


#include "mix-memory.hpp"
#include "cuda-tools.hpp"
#include <string.h>
#include <assert.h>inline static int check_and_trans_device_id(int device_id){if(device_id != CURRENT_DEVICE_ID){CUDATools::check_device_id(device_id);return device_id;}checkRuntime(cudaGetDevice(&device_id));return device_id;
}MixMemory::MixMemory(int device_id){device_id_ = check_and_trans_device_id(device_id);
}MixMemory::MixMemory(void* cpu, size_t cpu_size, void* gpu, size_t gpu_size, int device_id){reference_data(cpu, cpu_size, gpu, gpu_size, device_id);		
}void MixMemory::reference_data(void* cpu, size_t cpu_size, void* gpu, size_t gpu_size, int device_id){release_all();if(cpu == nullptr || cpu_size == 0){cpu = nullptr;cpu_size = 0;}if(gpu == nullptr || gpu_size == 0){gpu = nullptr;gpu_size = 0;}this->cpu_ = cpu;this->cpu_size_ = cpu_size;this->gpu_ = gpu;this->gpu_size_ = gpu_size;this->owner_cpu_ = !(cpu && cpu_size > 0);this->owner_gpu_ = !(gpu && gpu_size > 0);device_id_ = check_and_trans_device_id(device_id);
}MixMemory::~MixMemory() {release_all();
}void* MixMemory::gpu(size_t size) {if (gpu_size_ < size) {release_gpu();gpu_size_ = size;CUDATools::AutoDevice auto_device_exchange(device_id_);checkRuntime(cudaMalloc(&gpu_, size));checkRuntime(cudaMemset(gpu_, 0, size));}return gpu_;
}void* MixMemory::cpu(size_t size) {if (cpu_size_ < size) {release_cpu();cpu_size_ = size;CUDATools::AutoDevice auto_device_exchange(device_id_);checkRuntime(cudaMallocHost(&cpu_, size));assert(cpu_ != nullptr);memset(cpu_, 0, size);}return cpu_;
}void MixMemory::release_cpu() {if (cpu_) {if(owner_cpu_){CUDATools::AutoDevice auto_device_exchange(device_id_);checkRuntime(cudaFreeHost(cpu_));}cpu_ = nullptr;}cpu_size_ = 0;
}void MixMemory::release_gpu() {if (gpu_) {if(owner_gpu_){CUDATools::AutoDevice auto_device_exchange(device_id_);checkRuntime(cudaFree(gpu_));}gpu_ = nullptr;}gpu_size_ = 0;
}void MixMemory::release_all() {release_cpu();release_gpu();
}

在头文件中我们定义了一个 MixMemory 的类,专门用于混合内存(即CPU和GPU内存)的管理。类中提供了构造函数,允许已经分配的 CPU 和 GPU 内存定义为 MixMemory,提供了一些模板函数,用于返回特定类型的 GPU 和 CPU 内存指针,提供了用于分配和释放 GPU 和 CPU 内存的方法,还提供了一些内联函数,用于获取当前对象的属性,如 owner_gpu()gpu_size()device_id()gpu() 等,核心函数是 void gpu(size_t size)*

在 gpu 分配方法中,如果申请的大小大于当前的 GPU 内存大小,则释放现有的 GPU 内存,并为新的大小分配内存,它通过 cudaMalloccudaMemset 来分配和初始化内存。而如果申请的内存大小小于或等于当前的 GPU 内存大小,它将直接返回现有的 GPU 内存。

如果我之前在 GPU 上分配了一块 100 字节的空间,现在需要分配 10 个字节的空间,我会直接拿之前分配的 100 个字节的空间给你,而不用再分配,如果我现在需要分配 1000 个字节的空间,那么我会释放掉之前的 100 个字节,然后重新分配个 1000 字节的空间,以后但凡需要小于 1000 字节的内存空间,我都不会发生分配操作,性能上来讲更友好,对于使用者来讲更简单一些

对于使用者来说只需要给我大小,不用考虑中间是分配还是释放还是重新分配,给大小拿地址,非常友好,这是 MixMemory 提高性能的核心点,就是让同一块内存尽可能地重复的去使用它,而不是每次都去分配一块新内存

这种方法是一个常见的内存管理策略,称为 lazy allocationlazy resizing。其背后的思路是,如果已经分配了足够的内存来满足当前的请求,那么就没有必要重新分配。这样可以避免频繁的内存分配和释放操作,从而提高性能。

MixMemory 类为 CPU 和 GPU 内存分配和管理提供了一个封装。它有助于确保在分配新内存之前释放现有的内存,并通过 AutoDevice 来完成指定 device 上的内存分配,它简化了 CUDA 内存管理,通过内部跟踪和自动释放来实现内存的复用。

在 main.cpp 中,我们分配 host 和 device 内存时,就可以直接使用 MixMemory 了,部分代码如下:

MixMemory input_data;
float* input_data_host   = input_data.cpu<float>(input_numel);
float* input_data_device = input_data.gpu<float>(input_numel);MixMemory output_data;
float* output_data_host   = output_data.cpu<float>(num_classes);
float* output_data_device = output_data.gpu<float>(num_classes);

使用 MixMemory 相对来说轻松多了,不用去 cudaMallocHost、cudaMalloc 手动分配内存以及 cudaFreeHost、cudaFree 手动释放内存了,并且还可以解决内存复用的问题,

2. 补充知识

关于 MixMemory 的封装,你需要知道:(form 杜老师)

1. MixMemory 的存在,是为了避免每次内存都要分配和释放,对内存做重复使用提升性能

  • 如果第二次执行 gpu 获取 gpu 内存,会检查当前已经分配是否够用,如果不够则重新分配,够就直接返回

2. MixMemory 的封装,考虑到分配时当前设备 ID 如果不同该怎么办,释放时,当前设备 ID 不同怎么办

3. 对 cuda 的基本操作做了封装,对于这类常用的功能进行封装,便于使用

4. AutoDevice,对于当前设备 ID old 和准备分配内存所操作的设备 ID target 不用时,解决如下问题:

  • 获取当前设备 ID old
  • 设置当前设备 ID 为 target
  • 进行内存分配,分配结果在目的 ID target 上
  • 设置当前设备 ID 为 old ID

总结

本次课程学习了对 memory 的封装,我们每次都要去 cudaMalloc、cudaMallocHost 分配 device 和 host 内存,然后去 cudaFree、cudaFreeHost 去释放内存,非常麻烦,我们对混合内存进行了封装,通过 MixMemory 实现的内存的分配和释放以及内存的复用,其中复用思想在于申请分配的内存大于之前已经分配的内存才去释放并重新分配,否则直接返回之前已经分配好的内存,这样可以避免频繁的内存分配和释放操作,从而提高性能。

相关文章:

8.2.tensorRT高级(3)封装系列-内存管理的封装,内存的复用

目录 前言1. 内存管理封装2. 补充知识总结 前言 杜老师推出的 tensorRT从零起步高性能部署 课程&#xff0c;之前有看过一遍&#xff0c;但是没有做笔记&#xff0c;很多东西也忘了。这次重新撸一遍&#xff0c;顺便记记笔记。 本次课程学习 tensorRT 高级-内存管理的封装&…...

Keepalived入门指南:实现故障转移和负载均衡

文章目录 一、简介1. Keepalived概述2. 高可用性和负载均衡的重要性 二、故障转移1. 什么是故障转移2. Keepalived的故障转移原理a) VRRP协议b) 虚拟路由器ID和优先级 3. 配置Keepalived实现故障转移a) 主备服务器的设置b) 监控网络接口c) 虚拟IP的配置d) 备份服务器接管流程 三…...

cuOSD(CUDA On-Screen Display Library)库的学习

目录 前言1. cuOSD1.1 Description1.2 Getting started1.3 For Python Interface1.4 Demo1.5 Performance Table 2. cuOSD案例2.1 环境配置2.2 simple案例2.3 segment案例2.4 segment2案例2.5 polyline案例2.6 comp案例2.7 perf案例 3. cuOSD浅析3.1 simple_draw函数 4. 补充知…...

c++函数指针基本用法

将函数像变量一样传递&#xff0c;实际上拿到的是函数的地址&#xff0c;由于函数类型的多样&#xff0c;可以使用auto关键字&#xff0c;可以使用 void(*function2)() &#xff0c;不过它太繁琐&#xff0c;因此使用typedef 起个名字 typedef void(*HelloWorldFunction)(); 叫…...

Java创建对象的几种方式

在Java中&#xff0c;对象是程序中的一种基本元素&#xff0c;它通过类定义和创建。本篇教程旨在介绍Java中创建对象的几种方式&#xff0c;包括使用new关键字、反射、clone、反序列化等方式。 使用new关键字创建对象 在Java中&#xff0c;最常用的创建对象方式是使用new关键…...

Docker实战专栏简介

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…...

解放数据库,实时数据同步利器:Alibaba Canal

文章首发地址 Canal是一个开源的数据库增量订阅&消费组件&#xff0c;主要用于实时数据同步和数据订阅的场景&#xff0c;特别适用于构建分布式系统、数据仓库、缓存更新等应用。它支持MySQL、阿里云RDS等主流数据库&#xff0c;能够实时捕获数据库的增删改操作&#xff…...

机器学习基础之《分类算法(3)—模型选择与调优》

作用是如何选择出最好的K值 一、什么是交叉验证&#xff08;cross validation&#xff09; 1、定义 交叉验证&#xff1a;将拿到的训练数据&#xff0c;分为训练和验证集。以下图为例&#xff1a;将数据分成5份&#xff0c;其中一份作为验证集。然后经过5次(组)的测试&#x…...

Datawhale Django后端开发入门 TASK03 QuerySet和Instance、APIVIew

一、QuerySet QuerySet 是 Django 中的一个查询集合&#xff0c;它是由 Model.objects 方法返回的&#xff0c;并且可以用于生成数据库中所有满足一定条件的对象的列表。 QuerySet 在 Django 中表示从数据库中获取的对象集合,它是一个可迭代的、类似列表的对象集合。主要特点…...

Python 网页解析中级篇:深入理解BeautifulSoup库

在Python的网络爬虫中&#xff0c;BeautifulSoup库是一个重要的网页解析工具。在初级教程中&#xff0c;我们已经了解了BeautifulSoup库的基本使用方法。在本篇文章中&#xff0c;我们将深入学习BeautifulSoup库的进阶使用。 一、复杂的查找条件 在使用find和find_all方法查找…...

IDEA 如何制作代码补丁?IDEA 生成 patch 和使用 patch

什么是升级补丁&#xff1f; 比如你本地修复的 bug&#xff0c;需要把增量文件发给客户&#xff0c;很多场景下大家都需要手工整理修改的文件&#xff0c;并整理好目录&#xff0c;这个很麻烦。那有没有简单的技巧呢&#xff1f;看看 IDEA 生成 patch 和使用 patch 的使用。 介…...

Redis专题-秒杀

Redis专题-并发/秒杀 开局一张图&#xff0c;内容全靠“编”。 昨天晚上在群友里看到有人在讨论库存并发的问题&#xff0c;看到这里我就决定写一篇关于redis秒杀的文章。 1、理论部分 我们看看一般我们库存是怎么出问题的 其实redis提供了两种解决方案&#xff1a;加锁和原子操…...

C++笔记之std::move和右值引用的关系、以及移动语义

C笔记之std::move和右值引用的关系、以及移动语义 code review! 文章目录 C笔记之std::move和右值引用的关系、以及移动语义1.一个使用std::move的最简单C例子2.std::move 和 T&& reference_name expression;对比3.右值引用和常规引用的经典对比——移动语义和拷贝语…...

ES6自用笔记

目录 原型链 引用类型&#xff1a;__proto__(隐式原型)属性&#xff0c;属性值是对象函数&#xff1a;prototype(原型)属性&#xff0c;属性值是对象 相关方法 person.prototype.isPrototypeOf(stu) Object.getPrototypeOf(Object)替换已不推荐的Object._ _ proto _ _ Ob…...

【BASH】回顾与知识点梳理(二十九)

【BASH】回顾与知识点梳理 二十九 二十九. 进程和工作管理29.1 什么是进程 (process)进程与程序 (process & program)子进程与父进程&#xff1a;fork and exec&#xff1a;进程呼叫的流程系统或网络服务&#xff1a;常驻在内存的进程 29.2 Linux 的多人多任务环境多人环境…...

Docker的Cgroup资源限制

Docker通过Cgroup来控制容器使用的资源配额&#xff0c;包括 CPU、内存、磁盘三大方面&#xff0c;基本覆盖了常见的资源配颡和使用量控制。 Cgoup 是CotrolGroups 的缩写&#xff0c;是Linux 内核提供的一种可以限制、记录、隔高进程组所使用的物理资源&#xff08;如CPU、内存…...

AI智能语音机器人的基本业务流程

先画个图&#xff0c;了解下AI语音机器人的基本业务流程。 上图是一个AI语音机器人的业务流程&#xff0c;简单来说就是首先要配置话术&#xff0c;就是告诉机器人在遇到问题该怎么回答&#xff0c;这个不同公司不同行业的差别比较大&#xff0c;所以一般每个客户都会配置其个性…...

uniapp 上传比较大的视频文件就超时

uni.uploadFile&#xff0c;上传超过10兆左右的文件就报错err&#xff1a;uploadFile:fail timeout&#xff0c;超时 解决&#xff1a; 在manifest.json文件中做超时配置 uni.uploadFile({url: this.action,method: "POST",header: {Authorization: uni.getStorage…...

CSS简介

目录 CSS CSS概念 核心概念 为什么需要CSS 语法 CSS的引入方式 内联样式&#xff08;行内样式&#xff09; 内部样式 外部样式&#xff08;推荐&#xff09; CSS CSS概念 CSS&#xff08;Cascading Style Sheets&#xff09;层叠样式表&#xff0c;又叫级联样式表&am…...

卡方分箱(chi-square)

统计学&#xff0c;风控建模经常遇到卡方分箱算法ChiMerge。卡方分箱在金融信贷风控领域是逻辑回归评分卡的核心&#xff0c;让分箱具有统计学意义&#xff08;单调性&#xff09;。卡方分箱在生物医药领域可以比较两种药物或两组病人是否具有显著区别。但很多建模人员搞不清楚…...

RestClient

什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端&#xff0c;它允许HTTP与Elasticsearch 集群通信&#xff0c;而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级&#xff…...

Cesium1.95中高性能加载1500个点

一、基本方式&#xff1a; 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...

uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖

在前面的练习中&#xff0c;每个页面需要使用ref&#xff0c;onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入&#xff0c;需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...

Golang dig框架与GraphQL的完美结合

将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用&#xff0c;可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器&#xff0c;能够帮助开发者更好地管理复杂的依赖关系&#xff0c;而 GraphQL 则是一种用于 API 的查询语言&#xff0c;能够提…...

MySQL 8.0 OCP 英文题库解析(十三)

Oracle 为庆祝 MySQL 30 周年&#xff0c;截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始&#xff0c;将英文题库免费公布出来&#xff0c;并进行解析&#xff0c;帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...

Android15默认授权浮窗权限

我们经常有那种需求&#xff0c;客户需要定制的apk集成在ROM中&#xff0c;并且默认授予其【显示在其他应用的上层】权限&#xff0c;也就是我们常说的浮窗权限&#xff0c;那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...

3-11单元格区域边界定位(End属性)学习笔记

返回一个Range 对象&#xff0c;只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意&#xff1a;它移动的位置必须是相连的有内容的单元格…...

Mysql中select查询语句的执行过程

目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析&#xff08;Parser&#xff09; 2.4、执行sql 1. 预处理&#xff08;Preprocessor&#xff09; 2. 查询优化器&#xff08;Optimizer&#xff09; 3. 执行器…...

使用Spring AI和MCP协议构建图片搜索服务

目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式&#xff08;本地调用&#xff09; SSE模式&#xff08;远程调用&#xff09; 4. 注册工具提…...

无人机侦测与反制技术的进展与应用

国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机&#xff08;无人驾驶飞行器&#xff0c;UAV&#xff09;技术的快速发展&#xff0c;其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统&#xff0c;无人机的“黑飞”&…...