第八章 CUDA内存应用与性能优化篇(上篇)

news2024/11/24 2:15:13

cuda教程目录

第一章 指针篇
第二章 CUDA原理篇
第三章 CUDA编译器环境配置篇
第四章 kernel函数基础篇
第五章 kernel索引(index)篇
第六章 kenel矩阵计算实战篇
第七章 kenel实战强化篇
第八章 CUDA内存应用与性能优化篇
第九章 CUDA原子(atomic)实战篇
第十章 CUDA流(stream)实战篇
第十一章 CUDA的NMS算子实战篇
第十二章 YOLO的部署实战篇
第十三章 基于CUDA的YOLO部署实战篇

cuda教程背景

随着人工智能的发展与人才的内卷,很多企业已将深度学习算法的C++部署能力作为基本技能之一。面对诸多arm相关且资源有限的设备,往往想更好的提速,满足更高时效性,必将更多类似矩阵相关运算交给CUDA处理。同时,面对市场诸多教程与诸多博客岑子不起的教程或高昂教程费用,使读者(特别是小白)容易迷糊,无法快速入手CUDA编程,实现工程化。
因此,我将结合我的工程实战经验,我将在本专栏实现CUDA系列教程,帮助读者(或小白)实现CUDA工程化,掌握CUDA编程能力。学习我的教程专栏,你将绝对能实现CUDA工程化,完全从环境安装到CUDA核函数编程,从核函数到使用相关内存优化,从内存优化到深度学习算子开发(如:nms),从算子优化到模型(以yolo系列为基准)部署。最重要的是,我的教程将简单明了直切主题,CUDA理论与实战实例应用,并附相关代码,可直接上手实战。我的想法是掌握必要CUDA相关理论,去除非必须繁杂理论,实现CUDA算法应用开发,待进一步提高,将进一步理解更高深理论。

cuda教程内容

第一章到第三章探索指针在cuda函数中的作用与cuda相关原理及环境配置;

第四章初步探索cuda相关函数编写(globaldevice、__host__等),实现简单入门;

第五章探索不同grid与block配置,如何计算kernel函数的index,以便后续通过index实现各种运算;

第六、七章由浅入深探索核函数矩阵计算,深入探索grid、block与thread索引对kernel函数编写作用与影响,并实战多个应用列子(如:kernel函数实现图像颜色空间转换);

第八章探索cuda内存纹理内存、常量内存、全局内存等分配机制与内存实战应用(附代码),通过不同内存的使用来优化cuda计算性能;

第九章探索cuda原子(atomic)相关操作,并实战应用(如:获得某些自加索引等);

第十章探索cuda流stream相关应用,并给出相关实战列子(如:多流操作等);

第十一到十三章探索基于tensorrt部署yolo算法,我们首先将给出通用tensorrt的yolo算法部署,该部署的前后处理基于C++语言的host端实现,然后给出基于cuda的前后处理的算子核函数编写,最后数据无需在gpu与host间复制操作,实现gpu处理,提升算法性能。

目前,以上为我们的cuda教学全部内容,若后续读者有想了解知识,可留言,我们将根据实际情况,更新相关教学内容。

大神忽略

源码链接地址点击这里


文章目录

  • cuda教程目录
  • cuda教程背景
  • cuda教程内容
    • 源码链接地址[点击这里](https://github.com/tangjunjun966/cuda-tutorial-master)
  • 前言
  • 一、内存知识回顾
  • 二、GPU内存信息查询
  • 三、可分页内存与页锁定内存
  • 四、cudaMallocHost 和 cudaMalloc(可分页内存与页锁定内存)
    • 1、内存分配方式
      • Host端内存分配(Pageable Memory)
    • 2、分配的内存类型
    • 3、内存的使用方式
    • 4、内存的传输方式
    • 5、性能
    • 6、总结
  • 八、总结


前言

以上章节中,我们已经比较熟练掌握如何使用cuda编写自己想要的计算逻辑,已能成功编写cuda代码了。 那么,另外一个重要问题值得我们关注,如何优化其性能,使其工程部署能加速运行了。而这种性能优化与cuda内存密切相关。为此,我们在本节中介绍cuda内存相关内容,并附其源码。


一、内存知识回顾

我再次简单回顾下相关内存概念,详细内容可看我第二章内容(我个人局的还是重要)链接点击这里。

Registers:寄存器是GPU中最快的memory,kernel中没有什么特殊声明的自动变量都是放在寄存器中的。当数组的索引是constant类型且在编译期能被确定的话,就是内置类型,数组也是放在寄存器中。 寄存器变量是每个线程私有的,一旦thread执行结束,寄存器变量就会失效。

Shared Memory:用__shared__修饰符修饰的变量存放在shared memory中。Shared Memory位于GPU芯片上,访问延迟仅次于寄存器。所有Thread来进行访问的,可以实现Block内的线程间的低开销通信。 要使用__syncthread()同步。

Local Memory:本身在硬件中没有特定的存储单元,而是从Global Memory虚拟出来的地址空间。是为寄存器无法满足存储需求的情况而设计的,主要是用于存放单线程的大型数组和变量。Local Memory是线程私有的,线程之间是不可见的。它的访问是比较慢的,跟Global Memory的访问速度是接近的。使用情景,无法确定其索引是否为常量的数组;会消耗太多寄存器空间的大型结构或数组;如果内核使用了多于寄存器的任何变量(这也称为寄存器溢出);

Constant Memory:固定内存空间驻留在设备内存中,并缓存在固定缓存中(constant cache),范围是全局的,针对所有kernel; kernel只能从constant Memory中读取数据,因此其初始化必须在host端使用下面的function调用:cudaError_t cudaMemcpyToSymbol(const void* symbol,const void* src,size_t count); 当一个warp中所有线程都从同一个Memory地址读取数据时,constant Memory表现会非常好,会触发广播机制。

Global Memory:Global Memory在某种意义上等同于GPU显存,kernel函数通过Global Memory来读写显存。Global Memory是kernel函数输入数据和写入结果的唯一来源。

Texture Memory:是GPU的重要特性之一,也是GPU编程优化的关键。Texture Memory实际上也是Global Memory的一部分,但是它有自己专用的只读cache。这个cache在浮点运算很有用,Texture Memory是针对2D空间局部性的优化策略,所以thread要获取2D数据就可以使用texture Memory来达到很高的性能。从读取性能的角度跟Constant Memory类似。

Host Memory:主机端存储器主要是内存可以分为两类:可分页内存(Pageable)和页面 (Page-Locked 或 Pinned)内存。可分页内存通过操作系统 API(malloc/free) 分配存储器空间,该内存是可以换页的,即内存页可以被置换到磁盘中。可分页内存是不可用使用DMA(Direct Memory Acess)来进行访问的,普通的C程序使用的内存就是这个内存。

二、GPU内存信息查询

代码如下:

int inquire_GPU_info() {
    int deviceCount;
    cudaGetDeviceCount(&deviceCount);

    int dev;
    for (dev = 0; dev < deviceCount; dev++)
    {
        int driver_version(0), runtime_version(0);
        cudaDeviceProp deviceProp;
        cudaGetDeviceProperties(&deviceProp, dev);
        if (dev == 0)
            if (deviceProp.minor = 9999 && deviceProp.major == 9999)
                printf("\n");
        printf("\nDevice%d:\"%s\"\n", dev, deviceProp.name);
        cudaDriverGetVersion(&driver_version);
        printf("CUDA驱动版本:                                         %d.%d\n", driver_version / 1000, (driver_version % 1000) / 10);
        cudaRuntimeGetVersion(&runtime_version);
        printf("CUDA运行时版本:                                       %d.%d\n", runtime_version / 1000, (runtime_version % 1000) / 10);
        printf("设备计算能力:                                         %d.%d\n", deviceProp.major, deviceProp.minor);
        printf("设备全局内存总量 Global Memory:                       %u M\n", deviceProp.totalGlobalMem/(1024*1024));
        printf("Number of SMs:                                        %d\n", deviceProp.multiProcessorCount);
        printf("常量内存 Constant Memory:                             %u K\n", deviceProp.totalConstMem/1024);
        printf("每个block的共享内存 Shared Memory:                    %u K\n", deviceProp.sharedMemPerBlock/1024);
        printf("每个block的寄存器 registers :                         %d\n", deviceProp.regsPerBlock);
        printf("线程束Warp size:                                      %d\n", deviceProp.warpSize);
        printf("每个SM的最大线程数 threads per SM:                    %d\n", deviceProp.maxThreadsPerMultiProcessor);
        printf("每个block的最大线程数 threads per block:              %d\n", deviceProp.maxThreadsPerBlock);
        printf("每个block的最大维度 each dimension of a block:        %d x %d x %d\n", deviceProp.maxThreadsDim[0],     deviceProp.maxThreadsDim[1],  deviceProp.maxThreadsDim[2]);
        printf("每个grid的最大维度 dimension of a grid:               %d x %d x %d\n", deviceProp.maxGridSize[0], deviceProp.maxGridSize[1], deviceProp.maxGridSize[2]);
        printf("Maximum memory pitch:                                 %u bytes\n", deviceProp.memPitch);
        printf("Texture alignmemt:                                    %u bytes\n", deviceProp.texturePitchAlignment);
        printf("Clock rate:                                           %.2f GHz\n", deviceProp.clockRate * 1e-6f);
        printf("Memory Clock rate:                                    %.0f MHz\n", deviceProp.memoryClockRate * 1e-3f);
        printf("Memory Bus Width:                                     %d-bit\n", deviceProp.memoryBusWidth);
    }

    return 0;
}
}

查询结果显示如下:
在这里插入图片描述

三、可分页内存与页锁定内存

CPU内存,称之为Host Memory,逻辑上可分为Pageable Memory(可分页内存)、Page Lock Memory(页锁定内存),Page Lock Memory又称为Pinned Memory,从字面意思上而言Page Lock Memory是锁定的内存,一旦申请后就专供申请者使用,Pageable Memory则没有锁定特性,申请后可能会被交换。

总结如下:
①、pinned memory具有锁定特性,是稳定不会被交换的;
pageable memory没有锁定特性,对于第三方设备(比如GPU),去访问时,因为无法感知内存是否被交换,可能得不到正确的数据;
②、pageable memory的性能比pinned memory差,很可能降低你程序的优先级然后把内存交换给别人用;
pageable memory策略使用内存假象,实际8GB但是可以使用15GB,可以提高程序运行数量,但运行速度会降低;
pinned memory太多,会导致操作系统整体性能降低,因为程序运行数量减少了;
③、GPU可以直接访问pinned memory而不能访问pageable memory(因为第二条)。
说明:当将pageable host Memory数据送到device时,CUDA驱动会首先分配一个临时的page-locked或者pinned host Memory,并将host的数据放到这个临时空间里。然后GPU从这个所谓的pinned Memory中获取数据,如下图所示:

在这里插入图片描述

四、cudaMallocHost 和 cudaMalloc(可分页内存与页锁定内存)

之前章节一直以实例介绍cuda代码编写,也对host与device端的变量进行了内存分配,并未重点说明cudaMallocHost 和 cudaMalloc的使用方法,我个人觉得很重要,对于不同设备的数据传输(如host与GPU间)均需要使用复制方法,而针对GPU内存分配与CPU数据间关系,需要我们有更深入了解,在此,我介绍重点介绍一下。

1、内存分配方式

以上介绍gpu如何访问cpu的内存方式。对于给GPU访问而言,距离计算单元越近,内存访问效率越高。为此,由低到高访问速度为:Pinned Memory < Global Memory < Shared Memory
重点说明,GPU可以直接访问Pinned Memory,称之为DMA Direct Memory Access

接下来,我将介绍实际内存分配的几种方式:

Host端内存分配(Pageable Memory)

之前代码使用cudaMallocHost对内存分配,但也可使用new或malloc分配,而该分配属于Pageable Memory可分页内存。
其分配内存代码如下:

 std::cout << "设置new(malloc)可分页内存" << std::endl;
    float* memory_device = nullptr;
    float* memory_host = new float[100]; // Pageable Memory
    for (int i = 0; i < 100; i++) { memory_host[i] = i * 100; }
    checkRuntime(cudaMemcpy(memory_device, memory_host, sizeof(float) * 100, cudaMemcpyHostToDevice)); // 返回的地址是开辟的device地址,存放在memory_device
    show_value << <dim3(1), dim3(100) >> > (memory_device);

以上直接使用CPU分配内存,然后使用cudaMemcpy复制给memory_device中,仍然可以实现,然这种效率较低。但切记,这种memory_host可以直接使用new赋值在核函数中使用。同时,这种速度较慢,不建议使用。
预测结果显示(仅显示前10个数)如下:

在这里插入图片描述

2、分配的内存类型

cudaMallocHost 分配的内存是页锁定内存,而 cudaMalloc 分配的内存是普通可分页内存

3、内存的使用方式

cudaMallocHost 分配的内存可以通过主机和设备访问,而 cudaMalloc 分配的内存只能通过设备访问。
cudaMalloc分配内存方式为GPU的全局内存,代码如下:

	std::cout << "设置全局内存"  << std::endl;
    float* memory_device = nullptr; // Global Memory
    checkRuntime(cudaMalloc(&memory_device, 100 * sizeof(float))); // pointer to device

以上代码实际在前面章节中已大量使用,实际作用为:使用cudaMalloc在gpu设备上分配一个全局内存空间,便于在kernel计算中存储数据。
cudaMallocHost分配内存方式,代码如下:

	std::cout << "设置页锁定内存" << std::endl;
    float* memory_device = nullptr;
    float* memory_page_locked = nullptr; // Pinned Memory
    checkRuntime(cudaMallocHost(&memory_page_locked, 100 * sizeof(float))); // 返回的地址是被开辟的pin memory的地址,存放在memory_page_locked
    checkRuntime(cudaMemcpy(memory_page_locked, memory_device, sizeof(float) * 100, cudaMemcpyDeviceToHost)); // 将其返回host内存

以上代码实际在前面章节中已大量使用,实际作用也就是上面解释,即使用cudaMallocHost在gpu设备上分配内存,可使主机host和设备device均可访问,并使用cudaMemcpy赋值gpu数据。

4、内存的传输方式

由于 cudaMallocHost 分配的内存可以通过主机和设备访问,因此可以通过零拷贝技术(Zero-Copy)将数据直接从主机内存传输到设备内存,而 cudaMalloc 分配的内存则需要使用显式的数据传输函数(如 cudaMemcpy)进行传输。

5、性能

由于 cudaMallocHost 分配的内存是页锁定内存,因此可以避免在主机和设备之间进行数据传输时产生额外的复制操作,从而提高数据传输的性能。

6、总结

尽量多用Pinned Memory储存host端数据,或者显式处理Host到Device时用PinnedMemory做缓存,都是提高性能的关键。因此,如果需要在主机和设备之间进行频繁的数据传输,建议使用 cudaMallocHost 分配内存。如果只需要在设备上进行计算,并且不需要频繁地与主机进行数据交换,则可以使用 cudaMalloc 分配内存。

八、总结

以上为cuda内存应用与性能优化篇关于内存的相关知识和简单代码说明,旨在掌握在host端与device端间内存传输与分配相关原理,后面篇章将介绍如何定义常量内存、共享内存、纹理内存等相关方法,并附上相应代码。

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

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

相关文章

机器学习线性代数基础

本文是斯坦福大学CS 229机器学习课程的基础材料&#xff0c;原始文件下载 原文作者&#xff1a;Zico Kolter&#xff0c;修改&#xff1a;Chuong Do&#xff0c; Tengyu Ma 翻译&#xff1a;黄海广 备注&#xff1a;请关注github的更新&#xff0c;线性代数和概率论已经更新完毕…

ACL 2023 | 使用语言模型解决数学推理问题的协同推理框架

©PaperWeekly 原创 作者 | 朱欣宇 单位 | 清华大学 研究方向 | 自然语言处理 论文标题&#xff1a; Solving Math Word Problems via Cooperative Reasoning induced Language Models 论文链接&#xff1a; https://arxiv.org/abs/2210.16257 代码链接&#xff1a; https…

2023 互联网大厂薪资大比拼

最近整理了33家互联网大厂的薪资情况。可以看出来&#xff0c;大部分互联网大厂薪资还是很不错的&#xff0c;腾讯、阿里、美团、百度等大厂平均月薪超过30k&#xff0c;其他互联网大厂平均月薪也都在25k以上。01020304050607080910111213141516171819202122232425262728293031…

ESP8266(RTOS SDK)内嵌网页以实现WEB配网以及数据交互

【本文发布于https://blog.csdn.net/Stack_/article/details/131997098&#xff0c;未经允许不得转载&#xff0c;转载须注明出处】 1、执行make menuconfig&#xff0c;将http头由512改为更大的值&#xff0c;否则用电脑浏览器访问正常&#xff0c;但用手机浏览器访问会因为ht…

关于`IRIS/Caché`进程内存溢出解决方案

文章目录 关于IRIS/Cach进程内存溢出解决方案 描述原因相关系统变量$ZSTORAGE$STORAGE 什么情况下进程内存会变化&#xff1f;内存不足原理解决方案 关于 IRIS/Cach进程内存溢出解决方案 描述 在IRIS/Cach中&#xff0c;进程内存溢出错误是指一个进程&#xff08;例如运行中的…

群晖 nas 自建 ntfy 通知服务(梦寐以求)

目录 一、什么是 ntfy ? 二、在群晖nas上部署ntfy 1. 在Docker中安装ntfy 2. 设置ntfy工作文件夹 3. 启动部署在 docker 中的 ntfy&#xff08;binwiederhier/ntfy&#xff09; 三、启动配置好后&#xff0c;如何使用ntfy 1. 添加订阅主题&#xff08; Subscribe to topic…

六种不同的CRM系统类型分别有哪些特点?

企业想要管理销售&#xff0c;可以选择CRM系统&#xff1b;企业想要优化业务流程&#xff0c;可以选择CRM系统&#xff1b;企业想要提高收入&#xff0c;可以选择CRM系统。下面来说说&#xff0c;CRM是什么&#xff1f;六种常见CRM系统类型对比。 什么是CRM&#xff1f; CRM是…

当执行汇编指令MOV [0001H] 01H时,CPU都做了什么?

今天和几位单位大佬聊天时&#xff0c;讨论到一个非常有趣的问题-当程序执行MOV [0001H], 01H计算机实际上都做了哪些工作&#xff1f;乍一看这个问题平平无奇&#xff0c;CPU只是把立即数01H放在了地址为0001的内存里&#xff0c;但仔细想想这个问题远没有那么简单&#xff0c…

Synchronized八锁

/** * Description: 8 锁 * 1 标准访问&#xff0c;先打印短信还是邮件 ------sendSMS ------sendEmail 2 停 4 秒在短信方法内&#xff0c;先打印短信还是邮件 ------sendSMS ------sendEmail 3 新增普通的 hello 方法&#xff0c;是先打短信还是 hello ------getHello ------…

阿里云服务器IP地址查看方法(公网/私网)

阿里云服务器IP地址在哪查看&#xff1f;在云服务器ECS管理控制台即可查看&#xff0c;阿里云服务器IP地址包括公网IP和私有IP&#xff0c;阿里云百科分享阿里云服务器IP地址查询方法&#xff1a; 目录 阿里云服务器IP地址查询 阿里云服务器IP地址查询 1、登录到阿里云服务器…

HTML基础知识,网页和报表都可用

首先我们先介绍一下网页&#xff1a; 网页时构成网站的基本元素&#xff0c;它通常由图片&#xff0c;链接&#xff0c;文字&#xff0c;声音&#xff0c;视频等元素组成。通常我们看到的网页&#xff0c;常见以.htm或.html后缀结尾的文件&#xff0c;因此我们把它俗称为HTML文…

易服客工作室:Elementor AI简介 – 彻底改变您创建网站的方式

Elementor 作为领先的 WordPress 网站构建器&#xff0c;是第一个添加本机 AI 集成的。Elementor AI 的第一阶段将使您能够生成和改进文本和自定义代码&#xff08;HTML、自定义代码和自定义 CSS&#xff09;。我们还已经在进行以下阶段的工作&#xff0c;其中将包括基于人工智…

uniapp 使用 uni push 2.0 推送消息

因为之前使用uni push 1.0&#xff0c;开通账号和配置厂商就不写了。只说一点&#xff0c;配置厂商很重要&#xff0c;不然收不到离线推送的消息。那么就直接开始咯&#xff01;&#xff01;&#xff01; 一、创建并关联云服务空间 1.创建云服务空间&#xff0c;右键项目【创…

『C语言初阶』第八章 -隐式类型转换规则

前言 今天小羊又来给铁汁们分享关于C语言的隐式类型转换规则&#xff0c;在C语言中类型转换方式可分为隐式类型转换和显式类型转换(强制类型转换)&#xff0c;其中隐式类型转换是由编译器自动进行&#xff0c;无需程序员干预&#xff0c;今天小羊课堂说的就是关于隐式类型转换…

阻塞和非阻塞,同步和异步

文章目录 典型的一次IO的两个阶段IO多路复用是同步还是异步&#xff1f; 典型的一次IO的两个阶段 数据就绪和数据读写 同步&#xff1a;需要应用程序自己操作 IO多路复用是同步还是异步&#xff1f; epoll也是同步的 具体数据读取还是通过应用程序自己完成的 只有使用了特…

快速开发平台 WebBuilder 的功能特点

WebBuilder 是一款强大&#xff0c;全面和高效的应用开发和运行平台。基于浏览器的集成开发环境&#xff0c;智能化的设计&#xff0c;能轻松完成常规桌面应用和面向手机等的移动应用开发。高效、稳定和可扩展的特点&#xff0c;适合复杂企业级应用的运行。跨平台、数据库和浏览…

线性回归例子

转自&#xff1a;https://www.cnblogs.com/BlairGrowing/p/15061912.html 刚开始接触深度学习和机器学习&#xff0c;由于是非全日制&#xff0c;也没有方向感&#xff0c;缺乏学习氛围、圈子&#xff0c;全靠自己业余时间瞎琢磨&#xff0c;犹如黑夜中的摸索着过河。 只是顺…

易服客工作室:WordPress是什么?初学者的解释

目录 什么是WordPress&#xff1f; WordPress可以制作什么类型的网站&#xff1f; 谁制作了WordPress&#xff1f;它已经存在多久了&#xff1f; 谁使用 WordPress&#xff1f; 白宫网站 微软 滚石乐队 为什么要使用 WordPress&#xff1f; WordPress 是免费且…

【不支持发行版本 5】错误解决

说明&#xff1a;启动项目报下面的错误&#xff0c;不支持发行版本 5 解决&#xff1a;在pom文件中添加下面这两行配置&#xff0c;修改成你自己安装的jdk版本 <properties><maven.compiler.source>11</maven.compiler.source><maven.compiler.target&g…

TienChin 新建业务菜单

首先是移动菜单&#xff0c;参考下图将菜单移动到下图结构&#xff1a; 我这里将系统监控&#xff0c;系统工具都移动到了系统管理下面&#xff0c;并且排了个序&#xff0c;将多级菜单放在了一起&#xff0c;这样看起来更加的清晰。 修改一下系统管理(100)与TienChin健身官网(…