1. 全局内存

核函数中的所有线程都能够访问全局内存(global memory)。全局内存的容量是所有设备内存中最大的,但由于它没有放在GPU芯片内部,因此具有相对较高的延迟和较低的访问速度,cudaMalloc分配的就是全局内存。此外,当处理逻辑上的二维或者三维问题时,还可以使用cudaMallocPitchcudaMalloc3D分配内存,用cudaMemcpy2DcudaMemcpy3D复制数据,释放时依然使用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)变量仅对一个线程可见,它是所有内存中访问速度最高的。在核函数中定义的不加任何限定符的变量一般来说就存放在寄存器中。在核函数中定义的不加任何限定符的数组有可能存放在寄存器中、也有可能存放在局部内存中。各种内建变量,例如gridDimblockDimblockIdxthreadIdx以及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内存的更多相关文章

  1. 《Java并发编程的艺术》读书笔记:二、Java并发机制的底层实现原理

    二.Java并发机制底层实现原理 这里是我的<Java并发编程的艺术>读书笔记的第二篇,对前文有兴趣的朋友可以去这里看第一篇:一.并发编程的目的与挑战 有兴趣讨论的朋友可以给我留言! 1. ...

  2. 《jQuery基础教程》读书笔记

    最近在看<jQuery基础教程>这本书,做了点读书笔记以备回顾,不定期更新. 第一章第二章比较基础,就此略过了... 第三章 事件 jQuery中$(document).ready()与j ...

  3. 《Java并发编程的艺术》读书笔记:一、并发编程的目的与挑战

    发现自己有很多读书笔记了,但是一直都是自己闷头背,没有输出,突然想起还有博客圆这么个好平台给我留着位置,可不能荒废了. 此文读的书是<Jvava并发编程的艺术>,方腾飞等著,非常经典的一本 ...

  4. cuda编程基础

    转自: http://blog.csdn.net/augusdi/article/details/12529247 CUDA编程模型 CUDA编程模型将CPU作为主机,GPU作为协处理器(co-pro ...

  5. 《深入java虚拟机》读书笔记之Java内存区域

    前言 该读书笔记用于记录在学习<深入理解Java虚拟机--JVM高级特性与最佳实践>一书中的一些重要知识点,对其中的部分内容进行归纳,主要是方便之后进行复习. 运行时数据区域 Java虚拟 ...

  6. 《深入分析Java Web技术内幕》读书笔记之JVM内存管理

    今天看JVM的过程中收获颇丰,但一想到这些学习心得将来可能被遗忘,便一阵恐慌,自觉得以后要开始坚持做读书笔记了. 操作系统层面的内存管理 物理内存是一切内存管理的基础,Java中使用的内存和应用程序的 ...

  7. Java并发编程实践读书笔记(2)多线程基础组件

    同步容器 同步容器是指那些对所有的操作都进行加锁(synchronize)的容器.比如Vector.HashTable和Collections.synchronizedXXX返回系列对象: 可以看到, ...

  8. Java并发编程实践读书笔记(5) 线程池的使用

    Executor与Task的耦合性 1,除非线程池很非常大,否则一个Task不要依赖同一个线程服务中的另外一个Task,因为这样容易造成死锁: 2,线程的执行是并行的,所以在设计Task的时候要考虑到 ...

  9. Java并发编程实践(读书笔记) 任务执行(未完)

    任务的定义 大多数并发程序都是围绕任务进行管理的.任务就是抽象和离散的工作单元.   任务的执行策略 1.顺序的执行任务 这种策略的特点是一般只有按顺序处理到来的任务.一次只能处理一个任务,后来其它任 ...

  10. Java并发编程实践读书笔记(1)线程安全性和对象的共享

    2.线程的安全性 2.1什么是线程安全 在多个线程访问的时候,程序还能"正确",那就是线程安全的. 无状态(可以理解为没有字段的类)的对象一定是线程安全的. 2.2 原子性 典型的 ...

随机推荐

  1. 2020-10-03:java中satb和tlab有什么区别?

    福哥答案2020-10-03:#福大大架构师每日一题# 简单回答:satb: snapshot-at-the-beginning,快照.tlab:thread local allocation buf ...

  2. 2021-06-28:最接近目标值的子序列和。给你一个整数数组 nums 和一个目标值 goal 。你需要从 nums 中选出一个子序列,使子序列元素总和最接近 goal 。也就是说,如果子序列元素和

    2021-06-28:最接近目标值的子序列和.给你一个整数数组 nums 和一个目标值 goal .你需要从 nums 中选出一个子序列,使子序列元素总和最接近 goal .也就是说,如果子序列元素和 ...

  3. 深入理解 python 虚拟机:破解核心魔法——反序列化 pyc 文件

    深入理解 python 虚拟机:破解核心魔法--反序列化 pyc 文件 在前面的文章当中我们详细的对于 pyc 文件的结构进行了分析,pyc 文件主要有下面的四个部分组成:魔术. Bite Filed ...

  4. vscode运行java输出至指定文件夹

    一.前言 最近呢,需要用vscode编写一点小的java程序,也就是单java文件,但是呢,我发现coderunner运行java,一个java文件编译出一个class文件,这也太乱了!不符合我简约的 ...

  5. Error: Cannot find module ‘webpack-cli/bin/config-yargs‘

    今配置一个webpack-dev-server进行服务端渲染时老是不正确 npm run dev 就崩了 起初的配置为 "devDependencies": { "web ...

  6. 罕见的技术:MSIL的机器码简析

    前言 一般的只有最终的汇编代码才有机器码表示,然一个偶然的机会发现,MSIL(Microsoft intermediate language)作为一个中间语言表示,居然也有机器码,其实这也难怪,计算机 ...

  7. 【技术积累】Git中的基础知识【一】

    Git是什么?有什么特点? Git是一个分布式版本控制系统,常用于软件开发中的源代码管理.它最初由Linux开发者Linus Torvalds创建,旨在管理Linux内核的开发. Git具有以下特点: ...

  8. 从隐私保护到AI隐私保护:隐私隐私保护的跨隐私保护治理框架实践案例

    目录 标题:<从隐私保护到AI隐私保护:跨隐私保护治理框架实践案例> 背景介绍 随着人工智能技术的广泛应用,隐私保护问题也日益突出.数据隐私泄露.算法歧视等问题引发了公众的担忧和不满.为了 ...

  9. 图书商城Vue+Element+Node项目练习(...)

    本系列文章是为学习Vue的项目练习笔记,尽量详细记录一下一个完整项目的开发过程.面向初学者,本人也是初学者,搬砖技术还不成熟.项目在技术上前端为主,包含一些后端代码,从基础的数据库(Sqlite).到 ...

  10. 固定型思维 VS 成长型思维

    回顾进入职场工作以来,对比曾经的学生时代,如果让我讲一个对自己影响最大的改变,那就是思维模式的一个转变. 具体来说,就是从一个典型的固定型思维转变成一个具备有成长型思维的人. 当然,我不敢妄称自己已经 ...