Nvidia 官方CUDA课程学习笔记

news2025/3/20 8:03:44

之前心血来潮学习了一下Nvidia CUDA,外行,文章有理解不当的地方,望指正。

主要根据以下Nvidia官方课程学习:
https://www.bilibili.com/video/BV1JJ4m1P7xW/?spm_id_from=333.337.search-card.all.click&vd_source=c256dbf86bb455b27cf32825685af0e6

一些参考链接:
https://developer.nvidia.com/zh-cn/blog/nvidia-hopper-architecture-in-depth/
https://zhuanlan.zhihu.com/p/597695921
https://www.cnblogs.com/shuimuqingyang/p/15846584.html
https://zhuanlan.zhihu.com/p/34587739
https://zhuanlan.zhihu.com/p/488340235?utm_oi=889446526974320640
https://blog.csdn.net/gzq0723/article/details/107343365

01 CUDA C Basics

CUDA(Compute Unified Device Architecture)是NVIDIA公司开发的一种编程模型和软件环境,它允许开发者使用C、C++、Python等高级语言来编写GPU程序。

CUDA 架构

  • 使GPU具备并行计算的能力
  • 为了高性能

CUDA C++

  • 工业标准C++(2014)
  • 提供扩展集合来使能异构计算
  • 更直接的API来管理设备和内存等

异构计算(Heterogeneous Computing)(以下为个人理解)
在同一个业务中(代码中),代码分别运行在两个不同的体系架构中,Host 部分运行在CPU(常用的操作系统: Windows,Linux),Device 部分运行在GPU(显卡或专用加速芯片)。这两部分各有各的资源,CPU常见的资源有内存,磁盘,IO设备等;GPU有内存等。
异构为CPU和GPU两部分运算

CPU 和 GPU之间使用传统的PCI-E接口或 NVLink 专用接口连接。
硬件连接架构
NVCC这个专用的编译器有两个体系的编译器,Host端的代码与平常的C/C++没有区别,NVCC会使用标准C/C++编译来编译,例如Linux下的GCC;有特别的关键字和语法来标识这是Device端的代码,NVCC会选用CUDA专用的编译器编译,如下图所示。

// 用__global__ 标识 Device 代码函数定义
__global__ void add(int *a, int *b, int *c) {
    c[blockIdx.x] = a[blockIdx.x] + b[blockIdx.x]
}

#define N 512
int main(void) {
    int *a, *b, *c;              // host 端
    int *d_a, *d_b, *d_c;        
    int size = N * sizeof(int);

    // 专用的API在Device端申请内存
    cudaMalloc((void **)&d_a, size);
    cudaMalloc((void **)&d_b, size);
    cudaMalloc((void **)&d_c, size);

    // Host 端申请内存,并用随机数初始化
    a = (int *)malloc(size); random_ints(a, N);
    b = (int *)malloc(size); random_ints(b, N);
    c = (int *)malloc(size);

    // 从Host 复制数据到 Device
    cudaMemcpy(d_a, a, size, cudaMemcpyHostToDevice);
    cudaMemcpy(d_b, b, size, cudaMemcpyHostToDevice);

    // 调用Device端(GPU)中的函数,运算
    add<<<N, 1>>>(d_a, d_b, d_c);

    // 从Device端复制到Host端
    cudaMemcpy(c, d_c, size, cudaMemcpyDeviceToHost);

    // Host 端回收内存
    free(a);free(b);free(c);

    // Device 端回收内存
    cudaFree(d_a);cudaFree(d_b);cudaFree(d_c);
}

以上代码,由于有__global__标识,add函数定义在Device端,其它代码定义在Host端。提供了一些专用的API,例如cudaMalloc,cudaMemcpy, cudaFree,个人理解这些代码跑在Host端,但是又能处理Device端的内存,有点奇怪。
在调用Device端的add函数时,使用"<<<x, y>>>"来标识Device端使用的block和Thread个数,这个概念在下面会讲到。代码运行过程基本分为三步:

  1. 从Host端(CPU)复制数据到Device端(GPU)。
  2. Device端执行该端代码。
  3. 结果从Device端复制回Host端。

Block 与 Thread

block与thread

为了并行运算,把资源分成多个Block, 每个Block又拥有多个Thread,每个Thread就是独立的运行单元。例如两个矩阵的加法运算,两个长度都是M,用传统方式可能是for循环里面运行了M次,时间上也是M次。如果用并行的方法,为每一个相加的运算分配一个Thread,那样时间上只是1次。

__global__ void add(int *a, int *b, int *c, int n) {
    // 有点像把二维数组的下标转为一维数据下标,这个下标就对应着线程ID
    int index = threadIdx.x + blockIdx.x + blockDim.x;
    if (index < n)
        c[index] = a[index] + b[index]
}

// 调用函数时指定了Block 个数和Thread个数
add<<<(N + M-1) / M, M>>>(d_a, d_b, d_c, N);

疑问:

  • 代码有跑在CPU部分和GPU部分,哪些代码跑CPU,哪些跑在GPU,那么怎么分配合理?

  • 在CPU的代码调用GPU的代码,会在那里死等,执行完了再返回?或者用户自己用多线程跑?或者回调?

  • 有pci-e和nvlink两种接口,中间传输的时间导致的延迟怎么办?
    答:视频里面肯定了这个延迟,肯定会比内存(Device 则)到GPU或内存(host 则)到CPU慢的。这个不在本节的讨论范围。

  • 视频里面提到智能指针的问题,貌似并不完全肯定支持,特别是数据传输时。
    答:视频意思是支持c++ 2014语言标准,但是标准库就不一定了。

  • CUDA 里面有 Thread, block, grid 的概念,这跟一个显卡有多少个CUDA核,或PC里面的进程,线程有什么区别和联系?

  • 视频里面有人提到,有的编译器long是4字节,有的是8字节,cuda怎么处理这种问题?
    答:应该在最初的配置里指定了与host端匹配的编译器,这样Device端与Host端的类型是一致的。

02 CUDA Share Memory

share_memory_radius

个人理解:
在许多应用场景,输入的数据有可能被下一次的计算重用到,例如窗口的移动,或者需要一些额外的数据参与计算,这时就需要Share Memory。它类似于X86 CPU里面的一级,二级缓存,是内嵌在GPU里面的。区别于Global Memory,它是在GPU外面,目前通常是DRAM,所以速率上有较大区别。Share Memroy的大小通常比较小,限制在48k bytes per thread block,以block为申请单位,block内线程共享,block间独立。

__global__ void stencil_1d(int *in, int *out){
    __shared__ int temp[BLOCK_SIZE + 2 * RADIUS];
    int gindex = threadIdx.x + blockIdx.x * blockDim.x;
    int lindex = threadIdx.x + RADIUS;

    // Read input elements into shared memory
    temp[lindex] = in[gindex];
    if (threadIdx.x < RADIUS) {
        temp[lindex - RADIUS] = in[gindex - RADIUS];
        temp[lindex + BLOCK_SIZE] = in[gindex + BLOCK_SIZE];
    }

    // Synchronize (ensure all the data is available)
    __syncthreads();

    // Apply the stencil
    int result = 0;
    for (int offset = -RADIUS; offset <= RADIUS; offset++) {
        result += temp[lindex + offset];
    }

    // Store the result
    out[gindex] = result;
}

要注意,block内的线程要做同步,例如上面代码的"__syncthreads();"。

一些问题

  • 如果有两个线程同一时间读取同一位置的数据,会怎么样?
    答:无论是全局内存还是共享内存,都会发生广播。性能不会有影响,功能就要程序员自己保证了。

共享内存支持静态方式和动态方式申请,性能没有区别。

03 CUDA Fundamental Optimization (CUDA 基础架构与优化)

3.1 GPU 与 CPU区别

CPU与GPU区别
我们经常看到CPU,GPU有同样的参数,往往都是GPU碾压CPU,例如晶体管的数量,核心数。是不是GPU厂商比CPU厂商强太多?答案是二者没啥可比性。
上图是一个经典的CPU与GPU对比图,能直观大概看出二者的区别,右边的GPU有众多的逻辑运算单元,控制管理单元也比CPU多,但是CPU的控制管理单元和逻辑运算单元都比较强大。GPU的核只能处理有限的几个指令,最常见是浮点运算(通常16位,32位,64位还分开不同的核),而CPU能处理的指令远比GPU复杂得多。控制单元也是,CPU是负责整个计算机系统的,指令的流式处理过程也比GPU复杂得多,例如GPU应该没有中断的处理。
最简单最核心的例子是,GPU某个核只能做32位浮点数加减乘除,另一个核只做向量运算。而CPU的一个核,从main函数开始到退出,能把你整个程序执行完。
GH100 流式多处理器
上图是H100的架构图,看上面绿色的小方块,INT32代表整形运算单元,FP32就是32位浮点数运算单元,FP64就是64位浮点运算单元,即程序里面的doule类型。而Tensor Core是最近几代产品专门为AI设计的运算单元,更适合向量和矩阵的运算。
每个张量核提供一个 4x4x4 矩阵处理数组,该数组执行运算 D = A * B + C ,其中 答:, B 、 C 和 D 是 4 × 4 矩阵,如下图所示。矩阵乘法输入 A 和 B 是 FP16 矩阵,而累加矩阵 C 和 D 可以是 FP16 或 FP32 矩阵。
矩阵运算
张量核对 FP16 输入数据进行 FP32 累加运算。对于 4x4x4 矩阵乘法, FP16 乘法会产生一个全精度的结果,该结果在 FP32 运算中与给定点积中的其他乘积累加,如下图所示。
矩阵运算流量
H100的部分参数如下:
H100部分特性参数
上面提到的FP32 CUDA 内核,张量核(Tensor Core)相当于CPU里面的ALU,甚至远不如后者复杂,拿二者的核心数比较没有意义。

3.2 grid, block, warp(32 threads), 与 cuda 物理核的关系

从第一章就了解到,最小的执行单元是Thread, 多个Threads组成一个Block,多个Block再组成一个Grid,而每32个Threads就叫一个warp,而这些都是逻辑上的概念,软件和编程时需要遵循这个模式,物理上有一套硬件体系与之对应,但并不相等。
CUDA编程逻辑和物理对应关系
GPU硬件的一个核心组件是SM(Streaming Multiprocessor,流式多路处理器)。SM的核心组件包括CUDA核心,共享内存,寄存器等,SM可以并发地执行数百个线程,并发能力就取决于SM所拥有的资源数。

当一个kernel被执行时,它的Gird中的线程块被分配到SM上,一个线程块只能在一个SM上被调度,SM一般可以调度多个线程块。grid只是逻辑层,而SM才是执行的物理层。SM采用的是SIMT (Single-Instruction, Multiple-Thread,单指令多线程)架构,基本的执行单元是线程束(warps),线程束包含32个线程,这些线程同时执行相同的指令,但是每个线程都包含自己的指令地址计数器和寄存器状态,也有独立的执行路径。所以尽管线程束中的线程同时从同一程序地址执行,但是可能具有不同的行为。
warp概念
当Block被划分到某个SM上时,它将进一步划分为多个线程束(warps),因为这才是SM的基本执行单元,但是一个SM同时并发的线程束数是有限的。这是因为资源限制,SM要为每个线程块分配共享内存,而也要为每个线程束中的线程分配独立的寄存器。由于SM的基本执行单元是包含32个线程的线程束,所以Block大小一般要设置为32的倍数。

04 GPU 体系架构
4.1 SM(Streaming Multiprocessor,流式多路处理器)

如前面介绍,GPU架构的核心是并发高速运算,再根据业务配置不同的逻辑核,例如整形,浮点,矢量运算核,SM和内存是两大关键。拥有共享内存,调度器,寄存器等资源,负责指令执行的生命周期,线程(warp)调度,内存资源管理等功能。一个SM可同时包含多个Block,一个Block只能存在同一个SM。
GPU架构图(Nvidia Tesla)
在开发者角度,一个block就是一组thread的组合。而硬件上,每个block只能由单个SM执行(单个SM可以执行多个block,视情况而定)。SM里每32个连续的thread组成一个warp。单个warp的执行指令时是基于SIMT(Single Instruction Multi Thread)。
SM执行指令过程
在CUDA里面,指令是保序执行,例如上图,一定是I0, I1, I2这样的指令顺序。而CPU就不一定保序。
如上图,warp0(W0)花了2个clock周期执行了I0和I1,但是数据从内存复制到寄存器需要时间,还不能立即执行I2,这时SM会安排执行W1,如此类推。当数据传输到寄存器后,SM又回头执行W0。
如果能把所有指令周期都利用上,这样效率就最高,所以编程时应当使用尽量多的线程,例如512个线程以上,理想是2048个这种数量级。

4.2 内存管理

和CPU类似,GPU硬件上的存储也分为global memory、cache及register。global memory主要是储存由cudaMalloc所分配的数据,另外还包括各种constant和texture。一般在显卡销售的宣传上指的显存xxGB(比如RTX 3090 24GB,GTX 1060 6GB)就是global memory。Cache主要是储存需要经常访问的数据和指令。除此之外,共享内存(shared memory)也是存放到cache(具体是L1 cache)。而register则是储存执行中或即将执行的数据和指令。
GPU 内存架构
访问速度方面,由慢到快依序是global memory->cache->register,而容量方面则是相反。
各类存储分类比较
数据加载和输出流程如下图:
数据加载和输出流程
由于高度并行所带来的高数据吞吐量,大部分CUDA程序都是访存密集型的。对于访存的优化通常比并行优化还有效,甚至可以获得成倍的速度提升。访存优化方面主要涉及数据的合并访问(coalesced memory access)及利用更高速的shared memory或texture。

合并访问(coalesced memory access)
这部分的优化主要是针对global memory的访问。为了提高访存效率,NVIDIA在设计显卡时引入合并访问的设计。当程序的某个thread需要访问global memory上的某个节点时,内存控制器会将该节点周围的数据一起加载到cache上。这些在cache上多余的数据只允许被同一个warp的线程所访问。其他warp需要访问这些数据时需要内存控制器重新把数据加载到cache上。
内存没有冲突
上图无冲突对比有冲突的下图:
内存冲突

相邻的thread尽可能访问global memory上相邻的节点,最理想的情况是内存地址与线程完全对齐,如上图。在对其的前提下,个别thread交错访问并不影响。这两者只需一次的加载就能完成。

(END)

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

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

相关文章

【AI News | 20250319】每日AI进展

AI Repos 1、XianyuAutoAgent 实现了 24 小时自动化值守的 AI 智能客服系统&#xff0c;支持多专家协同决策、智能议价和上下文感知对话&#xff0c;让我们店铺管理更轻松。主要功能&#xff1a; 智能对话引擎&#xff0c;支持上下文感知和专家路由阶梯降价策略&#xff0c;自…

一种基于大规模语言模型LLM的数据分析洞察生成方法

从复杂数据库中提取洞察对数据驱动决策至关重要,但传统手动生成洞察的方式耗时耗力,现有自动化数据分析方法生成的洞察不如人工生成的有洞察力,且存在适用场景受限等问题。下文将介绍一种新的方法,通过生成高层次问题和子问题,并使用SQL查询和LLM总结生成多表数据库中的见…

【npm ERR! code ERESOLVE npm ERR! ERESOLVE unable to resolve dependency tree】

npm ERR! code ERESOLVE npm ERR! ERESOLVE unable to resolve dependency tree npm ERR! code ERESOLVE npm ERR! ERESOLVE unable to resolve dependency tree 当我们拿到一个前端项目的时候&#xff0c;想要把它运行起来&#xff0c;首先是要给它安装依赖&#xff0c;即cd到…

用 pytorch 从零开始创建大语言模型(四):从零开始实现一个用于生成文本的GPT模型

从零开始创建大语言模型&#xff08;Python/pytorch &#xff09;&#xff08;四&#xff09;&#xff1a;从零开始实现一个用于生成文本的GPT模型 4 从零开始实现一个用于生成文本的GPT模型4.1 编写 L L M LLM LLM架构4.2 使用层归一化对激活值进行标准化4.3 使用GELU激活函数…

【新能源汽车“心脏”赋能:三电系统研发、测试与应用匹配的恒压恒流源技术秘籍】

新能源汽车“心脏”赋能&#xff1a;三电系统研发、测试与应用匹配的恒压恒流源技术秘籍 在新能源汽车蓬勃发展的浪潮中&#xff0c;三电系统&#xff08;电池、电机、电控&#xff09;无疑是其核心驱动力。而恒压源与恒流源&#xff0c;作为电源管理的关键要素&#xff0c;在…

目标检测20年(一)

今天看的文献是《Object Detection in 20 Years: A Survey》&#xff0c;非常经典的一篇目标检测文献&#xff0c;希望通过这篇文章学习到目标检测的基础方法并提供一些创新思想。 论文链接&#xff1a;1905.05055 一、摘要 1.1 原文 Object detection, as of one the most…

【MySQL数据库】存储过程与自定义函数(含: SQL变量、分支语句、循环语句 和 游标、异常处理 等内容)

存储过程&#xff1a;一组预编译的SQL语句和流程控制语句&#xff0c;被命名并存储在数据库中。存储过程可以用来封装复杂的数据库操作逻辑&#xff0c;并在需要时进行调用。 类似的操作还有&#xff1a;自定义函数、.sql文件导入。 我们先从熟悉的函数开始说起&#xff1a; …

WEB攻防-PHP反序列化-字符串逃逸

目录 前置知识 字符串逃逸-减少 字符串逃逸-增多 前置知识 1.PHP 在反序列化时&#xff0c;语法是以 ; 作为字段的分隔&#xff0c;以 } 作为结尾&#xff0c;在结束符}之后的任何内容不会影响反序列化的后的结果 class people{ public $namelili; public $age20; } var_du…

英伟达GTC 2025大会产品全景剖析与未来路线深度洞察分析

【完整版】3月19日&#xff0c;黄仁勋Nvidia GTC 2025 主题演讲&#xff5c;英伟达 英伟达GTC 2025大会产品全景剖析与未来路线深度洞察分析 一、引言 1.1 分析内容 本研究主要采用了文献研究法、数据分析以及专家观点引用相结合的方法。在文献研究方面&#xff0c;广泛收集了…

基于java的ssm+JSP+MYSQL的九宫格日志网站(含LW+PPT+源码+系统演示视频+安装说明)

系统功能 管理员功能模块&#xff1a; 个人中心 用户管理 日记信息管理 美食信息管理 景点信息管理 新闻推荐管理 日志展示管理 论坛管理 我的收藏管理 管理员管理 留言板管理 系统管理 用户功能模块&#xff1a; 个人中心 日记信息管理 美食信息管理 景点信息…

【Java】Mybatis学习笔记

目录 一.搭建Mybatis 二.Mybatis核心配置文件解析 1.environment标签 2.typeAliases 3.mappers 三.Mybatis获取参数值 四.Mybatis查询功能 五.特殊的SQL执行 1.模糊查询 2.批量删除 3.动态设置表名 4.添加功能获取自增的主键 六.自定义映射ResultMap 1.配置文件处…

遗传算法+四模型+双向网络!GA-CNN-BiLSTM-Attention系列四模型多变量时序预测

遗传算法四模型双向网络&#xff01;GA-CNN-BiLSTM-Attention系列四模型多变量时序预测 目录 遗传算法四模型双向网络&#xff01;GA-CNN-BiLSTM-Attention系列四模型多变量时序预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 基于GA-CNN-BiLSTM-Attention、CNN-BiL…

中兴B860AV3.2-T/B860AV3.1-T2_S905L3-B_2+8G_安卓9.0_先线刷+后卡刷固件-完美修复反复重启瑕疵

中兴电信B860AV3.2-T&#xff0f;B860AV3.1-T2_晶晨S905L3-B芯片_28G_安卓9.0_先线刷后卡刷-刷机固件包&#xff0c;完美修复刷机后盒子反复重启的瑕疵。 这两款盒子是可以通刷的&#xff0c;最早这个固件之前论坛本人以及其他水友都有分享交流过不少的固件&#xff0c;大概都…

《Python实战进阶》No27: 日志管理:Logging 模块的最佳实践(下)

No27: 日志管理&#xff1a;Logging 模块的最佳实践&#xff08;下&#xff09; 实战案例 &#xff1a;复杂场景下的 Logging 配置与使用 本实战案例在 Python 3.11.5环境下运行通过 在本案例中&#xff0c;我们将通过一个复杂的日志配置示例&#xff0c;全面展示 logging 模…

Web 小项目: 网页版图书管理系统

目录 最终效果展示 代码 Gitee 地址 1. 引言 2. 留言板 [热身小练习] 2.1 准备工作 - 配置相关 2.2 创建留言表 2.3 创建 Java 类 2.4 定义 Mapper 接口 2.5 controller 2.6 service 3. 图书管理系统 3.1 准备工作 - 配置相关 3.2 创建数据库表 3.2.1 创建用户表…

【Dive Into Stable Diffusion v3.5】1:开源项目正式发布——深入探索SDv3.5模型全参/LoRA/RLHF训练

目录 1 引言2 项目简介3 快速上手3.1 下载代码3.2 环境配置3.3 项目结构3.4 下载模型与数据集3.5 运行指令3.6 核心参数说明3.6.1 通用参数3.6.2 优化器/学习率3.6.3 数据相关 4 结语 1 引言 在人工智能和机器学习领域&#xff0c;生成模型的应用越来越广泛。Stable Diffusion…

《Waf 火绒终端防护绕过实战:系统程序副本+Certutil木马下载技术详解》

目录 绕过火绒终端安全软件的详细方法 方法一&#xff1a;利用系统程序副本绕过命令监控 方法二&#xff1a;结合certutil.exe副本下载并执行上线木马 注意事项 总结 实际案例解决方案 前提条件 详细操作步骤 1. 攻击主机&#xff08;VPS&#xff09;上的准备工作 2.…

上海高考解析几何

解析几何的核心思想。 1. 核心分析方法&#xff1a; 自由度引入 方程组中&#xff0c; n n n 个未知数需要 n n n 个等式来解出具体的值。 自由度 性质 一个未知数带来一个自由度&#xff0c;一个等式条件减少一个自由度&#xff08;减少自由度的方式为消元&#xff09;。…

【AVRCP】服务发现互操作性:CT 与 TG 的 SDP 协议契约解析

目录 一、服务发现的核心目标&#xff1a;能力画像对齐 二、控制器&#xff08;CT&#xff09;服务记录&#xff1a;控制能力的声明 2.1 必选字段&#xff1a;角色与协议的刚性契约 2.1.1 服务类标识&#xff08;Service Class ID List&#xff09; 2.1.2 协议描述列表&am…

MySQL:数据库基础

数据库基础 1.什么是数据库&#xff1f;2.为什么要学习数据库&#xff1f;3.主流的数据库&#xff08;了解&#xff09;4.服务器&#xff0c;数据库&#xff0c;表之间的关系5.数据的逻辑存储6.MYSQL架构7.存储引擎 1.什么是数据库&#xff1f; 数据库(Database,简称DB)&#x…