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 内存,并为新的大小分配内存,它通过 cudaMalloc 和 cudaMemset 来分配和初始化内存。而如果申请的内存大小小于或等于当前的 GPU 内存大小,它将直接返回现有的 GPU 内存。
如果我之前在 GPU 上分配了一块 100 字节的空间,现在需要分配 10 个字节的空间,我会直接拿之前分配的 100 个字节的空间给你,而不用再分配,如果我现在需要分配 1000 个字节的空间,那么我会释放掉之前的 100 个字节,然后重新分配个 1000 字节的空间,以后但凡需要小于 1000 字节的内存空间,我都不会发生分配操作,性能上来讲更友好,对于使用者来讲更简单一些
对于使用者来说只需要给我大小,不用考虑中间是分配还是释放还是重新分配,给大小拿地址,非常友好,这是 MixMemory 提高性能的核心点,就是让同一块内存尽可能地重复的去使用它,而不是每次都去分配一块新内存
这种方法是一个常见的内存管理策略,称为 lazy allocation 或 lazy 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从零起步高性能部署 课程,之前有看过一遍,但是没有做笔记,很多东西也忘了。这次重新撸一遍,顺便记记笔记。 本次课程学习 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++函数指针基本用法
将函数像变量一样传递,实际上拿到的是函数的地址,由于函数类型的多样,可以使用auto关键字,可以使用 void(*function2)() ,不过它太繁琐,因此使用typedef 起个名字 typedef void(*HelloWorldFunction)(); 叫…...

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

Docker实战专栏简介
🌷🍁 博主猫头虎 带您 Go to New World.✨🍁 🦄 博客首页——猫头虎的博客🎐 🐳《面试题大全专栏》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 &a…...

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

机器学习基础之《分类算法(3)—模型选择与调优》
作用是如何选择出最好的K值 一、什么是交叉验证(cross validation) 1、定义 交叉验证:将拿到的训练数据,分为训练和验证集。以下图为例:将数据分成5份,其中一份作为验证集。然后经过5次(组)的测试&#x…...

Datawhale Django后端开发入门 TASK03 QuerySet和Instance、APIVIew
一、QuerySet QuerySet 是 Django 中的一个查询集合,它是由 Model.objects 方法返回的,并且可以用于生成数据库中所有满足一定条件的对象的列表。 QuerySet 在 Django 中表示从数据库中获取的对象集合,它是一个可迭代的、类似列表的对象集合。主要特点…...
Python 网页解析中级篇:深入理解BeautifulSoup库
在Python的网络爬虫中,BeautifulSoup库是一个重要的网页解析工具。在初级教程中,我们已经了解了BeautifulSoup库的基本使用方法。在本篇文章中,我们将深入学习BeautifulSoup库的进阶使用。 一、复杂的查找条件 在使用find和find_all方法查找…...

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

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

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

ES6自用笔记
目录 原型链 引用类型:__proto__(隐式原型)属性,属性值是对象函数:prototype(原型)属性,属性值是对象 相关方法 person.prototype.isPrototypeOf(stu) Object.getPrototypeOf(Object)替换已不推荐的Object._ _ proto _ _ Ob…...

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

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

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

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

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

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

Docker 离线安装指南
参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性,不同版本的Docker对内核版本有不同要求。例如,Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本,Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...

Python实现prophet 理论及参数优化
文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候,写过一篇简单实现,后期随着对该模型的深入研究,本次记录涉及到prophet 的公式以及参数调优,从公式可以更直观…...

Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...

10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...
服务器--宝塔命令
一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行! sudo su - 1. CentOS 系统: yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...

HDFS分布式存储 zookeeper
hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架,允许使用简单的变成模型跨计算机对大型集群进行分布式处理(1.海量的数据存储 2.海量数据的计算)Hadoop核心组件 hdfs(分布式文件存储系统)&a…...
Java毕业设计:WML信息查询与后端信息发布系统开发
JAVAWML信息查询与后端信息发布系统实现 一、系统概述 本系统基于Java和WML(无线标记语言)技术开发,实现了移动设备上的信息查询与后端信息发布功能。系统采用B/S架构,服务器端使用Java Servlet处理请求,数据库采用MySQL存储信息࿰…...

Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...

CVPR2025重磅突破:AnomalyAny框架实现单样本生成逼真异常数据,破解视觉检测瓶颈!
本文介绍了一种名为AnomalyAny的创新框架,该方法利用Stable Diffusion的强大生成能力,仅需单个正常样本和文本描述,即可生成逼真且多样化的异常样本,有效解决了视觉异常检测中异常样本稀缺的难题,为工业质检、医疗影像…...