CUDA从入门到放弃(六):CUDA内存结构(Memory Hierarchy)

2024-03-27 23:52

本文主要是介绍CUDA从入门到放弃(六):CUDA内存结构(Memory Hierarchy),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

CUDA从入门到放弃(六):CUDA内存结构(Memory Hierarchy)

CUDA线程在执行过程中可以从多个内存空间访问数据。每个线程都有私有的局部内存。每个线程块具有共享内存,该内存对所有线程块内的线程可见,并且与线程块具有相同的生命周期。线程块集群中的线程块可以相互执行对共享内存的读取、写入和原子操作。所有线程都可以访问相同的全局内存。

此外,还有两个所有线程都可以访问的只读内存空间:常量内存空间和纹理内存空间。

对于同一应用程序来说,全局内存、常量内存和纹理内存空间在内核启动之间是持久的。
在这里插入图片描述

1 全局内存 Global Memory

全局内存位于设备内存中,通过32字节、64字节或128字节的内存事务进行访问。这些内存事务必须自然对齐:只有对齐到其大小(即,其首地址是其大小的倍数)的设备内存的32字节、64字节或128字节段才能通过内存事务进行读取或写入。

当warp执行访问全局内存的指令时,它会根据每个线程访问的字的大小以及内存地址在线程之间的分布,将warp内部线程的内存访问合并为一个或多个这些内存事务。一般来说,所需的交易越多,除了线程访问的字之外,传输的未使用字就越多,从而相应地降低了指令吞吐量。例如,如果为每个线程的4字节访问生成一个32字节的内存事务,则吞吐量将减少到原来的八分之一。

大小和对齐要求

全局内存指令支持读写大小为1、2、4、8或16字节的数据。只有当数据类型大小符合这些值,并且数据自然对齐(地址是大小的倍数)时,访问才会编译为单个全局内存指令。

若不满足此要求,访问将编译为多个具有交错访问模式的指令,导致指令无法完全合并。因此,建议使用满足这些要求的类型来处理全局内存中的数据。

内置向量类型会自动满足对齐要求。对于结构体,可以使用__align__(8)或__align__(16)来确保对齐。

struct __align__(8) {float x;float y;
};struct __align__(16) {float x;float y;float z;
};

全局内存中的变量地址或由相关API返回的地址至少对齐到256字节。读取非自然对齐的8或16字节数据会产生错误结果,因此需特别注意保持对齐。特别是在使用自定义的全局内存分配方案时,应确保每个数组的起始地址正确对齐。

2 局部内存 Local Memory

局部内存空间位于设备内存中,因此局部内存访问与全局内存访问具有相同的高延迟和低带宽,并且必须满足与设备内存访问中描述的相同内存合并要求。然而,局部内存的组织方式是,连续的32位字由连续的线程ID访问。因此,只要warp中的所有线程访问相同的相对地址(例如,数组变量中的相同索引,结构变量中的相同成员),访问就会完全合并。

局部内存访问仅发生在某些自动变量上。编译器可能将以下自动变量放置在局部内存中:

  • 它无法确定使用常量索引访问的数组,
  • 会消耗过多寄存器空间的大型结构或数组,
  • 如果内核使用的寄存器数量超过可用数量(这也称为寄存器溢出)时的任何变量。

3 共享内存 Shared Memory

由于共享内存位于芯片上,因此它相比本地内存或全局内存具有更高的带宽和更低的延迟。

为了实现高带宽,共享内存被分割成大小相等的内存模块,称为内存bank,这些bank可以同时访问。因此,任何由n个不同内存bank中的地址组成的内存读或写请求都可以同时得到服务,从而得到比单个模块带宽高出n倍的整体带宽。

然而,如果内存请求的两个地址落在同一个内存银行中,就会发生bank冲突,并且访问必须串行化。硬件会将带有bank冲突的内存请求拆分成尽可能多的单独的无冲突请求,吞吐量将降低一个等于单独内存请求数量的因子。如果单独的内存请求数量是n,那么初始内存请求就被认为是造成了n路bank冲突。

4 常量内存 Constant Memory

常量内存空间位于设备内存中,并缓存在常量缓存中。

然后,请求会根据初始请求中不同内存地址的数量拆分成多个单独的请求,吞吐量将降低一个等于单独请求数量的因子。

在缓存命中的情况下,生成的请求将以常量缓存的吞吐量进行处理;否则,将以设备内存的吞吐量进行处理。

5 纹理和表面内存 Texture and Surface Memory

纹理和表面内存空间位于设备内存中,并被缓存在纹理缓存中。因此,只有在缓存未命中的情况下,纹理获取或表面读取才会从设备内存中进行一次内存读取,否则仅从纹理缓存中进行一次读取。纹理缓存针对二维空间局部性进行了优化,因此,在二维空间中读取纹理或表面地址相近的同一warp中的线程将实现最佳性能。此外,它还设计用于具有恒定延迟的流式获取;缓存命中可以减少对DRAM带宽的需求,但不会减少获取延迟。

通过纹理或表面获取读取设备内存具有一些优势,这使得它成为从全局或常量内存读取设备内存的有利替代方案:

如果内存读取不遵循全局或常量内存读取必须遵循的访问模式以获得良好性能,那么只要纹理获取或表面读取中存在局部性,就可以实现更高的带宽;

寻址计算由专用单元在内核外部执行;

可以通过单个操作将打包的数据广播到单独的变量中;

8位和16位整数输入数据可以选择性地转换为范围在[0.0, 1.0]或[-1.0, 1.0]内的32位浮点数值。

参考资料
1 CUDA编程入门
2 CUDA编程入门极简教程
3 CUDA C++ Programming Guide
4 CUDA C++ Best Practices Guide
5 NVIDIA CUDA初级教程视频
6 CUDA专家手册 [GPU编程权威指南]
7 CUDA并行程序设计:GPU编程指南
8 CUDA C编程权威指南

这篇关于CUDA从入门到放弃(六):CUDA内存结构(Memory Hierarchy)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/853826

相关文章

Python如何使用__slots__实现节省内存和性能优化

《Python如何使用__slots__实现节省内存和性能优化》你有想过,一个小小的__slots__能让你的Python类内存消耗直接减半吗,没错,今天咱们要聊的就是这个让人眼前一亮的技巧,感兴趣的... 目录背景:内存吃得满满的类__slots__:你的内存管理小助手举个大概的例子:看看效果如何?1.

使用Java实现通用树形结构构建工具类

《使用Java实现通用树形结构构建工具类》这篇文章主要为大家详细介绍了如何使用Java实现通用树形结构构建工具类,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录完整代码一、设计思想与核心功能二、核心实现原理1. 数据结构准备阶段2. 循环依赖检测算法3. 树形结构构建4. 搜索子

利用Python开发Markdown表格结构转换为Excel工具

《利用Python开发Markdown表格结构转换为Excel工具》在数据管理和文档编写过程中,我们经常使用Markdown来记录表格数据,但它没有Excel使用方便,所以本文将使用Python编写一... 目录1.完整代码2. 项目概述3. 代码解析3.1 依赖库3.2 GUI 设计3.3 解析 Mark

Python FastAPI入门安装使用

《PythonFastAPI入门安装使用》FastAPI是一个现代、快速的PythonWeb框架,用于构建API,它基于Python3.6+的类型提示特性,使得代码更加简洁且易于绶护,这篇文章主要介... 目录第一节:FastAPI入门一、FastAPI框架介绍什么是ASGI服务(WSGI)二、FastAP

Redis 内存淘汰策略深度解析(最新推荐)

《Redis内存淘汰策略深度解析(最新推荐)》本文详细探讨了Redis的内存淘汰策略、实现原理、适用场景及最佳实践,介绍了八种内存淘汰策略,包括noeviction、LRU、LFU、TTL、Rand... 目录一、 内存淘汰策略概述二、内存淘汰策略详解2.1 ​noeviction(不淘汰)​2.2 ​LR

Golang基于内存的键值存储缓存库go-cache

《Golang基于内存的键值存储缓存库go-cache》go-cache是一个内存中的key:valuestore/cache库,适用于单机应用程序,本文主要介绍了Golang基于内存的键值存储缓存库... 目录文档安装方法示例1示例2使用注意点优点缺点go-cache 和 Redis 缓存对比1)功能特性

Go使用pprof进行CPU,内存和阻塞情况分析

《Go使用pprof进行CPU,内存和阻塞情况分析》Go语言提供了强大的pprof工具,用于分析CPU、内存、Goroutine阻塞等性能问题,帮助开发者优化程序,提高运行效率,下面我们就来深入了解下... 目录1. pprof 介绍2. 快速上手:启用 pprof3. CPU Profiling:分析 C

mysql通过frm和ibd文件恢复表_mysql5.7根据.frm和.ibd文件恢复表结构和数据

《mysql通过frm和ibd文件恢复表_mysql5.7根据.frm和.ibd文件恢复表结构和数据》文章主要介绍了如何从.frm和.ibd文件恢复MySQLInnoDB表结构和数据,需要的朋友可以参... 目录一、恢复表结构二、恢复表数据补充方法一、恢复表结构(从 .frm 文件)方法 1:使用 mysq

golang内存对齐的项目实践

《golang内存对齐的项目实践》本文主要介绍了golang内存对齐的项目实践,内存对齐不仅有助于提高内存访问效率,还确保了与硬件接口的兼容性,是Go语言编程中不可忽视的重要优化手段,下面就来介绍一下... 目录一、结构体中的字段顺序与内存对齐二、内存对齐的原理与规则三、调整结构体字段顺序优化内存对齐四、内

Python中顺序结构和循环结构示例代码

《Python中顺序结构和循环结构示例代码》:本文主要介绍Python中的条件语句和循环语句,条件语句用于根据条件执行不同的代码块,循环语句用于重复执行一段代码,文章还详细说明了range函数的使... 目录一、条件语句(1)条件语句的定义(2)条件语句的语法(a)单分支 if(b)双分支 if-else(