当前位置: 首页 > 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;。卡方分箱在生物医药领域可以比较两种药物或两组病人是否具有显著区别。但很多建模人员搞不清楚…...

IGP(Interior Gateway Protocol,内部网关协议)

IGP&#xff08;Interior Gateway Protocol&#xff0c;内部网关协议&#xff09; 是一种用于在一个自治系统&#xff08;AS&#xff09;内部传递路由信息的路由协议&#xff0c;主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...

AtCoder 第409​场初级竞赛 A~E题解

A Conflict 【题目链接】 原题链接&#xff1a;A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串&#xff0c;只有在同时为 o 时输出 Yes 并结束程序&#xff0c;否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...

dedecms 织梦自定义表单留言增加ajax验证码功能

增加ajax功能模块&#xff0c;用户不点击提交按钮&#xff0c;只要输入框失去焦点&#xff0c;就会提前提示验证码是否正确。 一&#xff0c;模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...

转转集团旗下首家二手多品类循环仓店“超级转转”开业

6月9日&#xff0c;国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解&#xff0c;“超级…...

React19源码系列之 事件插件系统

事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...

vue3 定时器-定义全局方法 vue+ts

1.创建ts文件 路径&#xff1a;src/utils/timer.ts 完整代码&#xff1a; import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...

OPENCV形态学基础之二腐蚀

一.腐蚀的原理 (图1) 数学表达式&#xff1a;dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一&#xff0c;腐蚀跟膨胀属于反向操作&#xff0c;膨胀是把图像图像变大&#xff0c;而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...

sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!

简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求&#xff0c;并检查收到的响应。它以以下模式之一…...

Windows安装Miniconda

一、下载 https://www.anaconda.com/download/success 二、安装 三、配置镜像源 Anaconda/Miniconda pip 配置清华镜像源_anaconda配置清华源-CSDN博客 四、常用操作命令 Anaconda/Miniconda 基本操作命令_miniconda创建环境命令-CSDN博客...

Python Einops库:深度学习中的张量操作革命

Einops&#xff08;爱因斯坦操作库&#xff09;就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库&#xff0c;用类似自然语言的表达式替代了晦涩的API调用&#xff0c;彻底改变了深度学习工程…...