从GPU的内存访问视角对比NHWC和NCHW

本文涉及的产品
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
实时计算 Flink 版,5000CU*H 3个月
实时数仓Hologres,5000CU*H 100GB 3个月
简介: NHWC和NCHW是卷积神经网络(cnn)中广泛使用的数据格式。它们决定了多维数据,如图像、点云或特征图如何存储在内存中。
  • NHWC(样本数,高度,宽度,通道):这种格式存储数据通道在最后,是TensorFlow的默认格式。
  • NCHW(样本数,通道,高度,宽度):通道位于高度和宽度尺寸之前,经常与PyTorch一起使用。

NHWC和NCHW之间的选择会影响内存访问、计算效率吗?本文将从模型性能和硬件利用率来尝试说明这个问题。

卷积作为GEMM

GEneral Matrix to Matrix Multiplication (通用矩阵的矩阵乘法)

卷积可以使用基于变换的方法来实现,如快速傅立叶变换,它将卷积转换为频域的元素乘法,或者使用无变换的方法,如矩阵乘法,其中输入和滤波器(卷积核)被平面化并使用矩阵操作组合以计算输出特征映射。

但是:fft是内存密集型的,因为它们需要额外的内存来存储转换后的矩阵。并且fft的计算成本很高,特别是在时域和频域之间来回转换数据时,涉及操作开销。

而卷积运算的一般矩阵乘法是这样的。每个接受域按列堆叠,得到特征映射变换矩阵。同时还将滤波器矩阵逐行平摊和叠加,形成滤波器变换矩阵。滤波变换和特征映射变换矩阵经过矩阵乘法运算,形成扁平化的输出矩阵。这里的变换矩阵是一个中间矩阵,只是数值重排,与频域变换没有关系。

N -特征图的批量大小,C -输入通道,h -输入高度,W -输入宽度,

k -输出通道,r -滤波器高度,s -滤波器宽度,p -输出高度,q -输出宽度

特征映射变换矩阵和滤波变换矩阵被认为是中间矩阵,其维数大于特征映射本身。feature map的尺寸= C × H × W, (3x3x3) feature map transform的尺寸= CRS × NPQ (12x4)

GEMM的GPU实现:

GPU为了避免内存预感使用了隐式GEMM。在隐式GEMM中,不是形成Transform矩阵,而是对每个列和行进行动态索引。最终的输出直接存储在输出张量对应的索引中。

由SMs(流多处理器)组成的GPU主要用于执行并行计算。在上面的隐式GEMM中,每个矩阵乘法可以分成更小的矩阵乘法或块。然后每个块都由SMs同时处理,以加快过程。

有了上面的计算过程,还需要存储张量,下面我们看看张量是如何在GPU中存储的。

张量通常以跨行格式存储在GPU中,其中元素在内存布局中以非连续的方式存储。这种跨行存储方法提供了以各种模式(如NCHW或NHWC格式)排列张量的灵活性,优化了内存访问和计算效率。

下图中所示的给定张量,我们可以用NCHW和NHWC的行主格式表示它们,行主存储通过顺序存储每一行来安排内存中的张量元素。

NCHW

这里W是最动态的维度。同一通道中的元素存储在一起,然后是下一个通道中的元素。

NHWC

这里C是动态的维度。所有通道中来自相同空间位置的元素依次存储,然后是来自下一个空间位置的元素,从而优化对每个通道内空间数据的访问。

GPU上的内存吞吐量

GPU是高度并行的处理器,当数据访问以合并方式完成时,它们工作得最好,这意味着它们喜欢以连续的、有组织的方式读取数据。当每个线程在二级缓存中查找数据时,如果是缓存命中(请求内存的内容在缓存中可用),则内存访问速度很快。如果是缓存丢失(缓存命中的否定),那么GPU接近DRAM来获取请求的内存地址的内容,这是一个耗时的操作。

当GPU需要访问存储在内存中的数据时,它会在“事务”中这样做。根据GPU配置,每个事务访问32/128字节的信息。访问的信息保留在缓存中。当另一个GPU线程请求内存访问时,它首先检查缓存。如果数据在缓存中不可用,那么请求将被转发到DRAM。

GPU工作原理十分复杂,我们不想也没有时间在这里详细解释,所以将其简单概括为:

合并内存事务发生在GPU访问连续块中的内存时。如果GPU需要读取连续存储在内存中的32字节数据,它将执行单个合并内存事务来一次检索所有32字节。非合并内存事务发生在GPU需要访问未连续存储在内存中的数据时。在这种情况下,GPU将需要执行多个事务来检索所有必要的数据

在GEMM的情况下,无论滤波器的高度和宽度如何,我们都可以确保读取给定空间位置的所有通道信息。例如,如果我们的输入特征是128 x 128 x 32。无论使用1x1还是3x3内核,我们都可以读取位置(1,1)的所有通道。

如果使用NCHW,它将属于单个通道的所有元素存储在一起,我们将不得不跨到位置a[0], a[16384], a[32,768]……直到位置a[16384x31]进行1x1卷积。这些位置不是连续的,并且肯定会导致缓存丢失,从而导致内存读取期间的额外开销。在每个事务期间读取的其余数据也不被使用,也称为非合并内存事务。

当使用NHWC格式表示张量时,访问位置是a[0],a[1]…,a[127],它们是连续的,并且肯定是缓存命中。第一次访问a[0]会导致缓存丢失和从DRAM获取32/128字节数据的事务。当访问a[1]时,这将是保存事务的缓存命中。即使在一定数量的位置之后缓存丢失导致来自DRAM的事务,事务本身将携带连续内存位置的连续数据,可以在访问进一步位置时缓存命中,称为合并内存事务。

NHWC减少了张核gpu的内存访问瓶颈,从而优化了性能,与NCHW相比,这似乎是一个更好的选择

以下是NVIDIA A100-SXM4-80GB, CUDA 11.2, cuDNN 8.1下NCHW和NHCW的TFLOPS的性能条款。我们看到NHWC在两种设置下的TFLOPS方面表现更好。为了简单起见,在这里没有进入NC/xHWx布局,这是NHWC的一个变体,为NVIDIA张量核心操作准备。

那么为什么Pytorch还要使用NCHW呢?

官方论坛的一个帖子可以作为参考:

https://discuss.pytorch.org/t/why-does-pytorch-prefer-using-nchw/83637

另外就是TensorFlow 的官网也说过这么一段话,也可以作为参考

Most TensorFlow operations used by a CNN support both NHWC and NCHW data format. On GPU, NCHW is faster. But on CPU, NHWC is sometimes faster.

参考资料

https://avoid.overfit.cn/post/0b8715aa855045c693be2e9b20eb93ba

作者:Deepika

相关实践学习
部署Stable Diffusion玩转AI绘画(GPU云服务器)
本实验通过在ECS上从零开始部署Stable Diffusion来进行AI绘画创作,开启AIGC盲盒。
目录
相关文章
|
10天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
34 4
|
5月前
|
存储 缓存 Java
Java中的缓冲流提升I/O性能,通过内存缓冲区减少对硬件访问
【6月更文挑战第22天】Java中的缓冲流提升I/O性能,通过内存缓冲区减少对硬件访问。`BufferedInputStream`和`BufferedOutputStream`用于字节流,缓存数据批量读写。`BufferedReader`和`BufferedWriter`处理字符流,支持按行操作。使用后务必关闭流。
68 3
|
25天前
|
缓存 算法 数据处理
如何选择合适的内存访问模式
【10月更文挑战第20天】如何选择合适的内存访问模式
38 1
|
27天前
|
存储 容器
内存越界访问(Out-of-Bounds Access)
【10月更文挑战第12天】
133 2
|
27天前
|
Rust 编译器
|
2月前
|
存储 网络协议 大数据
一文读懂RDMA: Remote Direct Memory Access(远程直接内存访问)
该文档详细介绍了RDMA(远程直接内存访问)技术的基本原理、主要特点及其编程接口。RDMA通过硬件直接在应用程序间搬移数据,绕过操作系统协议栈,显著提升网络通信效率,尤其适用于高性能计算和大数据处理等场景。文档还提供了RDMA编程接口的概述及示例代码,帮助开发者更好地理解和应用这一技术。
|
2月前
|
编译器 Linux C语言
不对齐的内存访问 【ChatGPT】
不对齐的内存访问 【ChatGPT】
55 11
|
2月前
|
存储 并行计算 算法
CUDA统一内存:简化GPU编程的内存管理
在GPU编程中,内存管理是关键挑战之一。NVIDIA CUDA 6.0引入了统一内存,简化了CPU与GPU之间的数据传输。统一内存允许在单个地址空间内分配可被两者访问的内存,自动迁移数据,从而简化内存管理、提高性能并增强代码可扩展性。本文将详细介绍统一内存的工作原理、优势及其使用方法,帮助开发者更高效地开发CUDA应用程序。
|
2月前
|
存储 安全 Linux
将文件映射到内存,像数组一样访问
将文件映射到内存,像数组一样访问
31 0
|
2月前
ARM64技术 —— MMU处于关闭状态时,内存访问是怎样的?
ARM64技术 —— MMU处于关闭状态时,内存访问是怎样的?