关键词:
0 软件抽象和硬件结构对应关系的例子
把GPU
跟一个学校对应起来,学校里有教学楼、操场、食堂,还有老师和学生们;很快有领导(CPU
)来检查卫生(需要执行的任务Host程序
),因此这个学校的学生们要完成打扫除的工作(Device程序
)。
软件抽象资源包括Thread
、Warp
、Block
和Grid
硬件资源包括SP
和SM
0.1 软件抽象
Grid
对应的是年级
是抽象的划分组织方式
根据年级划分任务,Grid可以分为多个不同的班级
Block
对应的是班级
是抽象的划分组织方式
每个班级有若干的同学(线程),可能一个两个不同的年级会出现在同一层楼(SM),或者一层楼只有一个班级,或者没有班级,但是每一层楼的班级最大数量是固定的
Warp
对应的是兴趣小组
每个小组有32个学生;(同一时间他们一定是一个班级下的小组)
并且数量固定,即使凑不满这么多学生需要加进来不干活的学生,凑够一个小组
只要求他们有着一样的兴趣爱好(能执行相同的任务)
Thread
对应的是学生
一个Thread对应一个SP
每个学生都有个课桌 ,放自己的物品,不能让别人用,表示每个Thread在软件上都有自己的空间(寄存器等)
0.2 硬件资源
SM
对应的是教学楼的一个楼层
是实际存在的资源
一个楼层上可以有多个班级,年级和楼层并没有确定的对应关系,一个楼层中可以有很多来自不同的年级的Block
SM中的SP会被分成兴趣小组,承接不同的任务
SP
对应的是学生
一个SP对应一个Thread
是实际存在的资源
每个学生都有个课桌 ,放自己的物品,不能让别人用,表示每个SP在硬件上都有自己的空间(local memory + registers);
在楼层中,有公共的空间(走廊、厕所等),这一层楼的所有同学都可以停留,表示一个SM中有shared memory,这个SM上的Block都可以访问;(shared memory是不是所有的block都可以访问)
学校里的公共区域,比如操场、食堂等,所有同学都可以去运动、吃饭,表示GPU中有一些公共的存储空间供所有的Grid访问。
0.3 执行任务
虽然GPU
是并行运行,但也并不是我们理想中所有的Thread
一起工作,在打扫卫生时,并不是所有学生一起干活,学生经过老师(这里我们理解为Wrap Scheduler
)安排后,分为一组一组的小组,每一个小组都只会做一件一样的事情,如果有人先做完了或者不需要做,那么他也会在旁边等他的组员,处于等待状态idle
。
1 GPU不同存储的辨析
1.1 总述
这一点跟CPU
比较像,就是存储空间越大,访问速度越慢。
GPU
越靠近SM
的内存就越快。内存的访问速度从快到慢依次为:
Registers
->Caches
->Shared Memory
->Gloabl Memory(Local Memory)
。
1.1.1 GPU存储与CPU存储
CPU
的典型存储结构如下:
一般来说,CPU
和内存之间的带宽只有数十GB/s。比如对于Intel Xeon E5-2699 v3,内存带宽达到68GB/s((2133 * 64 / 8)*4 MB/s):
内存规格 | |
---|---|
最大内存大小(取决于内存类型) | 768 GB |
内存类型 | DDR4 1600/1866/2133 |
最大内存通道数 | 4 |
最大内存带宽 | 68 GB/s |
GPU
的存储结构一般如下:
GPU
的高速缓存较小,上图的Memory
实际上是指GPU
卡内部的显存。但是与显存之间的带宽可以达到数百GB/s,比如P40的显存带宽为346GB/s,远远大于CPU
的内存带宽,但是,相对于GPU
的计算能力,显存仍然是瓶颈所在。
1.1.3 CPU与GPU交互
在现代的异构计算系统中,GPU
是以PCIe卡
作为CPU
的外部设备存在,两者之间通过PCIe总线
通信:
---------- ------------
|___DRAM___| |___GDRAM____|
| |
---------- ------------
| CPU | | GPU |
|__________| |____________|
| |
--------- --------
|___IO____|---PCIe---|___IO___|
对于PCIe Gen3 x1
理论带宽约为1000MB/s,所以对于Gen3 x32
的最大带宽为32GB/s,而受限于本身的实现机制,有效带宽往往只有理论值的2/3还低。所以,CPU
与GPU
之间的通信开销是比较大的。
1.2 Registers
- 寄存器是访问速度最快的空间。
- 当我们在核函数中不加修饰的声明一个变量,那该变量就是寄存器变量,如果在核函数中定义了常数长度的数组,那也会被分配到
Registers
中;寄存器变量是每个线程私有的,当这个线程的核函数执行完成后,寄存器变量也就不能访问了。 - 寄存器是比较稀缺的资源,空间很小,
Fermi架构
中每个线程最多63个寄存器,Kepler架构
每个线程最多255个寄存器;一个线程中如果使用了比较少的寄存器,那么SM
中就会有更多的线程块,GPU
并行计算速度也就越快。 - 如果一个线程中变量太多,超出了
Registers
的空间,这时寄存器就会发生溢出,就需要其他内存(Local Memory
)来存储,当然程序的运行速度也会降低。 - 因此,在程序中,对于那种循环操作的变量,我们可以放到寄存器中;同时要尽量减少寄存器的使用数量,这样线程块的数量才能增多,整个程序的运行速度才能更快。
1.3 Local Memory
Local Memory
也是每个线程私有的,但却是存储在于Global Memory
中的。在核函数中符合存储在寄存器中但不能进入核函数分配的寄存器空间中的变量将被存储在Local Memory
中,Local Memory
中可能存放的变量有以下几种:
- 使用未知索引的本地数组
- 较大的本地数组或结构体
- 任何不满足核函数寄存器限定条件的变量
1.4 Shared Memory
每个SM
中都有共享内存,使用__shared__
关键字(CUDA关键字
的下划线一般都是两个)定义,共享内存在核函数中声明,生命周期和线程块一致。
同样需要注意的是,SM
中共享内存使用太多,会导致SM
上活跃的线程数量减少,也会影响程序的运行效率。
数据的共享肯定会导致线程间的竞争,可以通过同步语句来避免内存竞争,同步语句为:
void __syncthreads();
当所有线程都执行到这一步时,才能继续向下执行;频繁调用__syncthreads()
也会影响核函数的执行效率。
共享内存因为需要分配给不同的线程所以被分成了不同个Bank
,一个Warp
中有32个线程,在比较老的GPU中,16个Bank
可以同时互相访问,即一条指令就可以让半个Warp
同时访问16个Bank
,这种并行访问的效率可以极大的提高GPU
的效率。比较新的GPU
中,一个Warp
即32个SP
可以同时访问32个Bank
,效率又提升了一倍。
下面这个图中:
左边的图每个线程访问一个
Bank
,不存在内存冲突,通过一个指令即可完成访问所有的访问操作;
中间的图虽然看起来有些乱,但还是一个线程对应一个Bank
,也不存在冲突,一个指令即可完成。
右边的图中,存在多个Thread
访问一个Bank
的情况,如果是读操作,那么GPU
底层可以通过广播的方式将数据传给各个Thread
,延迟不会很大,但如果是写操作,就必须要等上一个线程写完成后才能进行下一个线程的写操作,延时会比较大。
1.5 Constant Memroy
常量内存驻留在设备内存中,每个SM
都有专用的常量内存空间,使用__constant__
关键字来声明,可以用来声明一些滤波系数等常量。
常量内存存在于核函数之外,在kernel函数外声明,即常量内存存在于内存中,并不在片上,常量内容的访问速度也是很快的,这是因为每个SM
都有专用的常量内存缓存,会把片外的常量读取到缓存中;对所有的核函数都可见,在Host
端进行初始化后,核函数不能再修改。
1.6 Texture Memory
纹理内存的使用并不多,它是为了GPU
的显示而设计的,这里不多讲了。纹理内存也是存在于片外。
1.7 Global Memory
全局内存,就是我们常说的显存,就是GDDR的空间,全局内存中的变量,只要不销毁,生命周期和应用程序是一样的。
在访问全局内存时,要求是对齐的,也就是一次要读取指定大小(32、64、128)整数倍字节的内存,数据对齐就意味着传输效率降低,比如我们想读33个字节,但实际操作中,需要读取64字节的空间。
对于Global
和Constant
,Host
可以通过下面的函数访问:
cudaGetSymbolAddress()
cudaGetSymbolSize()
cudaMemcpyToSymbol()
cudaMemcpyFromSymbol()
1.8 GPU缓存
每个SM
都有一个一级缓存,所有SM
公用一个二级缓存,GPU
读操作是可以使用缓存的,但写操作不能被缓存。
L1 Cache
:Pascal架构上,L1 Cache
和Texture已经合为一体(Unified L1/Texture Cache),作为一个连续缓存供给warp使用。
L2 Cache
:用来做Global Memory
的缓存,容量大,给整个GPU
使用。
每个SM
有一个只读常量缓存,只读纹理缓存
,它们用于设备内存中提高来自于各个内存空间内的读取性能。
讲到缓存,我们就必须要提一点,CPU和GPU在缓存上的一个重要差别就是“缓存一致性”(cache coherency
) 问题。缓存一致是指一个内存的写操作需要通知所有核的各个级别的缓存,因此,无论何时,所有处理器核看到的内存视图是完全一样的。随着处理器中核数量的增多,这个“通知”的开销迅速增大,使得“缓存一致性”成为限制一个处理器中核数不能太多的一个重要因素。“缓存一致”系统中最坏的情况是,一个内存写操作会强迫每个核的缓存都进行更新,进而每个核都要对相邻的内存单元进行写操作。
CPU遵循“缓存一致”原则,而GPU不是。在GPU中系统不会自动的更新其他核的缓存。所以GPU能扩展到一个芯片内具有大数量的核心。它需要由程序员写清楚每个处理器核输出的各自不同的目标区域。从程序的视角看,这支持一个核仅负责一个输出或者一个小的输出集。
1.9 总结如下:
存储器 | 作用域 | 声明期 |
---|---|---|
Register | Thread | Kernel |
Local Memory | Thread | Kernel |
Shared Memory | Block | Kernel |
Global Memory | Grid | Application |
Constant | Grid | Application |
2 内存与软件硬件的一一对应
2.1 Thread
- 每一个
Thread
都有自己的local memory
和Registers
即每个同学都可以把自己的东西放到自己的课桌上,别的同学不可以使用;
Local Memory
,它是每个线程专有的线程,但却是存在于Global Memory
中的,结合我们在第0节例子中拿学校和学生举的例子,可以理解为:学生的课桌都放满了,只能在操场里给他再找个地方放东西,所以访问速度是很慢的,但是这部分还是属于他的local memory
,别的线程应该是访问不了的。
2.2 Block
- 每一个
Block
有自己的shared memory
,构成Block
的所有Thread
都可以访问。可以被线程中所有的线程共享,其生命周期与线程块一致
即每个班所在的教室里的走道、讲台等,是这个班里同学们的公共区域,别的班级的同学不能进入;
2.3 Grid
Grid
之间会有Global memory
和Cache
所有的Grid
都可以访问,即学校里的操场、餐厅等,是全校同学的公共区域,所有年级的同学都可以共享。
所有的thread
(包括不同block
的thread
)都共享一份 global memory
、constant memory
、和texture memory
。所有的线程都可以访问全局内存(Global Memory
)
2.4 Warp
每一个时钟周期内,Warp
(一个block
里面一起运行的thread
,其中各个线程对应的数据资源不同,因为指令相同但是数据不同)现在规定的thread
数量是32个。一个block
中最多含有16个warp
。所以一个block
中最多含有512个线程。
2.5 其他存储
- 只读内存块:常量内存(
Constant Memory
)和纹理内存(Texture Memory
) - 每个
SM
有自己的L1 cache
,SM
通过L2 cache
连接到Global Memory
3 SM中的存储实现细节
GPU
基本处理单元是流多处理器,有关处理单元介绍,之前的文章中有叙述。这篇主要讲存储结构
3.1 寄存器
- 流处理器先与寄存器交换数据,寄存器负责存储指令,以及指令计算所需的变量。寄存器速度最高,与核心同频。可以无延迟的让
Core
执行指令运算。
3.2 L1、L2和SMEM
L1
和SMEM(Shared Memory)
其实算作一个东西,或者准确来讲,在一同一块芯片区域当中。只是根据程序需要,决定哪一部分划分成L1缓存
,哪一部分划分成Shared Memory
,这个操作是动态的。L1 cache
负责缓存内存地址,而SMEM
负责缓存计算所需的变量(包括顶点数据,纹理数据,以及计算引入的中间变量等)。L1/SMEM
缓存频率非常高,延迟非常低,而且访问这个东西所需要的指令周期是可预测的。这个可预测十分重要,这种情况下算法可以优化其计算和访问SMEM
里面数据的策略,比如,遇到数据访问操作,可以切换执行其他计算指令,待SMEM
数据被取回到寄存器后,就切回刚刚执行数据访问操作的位置,继续执行后续的指令。L1/SMEM
每个SM
独享一份,不与其他SM
共享。如果有共享需求,则这时候应该L2
出场。L2
相比L1
,其频率没那么高,相应的访问其中的数据需要付出一定的延迟代价。当然了,GPU
执行当中为了最大化执行效率,都有动态缓存执行状态和切换执行其他指令的策略。L2缓存
保存了显存的一部分拷贝。在必要情况下(GPU
没有独立显存,或者系统也在内存当中划分了一部分区域提供给显存使用),它也可以保存一部分内存的拷贝。所谓拷贝,意味着这是一份连续的显存/内存拷贝(所以指望tex2DLod这样的操作可以减少显存读写以提升性能的可以省省了)。- 对于
CPU
而言,多线程访问L2
可能是不安全的。所以CPU
一般是提供了额外的指令集去确保L2读-改-写操作
是安全的。但是现在某些GPU
(比如NVidia Fermi系列)就可以保证这套流程是安全的。GPU
相比CPU
还有一个好处,它往往对一套数据(比如数组,纹理等)访问是并行的,所以在GPU
侧进行这个访问操作就可以安全很多。
为了最大化执行效率动态切换执行指令策略。
3.3 显存
我们经常考虑优化一些东西,包括OverDraw
,贴图纹理,遮挡剔除,批次合并,不少都是为了照顾显存带宽的。显存虽然大,但是它频率不高,访问还有延时,而且这个延时往往是三位数甚至四位数的GPU
核心指令周期。
通常交给GPU
计算东西的时候,都是要把数据从内存拷贝到显存,GPU
计算完成之后,从显存拷贝回来。当然如果这些数据用于显示,直接划分一块显存区域给帧缓存,然后让显示器读取这块显存即可,这时候不需要往回拷贝。
LAST 参考文献
(3条消息) gpu的单位表示_GPU中的基本概念_weixin_39717121的博客-CSDN博客
CUDA的thread,block,grid和warp - 知乎
GPU架构之Hierarchy Memory多级存储 - 知乎
Nvidia GPU架构 - Cuda Core,SM,SP等等傻傻分不清?_咚咚锵的博客-CSDN博客_cuda sm
gpu结构与cuda系列3gpu软件抽象:grid,block,thread,warp定义说明与硬件的映射执行细节(代码片段)
1GPU运行机制总述市面上有很多GPU厂家,他们产品的架构各不相同,但是核心往往差不多,整明白了一个基本上就可以触类旁通了。1.0GPU计算流程(CPU协同GPU计算)一个典型的计算流程是这样的:数据从CPU的内存... 查看详情
gpu结构与cuda系列2gpu硬件结构及架构分析:流多处理器sm,流处理器sp,示例架构分析(代码片段)
1GPU架构的发展架构名发布年份显卡名称每个SM中SP的数量Tesla2008Fermi2010GTX400GTX500GF100:32;GF10X:48Kepler2012GTX600GTX700192Maxwell2014GTX800GTX900Jetson-Nano128Pascal2016GP100GTX1000MX150MX250Jetson-T 查看详情
gpu结构与cuda系列0背景知识:gflops,带宽,延迟和吞吐量,乱序执行,上下文切换,指令集体系结构
1GFLOPSGigaFloating-pointOperationsPerSecond即10亿次每秒的浮点运算数,常作为GPU性能参数,但不一定代表GPU的实际表现,因为还要考虑具体如何拆分多边形和像素以及纹理填充,理论上该数值越高越好。1GFlops=1,000MFlops... 查看详情
cuda并行程序设计系列gpu技术简介
http://www.cnblogs.com/5long/p/cuda-parallel-programming-1.html本系列目录:【CUDA并行程序设计系列(1)】GPU技术简介【CUDA并行程序设计系列(2)】CUDA简介及CUDA初步编程【CUDA并行程序设计系列(3)】CUDA线程模型【CUDA并行程序设计系列(4... 查看详情
cuda学习寄存器用法
一、映射寄存器方式 CPU与GPU架构的一个主要区别就是CPU与GPU映射寄存器的方式。CPU通过使用寄存器重命名和栈来执行多线程。为了运行一个新的任务,CPU需要进行上下文切换,将当前所有寄存器的状态保存到栈... 查看详情
一文了解gpu并行计算cuda
了解GPU并行计算CUDA一、CUDA和GPU简介二、GPU工作原理与结构2.1、基础GPU架构2.2、GPU编程模型2.3、软件和硬件的对应关系三、GPU应用领域四、GPU+CPU异构计算五、MPI与CUDA的区别一、CUDA和GPU简介CUDA(ComputeUnifiedDeviceArchitecture... 查看详情
cuda内存类型memory(代码片段)
...线程访问的只读存储器:constantmemoryandtexturememory1、 寄存器Register 寄存器是GPU上的高速缓存器,其基本单元是寄存器文件,每个寄存器文件大小为32bit. Kernel中的局部(简单类型)变量第一选择是被分配到Register中。 ... 查看详情
cuda与opengl互操作
...OpenGL在进行后期渲染更具有优势。由于CUDA中的运算结果存储在GPU中,如果将数据download到CPU,然后再将CPU中的数据上传到GPU,使用OpenGL进行渲染,中间的GPU与CPU的交互会很耗时,毕竟使用GPU的目的就是为了加速,这样的数 查看详情
cuda编程cuda的运行方式以及gridblock结构关系(代码片段)
...grid与block1.3dim类型定义2.CUDA的第一个程序3.CUDA线程的组织结构——grid与block关系1.CUDA基础知识1.1程序基本运行顺序一般来说,一个cpu+gpu的程序运行如下所示:1.2grid与block从GPU至线程的关系依次为:显卡(GPU 查看详情
哪些nvidia显卡支持cuda技术
...口来实现GPU的访问。在架构上采用了一种全新的计算体系结构来使用GPU提供的硬件资源,从而给大规模的数据计算应用提供了一种比CPU更加强大的计算能力。CUDA采用C语言作为编程语言提供大量的高性能计算指令开发能力,使开... 查看详情
cuda编程模型
...(不是很懂)3.CUDA中有内存层次和线程层次的概念内存层次结构线程层次结构CUDA编程模型中存在共享内存,通过为主内存节省带宽来大幅度提高运行速度。4.在CUDA编程中,你只需要编写单个线程需要执行的操作命令,通过调用核函... 查看详情
pytorch各个gpu版本cuda和cudnn对应版本
...idia.com/cuda/cuda-toolkit-release-notes/index.html下载路径给出其他资源的下载路径:CUDA下载地址下载地址下载路径给出其他资源的下载路径:CUDA下载地址下载地址cudnn下载地址(需要注册账号)下载地址Pytorch官网下载地址 查看详情
arm体系结构与编程的作品目录
...ARM9E系列1.3.4ARM1OE系列1.3.5SecurCore系列l.4ARM处理器模式1.5ARM寄存器介绍1.5.l通用寄存器1.5.3程序状态寄存器1.6ARM体系的异常中断1.6.1ARM中异常中断种类1.6.2ARM处理器对异常中断的响应过程1.6.3从异常中断处理程序中返回1.7ARM体系中存... 查看详情
[架构之路-123]-《软考-系统架构设计师》-操作系统-2-操作系统原理-存储层次结构与存储管理(寄存器cachemmu内存外存页表)
前言:操作系统的本质就是创建一个并发的应用程序执行的环境,使得各种应用程序可以动态、共享相同的计算机物理硬件资源,计算机的三大物理资源包括:CPU内存外设应用程序(管理应用程序):... 查看详情
nvidia显卡支持cuda,啥是cuda
...口来实现GPU的访问。在架构上采用了一种全新的计算体系结构来使用GPU提供的硬件资源,从而给大规模的数据计算应用提供了一种比CPU更加强大的计算能力。CUDA采用C语言作为编程语言提供大量的高性能计算指令开发能力,使开... 查看详情
cuda版本与gpu驱动版本问题
参考技术AGPU的驱动版本决定了支持CUDA的最高版本,所以安装tensorflowgpu(或者pytorchgpu)版本时,版本选择顺序一般为:tensorflowgpu(或pytorchgpu)版本---->CUDA,cuDNN版本---->GPU驱动版本注:当然安装CUDA时可以选择一起安装驱动,... 查看详情
什么是cuda
...口来实现GPU的访问。在架构上采用了一种全新的计算体系结构来使用GPU提供的硬件资源,从而给大规模的数据计算应用提供了一种比CPU更加强大的计算能力。CUDA采用C语言作为编程语言提供大量的高性能 查看详情
cuda编程cuda内存模型(代码片段)
文章目录1.内存结构2.GPUdevice内存2.1寄存器(Registers)2.2本地内存(LocalMemory)2.3共享内存(SharedMemory)2.4常量内存(ConstantMemory)2.5纹理内存(TextureMemory)2.6全局内存(GlobalMemory)3.CPUHost内存1.内存结构在CUDA中可编程内存的类型有:寄存器(Reg... 查看详情