《CUDA编程:基础与实践》读书笔记(2):CUDA内存
1. 全局内存
核函数中的所有线程都能够访问全局内存(global memory)。全局内存的容量是所有设备内存中最大的,但由于它没有放在GPU芯片内部,因此具有相对较高的延迟和较低的访问速度,cudaMalloc
分配的就是全局内存。此外,当处理逻辑上的二维或者三维问题时,还可以使用cudaMallocPitch
和cudaMalloc3D
分配内存,用cudaMemcpy2D
和cudaMemcpy3D
复制数据,释放时依然使用cudaFree
函数。
除了上述动态分配的全局内存外,CUDA也允许使用静态全局内存,其所占内存数量是在编译期确定的。静态全局内存变量必须在所有主机与设备函数外部定义,从其定义之处开始对一个翻译单元内的所有设备函数直接可见。
__device__ T x; //单个变量
__device__ T y[N]; //固定长度的数组
无需将静态全局内存变量传入核函数,核函数可以直接访问静态全局内存。不可以在主机函数中直接访问静态全局内存,但可以用如下函数在主机内存和静态全局内存之间传输数据:
cudaError_t cudaMemcpyToSymbol
(
const void* symbol, //静态全局内存变量
const void* src, //主机内存指针
size_t count, //复制的字节数
size_t offset = 0, //从symbol对应设备地址开始偏移的字节数
enum cudaMemcpyKind kind = cudaMemcpyHostToDevice
);
cudaError_t cudaMemcpyFromSymbol
(
void* dst, //主机内存指针
const void* symbol, //静态全局内存变量
size_t count, //复制的字节数
size_t offset = 0, //从symbol对应设备地址开始偏移的字节数
enum cudaMemcpyKind kind = cudaMemcpyDeviceToHost
);
一般来说,对全局内存的访问都会通过缓存:如果通过L1缓存, 那么内存访问由一个128字节的内存事务(memory transaction)实现;如果只通过L2缓存, 那么内存访问由一个32字节的内存事务实现。以L2缓存为例,在一次内存事务的数据传输中,从全局内存转移到L2缓存的一片内存的首地址一定是其最小粒度(32字节)的整数倍,也就是说,一次数据传输只能从全局内存读取地址为0~31字节、32~63字节、64~95字节、96~127字节等片段的数据。如果线程束请求的全局内存数据地址刚好为0~127,那么就能与4次数据传输所处理的数据完全吻合,这种情况下的访问是合并的(coalesced)。合并的内存访问不会浪费传输的数据,从而能够更好地利用显存带宽。
//cudaMalloc及其它CUDA运行时API分配的内存的首地址至少是256的整数倍。
//下面的例子中,假设x,y,z是由cudaMalloc分配的全局内存指针。
//第一个线程块中的线程束将访问数组x中的第0~31个元素,对应128字节的连续内存,且首地址一定是256字节的整数倍。这样的访问只需要4次数据传输即可完成,所以是合并访问,合并度为100%。
void __global__ add(float* x, float* y, float* z)
{
int n = threadIdx.x + blockIdx.x * blockDim.x;
z[n] = x[n] + y[n];
}
add<<<128, 32>>>(x, y, z);
//第一个线程块中的线程束将访问数组x中的第1~32个元素,假设数组x首地址为256字节,该线程束将访问设备内存的260~387字节,这将触发5次数据传输,对应的内存地址分别是256~287字节、288~319字节、320~351字节、352~383字节、384~415字节。这样的访问属于非合并的访问,合并度=使用的数据量/传输的数据量=256/320=80%。
void __global__ add(float* x, float* y, float* z)
{
int n = threadIdx.x + blockIdx.x * blockDim.x + 1;
z[n] = x[n] + y[n];
}
add<<<128, 32>>>(x, y, z);
2. 常量内存
常量内存(constant memory)是有常量缓存的全局内存,它可读不可写,且数量有限(仅有64KB)。使用常量内存的方法是在核函数外用__constant__
定义变量(可以是结构体),并且用cudaMemcpyToSymbol
函数将数据从主机端复制到设备的常量内存后供核函数使用。给核函数传递的参数(传值,不是像全局变量那样传递指针)就存放在常量内存中,但给核函数传递参数最多只能在一个核函数中使用4KB常量内存。
3. 纹理内存和表面内存
纹理内存(texture memory)和表面内存(surface memory)也是一种具有缓存的全局内存,一般仅可读(表面内存也可写)。
4. 寄存器
寄存器(register)变量仅对一个线程可见,它是所有内存中访问速度最高的。在核函数中定义的不加任何限定符的变量一般来说就存放在寄存器中。在核函数中定义的不加任何限定符的数组有可能存放在寄存器中、也有可能存放在局部内存中。各种内建变量,例如gridDim
、blockDim
、blockIdx
、threadIdx
以及warpSize
都保存在特殊的寄存器中。
5. 局部内存
寄存器中放不下的变量,以及索引值不能在编译时就确定的数组,都可能放在局部内存(local memory)中,这种判断是由编译器自动做的。虽然局部内存在用法上与寄存器类似,但从硬件上看,局部内存只是全局内存的一部分。所以,局部内存的延迟也较高,每个线程最多能使用512KB的局部内存,但过多使用也会降低程序性能。
6. 共享内存
共享内存(shared memory)具有仅次于寄存器的读写速度,数量也有限,它对整个线程块可见。在核函数中,如果要将一个变量定义为共享内存变量,就要在定义语句中加上限定符__shared__
,例如:
__shared__ float y[128];
除了上述的静态共享内存外,还可以使用动态共享内存,这不会影响程序性能,但有时可以提高程序的可维护性:
__global__ void func()
{
//使用动态共享内存,必须加上限定词extern,且不能指定数组大小
extern __shared__ float y[];
//...
}
//调用核函数的执行配置中,第三个参数指明动态共享内存的字节数
func<<<128, 32, sizeof(float) * 128>>>();
为了获得高的内存带宽,共享内存在物理上被分为32个bank,每个bank的宽度是4字节(只有Kepler架构的是8字节)。对于一个长度为128的单精度浮点数的共享内存数组来说,第0~31个数组元素依次对应到32个bank的第一层,第32~63个数组元素依次对应到32个bank的第二层,第64~95个数组元素依次对应到32个bank的第三层,第96~127个数组元素依次对应到32个bank的第四层。也就是说,每个bank分摊4个在地址上相差128字节的数据。
不同bank间的数据可以并行读写,而同一个bank中不同层的数据只能串行读写。所以,使用共享内存时要尽量避免同一个线程束内的多个线程访问同一个bank中不同层的数据,因为这会导致bank冲突,从而降低程序性能。
7. L1/L2缓存
从Fermi架构开始,有了SM层级的L1缓存和设备层级的L2缓存。它们主要用来缓存全局内存和局部内存的访问,减少延迟。在启用了L1缓存的情况下,对全局内存的读取将首先尝试经过L1缓存,如果未命中,则接着尝试经过L2缓存,如果再次未命中,则直接从全局内存读取。
《CUDA编程:基础与实践》读书笔记(2):CUDA内存的更多相关文章
- 《Java并发编程的艺术》读书笔记:二、Java并发机制的底层实现原理
二.Java并发机制底层实现原理 这里是我的<Java并发编程的艺术>读书笔记的第二篇,对前文有兴趣的朋友可以去这里看第一篇:一.并发编程的目的与挑战 有兴趣讨论的朋友可以给我留言! 1. ...
- 《jQuery基础教程》读书笔记
最近在看<jQuery基础教程>这本书,做了点读书笔记以备回顾,不定期更新. 第一章第二章比较基础,就此略过了... 第三章 事件 jQuery中$(document).ready()与j ...
- 《Java并发编程的艺术》读书笔记:一、并发编程的目的与挑战
发现自己有很多读书笔记了,但是一直都是自己闷头背,没有输出,突然想起还有博客圆这么个好平台给我留着位置,可不能荒废了. 此文读的书是<Jvava并发编程的艺术>,方腾飞等著,非常经典的一本 ...
- cuda编程基础
转自: http://blog.csdn.net/augusdi/article/details/12529247 CUDA编程模型 CUDA编程模型将CPU作为主机,GPU作为协处理器(co-pro ...
- 《深入java虚拟机》读书笔记之Java内存区域
前言 该读书笔记用于记录在学习<深入理解Java虚拟机--JVM高级特性与最佳实践>一书中的一些重要知识点,对其中的部分内容进行归纳,主要是方便之后进行复习. 运行时数据区域 Java虚拟 ...
- 《深入分析Java Web技术内幕》读书笔记之JVM内存管理
今天看JVM的过程中收获颇丰,但一想到这些学习心得将来可能被遗忘,便一阵恐慌,自觉得以后要开始坚持做读书笔记了. 操作系统层面的内存管理 物理内存是一切内存管理的基础,Java中使用的内存和应用程序的 ...
- Java并发编程实践读书笔记(2)多线程基础组件
同步容器 同步容器是指那些对所有的操作都进行加锁(synchronize)的容器.比如Vector.HashTable和Collections.synchronizedXXX返回系列对象: 可以看到, ...
- Java并发编程实践读书笔记(5) 线程池的使用
Executor与Task的耦合性 1,除非线程池很非常大,否则一个Task不要依赖同一个线程服务中的另外一个Task,因为这样容易造成死锁: 2,线程的执行是并行的,所以在设计Task的时候要考虑到 ...
- Java并发编程实践(读书笔记) 任务执行(未完)
任务的定义 大多数并发程序都是围绕任务进行管理的.任务就是抽象和离散的工作单元. 任务的执行策略 1.顺序的执行任务 这种策略的特点是一般只有按顺序处理到来的任务.一次只能处理一个任务,后来其它任 ...
- Java并发编程实践读书笔记(1)线程安全性和对象的共享
2.线程的安全性 2.1什么是线程安全 在多个线程访问的时候,程序还能"正确",那就是线程安全的. 无状态(可以理解为没有字段的类)的对象一定是线程安全的. 2.2 原子性 典型的 ...
随机推荐
- 2022-11-28:给定两个数组A和B,比如 A = { 0, 1, 1 } B = { 1, 2, 3 } A[0] = 0, B[0] = 1,表示0到1有双向道路 A[1] = 1, B[1]
2022-11-28:给定两个数组A和B,比如 A = { 0, 1, 1 } B = { 1, 2, 3 } A[0] = 0, B[0] = 1,表示0到1有双向道路 A[1] = 1, B[1] ...
- ubuntu配置vscode全过程(下载安装配置优化插件)
一.安装vscode 下载vscode 当然啦,我们安装vscode,当然要先下载啦,但是但是但是!不要在ubuntu的软件中心(Ubuntu Software)下载!贼坑!下载完不能用! 推荐下载方 ...
- IntelliJ IDEA一站式配置【全】(提高开发效率)
IDEA常用设置(提高开发效率) 本人也是IDEA编译器的忠实用户了,但是有时出于各种原因,比如更换设备等等,IDEA总是需要重新安装配置.这就让我比较苦恼,因为总是记不全自己之前都修改了哪些地方(原 ...
- PHP反序列化常用魔术方法
PHP反序列化 php序列化(serialize):是将变量转换为可保存或传输的字符串的过程 php反序列化(unserialize):就是在适当的时候把这个字符串再转化成原来的变量使用 PHP反序列 ...
- git push origin master 提示输入用户名和密码
今天更换了一台电脑,重新配置了SSH keys:但是在push得时候提示我输入用户名和密码 taodeMacBook-Pro:my_trip_proj tao$ git push origin mas ...
- 基于.NetCore开源的Windows的GIF录屏工具
推荐一个Github上Start超过20K的超火.好用的屏幕截图转换为 GIF 动图开源项目. 项目简介 这是基于.Net Core + WPF 开发的.开源项目,可将屏幕截图转为 GIF 动画.它的 ...
- 代码随想录算法训练营Day50 动态规划
代码随想录算法训练营 代码随想录算法训练营Day50 动态规划| 123.买卖股票的最佳时机III 188.买卖股票的最佳时机IV 123.买卖股票的最佳时机III 题目链接:123.买卖股票的最佳时 ...
- 人工智能导论——口罩佩戴检测详解(附带MTCNN论文精读)
人工智能导论--口罩佩戴检测详解(附带MTCNN论文精读) 一.问题重述 随着人类的科技不断进步,病毒也在随之更新迭代:在19年席卷全球的新冠肺炎疫情给人们的生活带来了极大的灾难,造成了无数的人因此失 ...
- Java 新的生态,Solon v2.3.2 发布
Solon 是什么框架? 一个,Java 新的生态型应用开发框架.它从零开始构建,有自己的标准规范与开放生态(全球第二级别的生态).与其他框架相比,它解决了两个重要的痛点:启动慢,费资源. 解决痛点? ...
- 记一次线上问题,Netty接收到的报文一次有数据一次没有数据
最近线上遇到一个问题,客户端发送的tcp报文第一次连接成功后没有数据,第二次连接后正常带数据,第三次又没有数据... 问题排查1:是否有负载均衡,其中有一台机器出现了异常,会出现一次成功一次失败的情况 ...