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

news2024/11/24 4:47:54

目录

    • 前言
    • 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   -1

class 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/cpu
    inline 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 Memory
    inline void* cpu() const { return cpu_; }

    template<typename _T>
    inline _T* gpu() const { return (_T*)gpu_; }

    // Pinned Memory
    template<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 实现的内存的分配和释放以及内存的复用,其中复用思想在于申请分配的内存大于之前已经分配的内存才去释放并重新分配,否则直接返回之前已经分配好的内存,这样可以避免频繁的内存分配和释放操作,从而提高性能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/904801.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C语言】线性搜索数组

题目 给一个数&#xff0c;判定这个数是否在数组a[]中&#xff0c;若在则输出该数在数组中的位置&#xff0c;若不在则输出-1 代码 #include<stdio.h> int search(int key, int a[], int len) {int ret -1;for(int i0; i<len; i){if(key a[i]){ret i;break;}}ret…

贝叶斯公式

一、贝叶斯公式 贝叶斯公式是一种用于概率推断的重要数学工具&#xff0c;它描述了在观测到新信息后如何更新关于某个事件的概率分布。贝叶斯公式的一般形式如下&#xff1a; P(A∣B)P(B∣A)⋅P(A) ​/ P(B) 其中&#xff1a; P(A∣B) 表示在给定观测到事件 B 后&#xff0c…

华为OD机试 - 考勤信息 - 双指针(Java 2023 B卷 100分)

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路五、Java算法源码六、效果展示1、输入输出2、说明 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷&#xff…

解锁项目成功的关键:项目经理的结构化思维之道

1. 项目经理的核心职责 作为项目经理&#xff0c;我们的工作不仅仅是跟踪进度和管理团队。我们的角色在整个项目生命周期中都是至关重要的&#xff0c;从初始概念到最终交付。以下是项目经理的几个核心职责&#xff1a; 确保项目目标的清晰性项目的成功在很大程度上取决于其目…

神经网络改进:注重空间变化,权重参数调整,正则化, 熵的简单理解

目录 神经网络改进&#xff1a;注重空间变化 将高纬空间映射到地位空间便于表示&#xff08;供给数据&#xff09; 将地位空间映射到高纬空间进行分类聚合&#xff08;达到可分状态&#xff08;K-means&#xff09;&#xff09; 神经网络改进&#xff1a;权重参数调整 自注…

docker搭建es+kibana

docker搭建eskibana 0 安装docker 如果是mac或者windows&#xff0c;可以直接安装Docker Desktop更加便捷。 前提条件&#xff1a; Docker可以运行在Windows、Mac、CentOS、Ubuntu等操作系统上 Docker支持以下的CentOS版本&#xff1a; CentOS 7 (64-bit)CentOS 6.5 (64-bit…

Viewpager2+Fragment+指示器

Viewpager2Fragment指示器 效果展示&#xff1a; MainActivity.java package com.huawei.myviewpager;import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.Fragment; import androidx.viewpager2.widget.ViewPager2;import android.os.Bundle; …

Java之接口

作者简介&#xff1a; zoro-1&#xff0c;目前大一&#xff0c;正在学习Java&#xff0c;数据结构等 作者主页&#xff1a; zoro-1的主页 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01;&#x1f496;&#x1f496; Java之接口 接口的概念语法规则接口特性接口使用案…

初出茅庐的小李博客之STM32CubeMx驱动WS2812B实现幻彩(超详)

STM32CubeMx驱动WS2812B实现幻彩&#xff08;超详&#xff09; 1.创建基于STM32F03C8T6工程 1.1配置时钟 选择外部高速时钟源HSE 1.2配置系统时钟树使其达到最大时钟72MHz&#xff08;最大系统时钟&#xff09; 由时钟树可以知道APB1上定时器时钟频率是72MHz,实验使用的硬件…

人脸老化预测(Python)

本次项目的文件 main.py主程序如下 导入必要的库和模块&#xff1a; 导入 TensorFlow 库以及自定义的 FaceAging 模块。导入操作系统库和参数解析库。 定义 str2bool 函数&#xff1a; 自定义函数用于将字符串转换为布尔值。 创建命令行参数解析器&#xff1a; 使用 argparse.A…

linux驱动学习3-外部中断

在做中断试验时&#xff0c;发现中断驱动总是insmod失败&#xff0c;之后定位到 gpio_request 失败&#xff0c;之后是想到使用的野火做好的系统&#xff0c;在uEnv.txt中会加载大量设备树插件&#xff0c;将key相关的设备树插件屏蔽即可。 linux中断API函数 中断号 每个中断…

【管理运筹学】第 5 章 | 整数规划 (3,隐枚举法计算步骤)

文章目录 引言四、0-1 整数规划4.2 0-1 整数规划的解法4.2.1 0-1 规划模型标准型4.2.2 隐枚举法计算步骤 写在最后 引言 经过前文&#xff0c;了解以及体会到 0-1 变量的特性后&#xff0c;我们来研究该如何去求解这类特殊的 0-1 整数规划模型。 四、0-1 整数规划 4.2 0-1 整…

【Maven教程】(二)安装配置篇:手把手教你安装及配置Maven环境~

Maven安装配置篇 1️⃣ 在 Windows 上安装 Maven1.1 下载及安装 Maven1.2 升级 Maven 2️⃣ 在基于UNIX 的系统上安装 Maven2.1 下载和安装2.2 升级 Maven2.3 安装目录分析2.4 设置 HTTP 代理 3️⃣ 在 IDE中安装Maven插件 1️⃣ 在 Windows 上安装 Maven 在安装 Maven 之前&a…

【面试专题】Java核心基础篇②

&#x1f4c3;个人主页&#xff1a;个人主页 &#x1f525;系列专栏&#xff1a;Java面试专题 目录 1.接口和抽象类有什么区别&#xff1f; 2.两个对象的 hashCode() 相同&#xff0c;则 equals()也一定为 true&#xff0c;对吗&#xff1f; 3.说一说hashCode()和equals()的…

一般文章让你了解mybatis,以及如今在Java开发的地位!

一.了解mybatis&#xff01; A.什么是mybatis&#xff1f; MyBatis是一个开源的持久层框架&#xff0c;它简化了在Java应用程序中使用关系型数据库的开发工作。MyBatis提供了将SQL语句和Java代码进行解耦的能力&#xff0c;使得应用程序可以通过简单的配置来访问数据库&#x…

【AI视频教程】只需5步,AI作出鸡你太美视频

1.视频效果 黄昏见证虔诚的信徒 2.准备工作 制作视频效果&#xff0c;需要准备下面3个条件&#xff1a; 准备stable diffusion的环境剪辑一段【鸡你太美】原版视频stable diffusion安装sd-webui-IS-NET-pro插件 2.1部署stable diffusion环境 部署步骤参考制作ikun图片的文章…

软件开发中常用数据结构介绍:C语言队列

工作之余来写写C语言相关知识&#xff0c;以免忘记。今天就来聊聊C语言实现循环队列&#xff0c;我是分享人M哥&#xff0c;目前从事车载控制器的软件开发及测试工作。 学习过程中如有任何疑问&#xff0c;可底下评论&#xff01; 如果觉得文章内容在工作学习中有帮助到你&…

基于springboot+vue的考研资讯平台(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…

Python中Setup.py的作用是什么

Python中Setup.py的作用是什么 今天就跟大家聊聊有关Python中Setup.py的作用是什么&#xff0c;可能很多人都不太了解&#xff0c;为了让大家更加了解. 1. 为什么需要对项目分发打包? 平常我们习惯了使用 pip 来安装一些第三方模块&#xff0c;这个安装过程之所以简单&#…

高项4.项目管理核心技术.

第一部分 项目管理概论 价值驱动的项目管理知识体系: 十二项原则;生命周期四个阶段;五个过程组;十大PM知识领域;八大绩效域;外加价值交付系统; 自1987 年以来, PMBOK 一直是基于过程的项目管理标准的重要代表,项目管理从业者一 直坚持基于过程的项目管理方法。随着…