cuda编程学习——CUDA内存介绍(七)

news2024/11/16 18:31:03

前言

参考资料:

高升博客
《CUDA C编程权威指南》
以及 CUDA官方文档
CUDA编程:基础与实践 樊哲勇

文章所有代码可在我的GitHub获得,后续会慢慢更新

文章、讲解视频同步更新公众《AI知识物语》,B站:出门吃三碗饭

1:内存组织介绍

现代计算机中的内存往往存在一种组织结构(hierarchy)。在这种结构中,含有多种类 型的内存,每种内存分别具有不同的容量和延迟(latency,可以理解为处理器等待内存数据的时间)。一般来说,延迟低(速度高)的内存容量小,延迟高(速度低)的内存容量大。
在这里插入图片描述
在这里插入图片描述

2:CUDA 不同类型的内存

2.1全局内存:

(1)其含义是核函数中的所有线程都能够访问其中的数 据,和C++中的“全局变量”不是一回事。我们已经用过这种内存,在数组相加的例子中, 指针 d_x、d_y 和 d_z 都是指向全局内存的。全局内存由于没有存放在GPU的芯片上,因此具有较高的延迟和较低的访问速度。

(2)全局内存的主要作用是为核函数提供数据,并在主机与设备及设备与设备之间传递数 据。首先,我们用cudaMalloc 函数为全局内存变量分配设备内存。然后,可以直接在核函数中访问分配的内存,改变其中的数据值。
(3)全局内存对整个网格的所有线程可见。也就是说,一个网格的所有线程都可以访问(读 或写)传入核函数的设备指针所指向的全局内存中的全部数据。
(4)全局内存的生命周期(lifetime)不是由核函数决定的,而是由主机端决定的。在数组 相加的例子中,由指针 d_x、d_y 和 d_z 所指向的全局内存缓冲区的生命周期就是从主机端用 cudaMalloc 对它们分配内存开始,到主机端用 cudaFree 释放它们的内存结束。

2.2静态全局内存:

静态全局内存变量由以下方式在任何函数外部定义:

__device__ T x; // 单个变量 
__device__ T y[N]; // 固定长度的数组

其中,修饰符 device 说明该变量是设备中的变量,而不是主机中的变量;T 是变量的 类型;N是一个整型常数。

在核函数中,可直接对静态全局内存变量进行访问,并不需要将它们以参数的形式传给核 函数。不可在主机函数中直接访问静态全局内存变量,但可以用 cudaMemcpyToSymbol 函
数和 cudaMemcpyFromSymbol 函数在静态全局内存与主机内存之间传输数据。

2.3常量内存:

常量内存(constant memory)是有常量缓存的全局内存,数量有限,一共仅有 64 KB。 它的可见范围和生命周期与全局内存一样。不同的是,常量内存仅可读、不可写。由于有 缓存,常量内存的访问速度比全局内存高,但得到高访问速度的前提是一个线程束中的线程(一个线程块中相邻的 32 个线程)要读取相同的常量内存数据。

一个使用常量内存的方法是在核函数外面用 constant 定义变量,并用前面介绍 的CUDA运行时API函数cudaMemcpyToSymbol将数据从主机端复制到设备的常量内存后供核函数使用。

在数组相加的例子中,核函数的参数 const int N 就是在主机端定义的变量,并通过传值的方式传送给核函数中的线程 使用。在核函数中的代码段 if (n < N) 中,这个参数 N 就被每一个线程使用了。所以, 核函数中的每一个线程都知道该变量的值,而且对它的访问比对全局内存的访问要快。除给核函数传递单个的变量外,还可以传递结构体,同样也是使用常量内存。

2.4纹理内存和表面内存

纹理内存(texture memory)和表面内存(surface memory)类似于常量内存,也是一 种具有缓存的全局内存,有相同的可见范围和生命周期,而且一般仅可读(表面内存也可写)。不同的是,纹理内存和表面内存容量更大,而且使用方式和常量内存也不一样。

2.5寄存器

在核函数中定义的不加任何限定符的变量一般来说就存放于寄存器(register)中。 核函数中定义的不加任何限定符的数组有可能存放于寄存器中,但也有可能存放于 局部内存中。另外,以前提到过的各种内建变量,如 gridDim、blockDim、blockIdx、 threadIdx 及 warpSize 都保存在特殊的寄存器中。在核函数中访问这些内建变量是很高效的。

const int n = blockDim.x * blockIdx.x + threadIdx.x;

这里的 n 就是一个寄存器变量。寄存器可读可写。上述语句的作用就是定义一个寄存器变 量 n 并将赋值号右边计算出来的值赋给它(写入)。在稍后的语句

z[n] = x[n] + y[n];

中,寄存器变量 n 的值被使用(读出)。
寄存器变量仅仅被一个线程可见。也就是说,每一个线程都有一个变量 n 的副本。虽 然在核函数的代码中用了这同一个变量名,但是不同的线程中该寄存器变量的值是可以不
同的。

2.6局部内存

局部内存和寄存器几乎一 样。核函数中定义的不加任何限定符的变量有可能在寄存器中,也有可能在局部内存中。寄存器中放不下的变量,以及索引值不能在编译时就确定的数组,都有可能放在局部内存中。
在这里插入图片描述
这种判断是由编译器自动做的。对于数组相加例子中的变量 n 来说,作者可以肯定它在寄 存器中,而不是局部内存中,因为核函数所用寄存器数量还远远没有达到上限。

2.7共享内存

共享内存和寄存器类似,存在于芯片 上,具有仅次于寄存器的读写速度,数量也有限。表 6.2 列出了与几个计算能力对应的共享 内存数量指标。

不同于寄存器的是,共享内存对整个线程块可见,其生命周期也与整个线程块一致。也就是说,每个线程块拥有一个共享内存变量的副本。共享内存变量的值在不同的线程块中 可以不同。一个线程块中的所有线程都可以访问该线程块的共享内存变量副本,但是不能访问其他线程块的共享内存变量副本。

2.8 L1 、L2缓存

从费米架构开始,有了SM层次的L1缓存(一级缓存)和设备(一个设备有多个SM) 层次的 L2 缓存(二级缓存)。它们主要用来缓存全局内存和局部内存的访问,减少延迟。

3:流多处理器SM

一个 GPU 是由多个SM构成的。一个SM包含如下资源:
• 一定数量的寄存器。
• 一定数量的共享内存。
• 常量内存的缓存。
• 纹理和表面内存的缓存。
• L1 缓存。
• 两个(计算能力 6.0)或 4 个(其他计算能力)线程束调度器(warp scheduler),用于 在不同线程的上下文之间迅速地切换,以及为准备就绪的线程束发出执行指令。
• 执行核心,包括:
– 若干整型数运算的核心(INT32)。
– 若干单精度浮点数运算的核心(FP32)。
– 若干双精度浮点数运算的核心(FP64)。
– 若干单精度浮点数超越函数(transcendental functions)的特殊函数单元(Special Function Units,SFUs)。
– 若干混合精度的张量核心(tensor cores,由伏特架构引入,适用于机器学习中的
低精度矩阵计算,本书不讨论)。

SM占有率

因为一个 SM 中的各种计算资源是有限的,那么有些情况下一个 SM 中驻留的线程数 目就有可能达不到理想的最大值。此时,我们说该SM的占有率小于 100%。获得 100%的 占有率并不是获得高性能的必要或充分条件,但一般来说,要尽量让 SM 的占有率不小于某个值,比如 25%,才有可能获得较高的性能。

4:用CUDA运行时 API 函数查询设备

#include <stdio.h>

#define CHECK(call)                                   \
do                                                    \
{                                                     \
    const cudaError_t error_code = call;              \
    if (error_code != cudaSuccess)                    \
    {                                                 \
        printf("CUDA Error:\n");                      \
        printf("    File:       %s\n", __FILE__);     \
        printf("    Line:       %d\n", __LINE__);     \
        printf("    Error code: %d\n", error_code);   \
        printf("    Error text: %s\n",                \
            cudaGetErrorString(error_code));          \
        exit(1);                                      \
    }                                                 \
} while (0)


int main(int argc, char* argv[])
{
    int device_id = 0;
    if (argc > 1) device_id = atoi(argv[1]);
    CHECK(cudaSetDevice(device_id));

    cudaDeviceProp prop;
    CHECK(cudaGetDeviceProperties(&prop, device_id));

    printf("Device id:                                 %d\n",
        device_id);
    printf("Device name:                               %s\n",
        prop.name);
    printf("Compute capability:                        %d.%d\n",
        prop.major, prop.minor);
    printf("Amount of global memory:                   %g GB\n",
        prop.totalGlobalMem / (1024.0 * 1024 * 1024));
    printf("Amount of constant memory:                 %g KB\n",
        prop.totalConstMem / 1024.0);
    printf("Maximum grid size:                         %d %d %d\n",
        prop.maxGridSize[0],
        prop.maxGridSize[1], prop.maxGridSize[2]);
    printf("Maximum block size:                        %d %d %d\n",
        prop.maxThreadsDim[0], prop.maxThreadsDim[1],
        prop.maxThreadsDim[2]);
    printf("Number of SMs:                             %d\n",
        prop.multiProcessorCount);
    printf("Maximum amount of shared memory per block: %g KB\n",
        prop.sharedMemPerBlock / 1024.0);
    printf("Maximum amount of shared memory per SM:    %g KB\n",
        prop.sharedMemPerMultiprocessor / 1024.0);
    printf("Maximum number of registers per block:     %d K\n",
        prop.regsPerBlock / 1024);
    printf("Maximum number of registers per SM:        %d K\n",
        prop.regsPerMultiprocessor / 1024);
    printf("Maximum number of threads per block:       %d\n",
        prop.maxThreadsPerBlock);
    printf("Maximum number of threads per SM:          %d\n",
        prop.maxThreadsPerMultiProcessor);

    return 0;
}

在这里插入图片描述

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

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

相关文章

Linux 中断子系统中GIC 中断控制器基本分析

GIC 是 ARM 公司给 Cortex-A/R 内核提供的一个中断控制器&#xff0c;类似 Cortex-M 内核&#xff08;STM32&#xff09;中的 NVIC。 GIC&#xff1a;Generic Interrupt Controller&#xff0c;通用中断控制器。 NVIC&#xff1a;Nested Vectored Interrupt Controller&#…

RTX4060Ti上演史诗级尴尬,线下核心商区斩获个位数销量

时隔两年半&#xff0c;一代甜品 RTX 3060 Ti 显卡继任者 RTX 4060 Ti 终于上架了。 经过几天网上对比测试&#xff0c;情况就这么个情况&#xff0c;综合性能提升堪堪 10% 左右&#xff0c;价格上涨至 3199 元起。 来源&#xff1a;clubic 参考 RTX 4090 超 50% 性能提升&am…

聊聊 220V交流 过零检测

聊聊过零检测&#xff0c;以及如何实现过零检测 ...... by 矜辰所致目录 前言一、什么是过零检测1.1 为何需要过零检测 二、如何做过零检测2.1 光耦2.2 比较器/运放2.3 三极管/MOS管2.4 过零检测芯片 三、过零检测电路结语 前言 最近正好项目需求遇到需要做过零检测&#xff…

【刷题之路Ⅱ】LeetCode 1823. 找出游戏的获胜者(约瑟夫问题)

【刷题之路Ⅱ】LeetCode 1823. 找出游戏的获胜者 一、题目描述二、解题1、方法1——单向环形链表1.1、思路分析1.2、代码实现 2、方法2——队列2.1、思路分析2.2、先将队列实现一下2.3、代码实现 一、题目描述 原题连接&#xff1a; 1823. 找出游戏的获胜者 题目描述&#xff…

Java基础篇 | Java基础语法

✅作者简介&#xff1a;大家好&#xff0c;我是Cisyam&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Cisyam-Shark的博客 &#x1f49e;当前专栏&#xff1a; Java从入门到精通 ✨特色…

2023年上半年网络工程师上午真题及答案解析

1.固态硬盘的存储介质是( )。 A.光盘 B.闪存 C.软盘 D.磁盘 2.虚拟存储技术把( )有机地结合起来使用&#xff0c;从而得到一个更大容量的“内存”。 A.内存与外存 B.Cache与内存 C.寄存器与Cache D.Cache与外存 3.下列接口协议中&…

论文阅读 —— 语义激光SLAM

文章目录 点云语义分割算法1 基于点的方法2 基于网格的方法3 基于投影的方法 一、SLOAM1.1 语义部分1.2 SLAM部分1.2.1 树的残差1.2.2 地面的残差1.2.3匹配过程 二、SSC: Semantic Scan Context for Large-Scale Place Recognition2.1 两步全局语义ICP2.1.1 快速偏航角计算2.1.…

【已解决---ChatGPT学术优化下载安装问题集锦】

文章目录 问题1&#xff1a;关于配置完项目后关闭&#xff0c;如何再次打开快速启动。问题2&#xff1a;项目链接打不开,是404。问题3&#xff1a;出现关于API的报错。问题4&#xff1a;[Local Message] Request timeout. Network error. Please check proxy settings in confi…

8.Ansible Variables介绍

什么是Ansible Variables&#xff1f; 就像任何其他脚本或编程语言一样&#xff0c;变量用于存储变化的值&#xff61;例如, 假设我们要尝试执行相同的操作, 将修补程序应用于数百台服务器&#xff61;我们只需要一个playbook就可以满足所有100台服务器的需求&#xff61;但是,…

chatgpt赋能python:Python中提取指定元素——一个简单而精细的方法

Python中提取指定元素——一个简单而精细的方法 在网页抓取中&#xff0c;经常需要提取特定元素&#xff0c;例如标题、段落、图片等&#xff0c;以便于后续的数据处理与分析。而Python则是许多工程师在此领域中的首选语言&#xff0c;其灵活的语法和强大的第三方库给爬虫和数…

idea怎么搭建springboot

一般来说&#xff0c;用IDEA创建SpringBoot项目有两种方式。其一是Spring官网提供了一个快速生成SpringBoot项目的网站&#xff0c;可以在官网直接下载后&#xff0c;再导入IDEA中。另外一种是直接用IDEA创建一个SpringBoot项目&#xff0c;一般开发也是用的这种方式进行创建。…

为什么串行接口速率比并行接口快?

串行接口的速率会比并行快&#xff0c;可以从下面四个方面考虑&#xff1a; ①高速串口不需要时钟信号来同步数据流&#xff0c;也就没有时钟周期性的边沿&#xff0c;频谱不会集中&#xff0c;所以噪声干扰少很多。 以PCIE和SATA为例&#xff0c;时钟信息通过8b/10b编码已经集…

正运动技术运动控制器如何快速实现单轴/多轴同步跟随功能?

本文主要介绍如何使用MOVESYNC指令快速实现单轴/多轴同步跟随功能&#xff0c;适用于XYZ&#xff08;R&#xff09;、SCARA、DELTA等常见机械结构&#xff0c;在流水线点胶、流水线产品分拣、流水线产品搬运等场景中广泛应用。 阅读本文&#xff0c;学习同步跟随的原理和实现方…

抖音seo源码系统开发服务商选择

“账号矩阵”是一种账号运营的高阶玩法&#xff0c;指一个运营主体同时开设多个平台多个账号利用品牌联动的形式来实现账号之间的相关引流&#xff0c;以账号组的形式实现企业营销价值最大化。那么运营多个账号&#xff0c;短视频平台内容是核心&#xff0c;势必要招募多个剪辑…

RK平台使用IO指令

简介 RK平台开发过程经常要用到IO指令&#xff0c;主要是用来读写CPU各个模块寄存器的值&#xff0c;从而实现在线调试。 RK平台的SDK默认有包含IO指令的源码&#xff0c;如果执行的时候找不到指令&#xff0c;可能是没有编译进去&#xff0c;找到对应的编译脚本编译进去即可。…

Dream音频芯片开发虚拟环绕声算法概论

1 项目需求 2 开发平台介绍 Dream S.A.S France公司网站&#xff1a;https://www.dream.fr Dream全系列的芯片包含SAM2000 series ICs、SAM3000 series ICs以及SAM5000 series ICs。 SAM5000 series ICs包括 sam5504、sam5704、sam5708、sam5808、sam5716、sam5916。 目前drea…

为什么电源纹波那么大?

某用户在用500MHz带宽的示波器对其开关电源输出5V信号的纹波进行测试时&#xff0c;发现纹波和噪声的峰峰值达到了900多mV&#xff08;如下图所示&#xff09;&#xff0c;而其开关电源标称的纹波的峰峰值<20mv。虽然用户电路板上后级还有LDO对开关电源的这个输出再进行稳压…

出学校干了 5 年外包,已经废了

如果不是女朋友和我提分手&#xff0c;我估计现在还没醒悟 本科大专&#xff0c;17年通过校招进入某软件公司做测试&#xff0c;干了接近5年的功能。 今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落&#xff01;而我已经…

Spring面向切面编程(AOP)

Spring面向切面编程&#xff08;AOP&#xff09; 概念 AOP&#xff08;Aspect Oriented Programming&#xff09;&#xff0c;即面向切面编程&#xff0c;利用一种称为"横切"的技术&#xff0c;剖开封装的对象内部&#xff0c;并将那些影响了多个类的公共行为封装到…

DICOM笔记-CT图像的边界

常见CT图像在有效范围内都是有效CT值。 对CT值的处理也就仅限于做斜率和截距的线性处理&#xff1b; 可参加常用的DICOM标签信息&#xff1a; DICOM笔记-DICOM常用Tag标签汇总_dicom tag列表_黑山老妖的博客的博客-CSDN博客文件引言MetaInfoGroupElementTag Description中文解…