对于缓存的 GPU,哪种内存访问模式更有效?

Posted

技术标签:

【中文标题】对于缓存的 GPU,哪种内存访问模式更有效?【英文标题】:Which memory access pattern is more efficient for a cached GPU? 【发布时间】:2014-01-27 06:57:25 【问题描述】:

假设我有一个全局内存数组:

|a|b|c| |e|f|g| |i|j|k| |

有四个“线程”(OpenCL 中的本地工作项)访问此内存,并且此访问有两种可能的模式(列是时间片,行是线程):

   0 -> 1 -> 2 -> 3
t1 a -> b -> c -> .
t2 e -> f -> g -> .
t3 i -> j -> k -> .
t4 .    .    . `> .

上述模式将数组拆分为块,每个线程在每个时间片中迭代并访问块中的下一个元素。我相信这种访问对于 CPU 来说会很有效,因为它可以最大化每个线程的缓存局部性。此外,使用这种模式的循环可以很容易地被编译器展开。

第二种模式:

   0 -> 1 -> 2 -> 3
t1 a -> e -> i -> .
t2 b -> f -> j -> .
t3 c -> g -> k -> .
t4 .    .    . `> .

上述模式大步访问内存:例如,线程 1 访问 a,然后是 e,然后是 i 等。这最大化了每单位时间的缓存局部性。假设您在任何给定的时间片上都有 64 个工作项“跨步”。这意味着,对于 64 字节的缓存行大小和 sizeof(float) 的元素,工作项 1-16 的读取由工作项 1 的读取缓存。必须仔细选择每个单元格的数据宽度/计数(其中“a”是上面的一个单元格)以避免未对齐的访问。这些循环似乎不那么容易展开(或者根本不使用 Intel 的 Kernel Builder 和 CPU)。我相信这种模式适用于 GPU。

我的目标是具有缓存层次结构的 GPU。特别是 AMD 的最新架构 (GCN)。第二种访问模式是“合并”的一个例子吗?我的思维过程是否有错误?

【问题讨论】:

是的;第二种模式是合并,通常在流处理器或任何您想调用的处理器上首选。我自己还没有使用过 AMD 硬件,但我想同样的事情必须坚持 是的,第二个是正确的方法。你所有的解释都很好 据我所知,列与行访问仅适用于二维数组全局内存访问。第一种形式一次将使用总 mcache 行的 1/3。第二个将使用所有缓存行。在我的 HD7870 上使用阻塞算法进行矩阵乘法时,第二个工作得更快。 @huseyintugrulbuyukisik 堆栈上的缓冲区应该以相同的方式存储数据,无论维度如何,维度不只是抽象吗?现在 2DImages 有点不同,并且可以缓存在多个维度中(比如上面、下面和旁边的元素,而不是前面的元素)。 【参考方案1】:

我认为答案取决于访问是针对全局内存还是本地内存。如果您从全局内存中提取数据,那么您需要担心合并读取(即连续块,第二个示例)。但是,如果您是从本地内存中提取数据,那么您需要担心银行冲突。我有一些但不是很多经验,所以我并不是说这是绝对真理。

编辑:在阅读了 GCN 之后,我认为缓存在这里没有什么不同。如果您重复读/写相同的元素,您基本上可以将它们视为加速全局内存。顺便说一句,感谢您提出这个问题,因为阅读新架构非常有趣。

编辑 2:这里有一个关于本地和全局内存库的 Stack Overflow 讨论:Why aren't there bank conflicts in global memory for Cuda/OpenCL?

【讨论】:

谢谢。我完全忘了提到这是为了全球访问。本地内存和全局内存都被存储起来,但是本地内存可以针对更多种类的访问模式进行优化(至少在 Intel HD 2500->5200 的情况下)。

以上是关于对于缓存的 GPU,哪种内存访问模式更有效?的主要内容,如果未能解决你的问题,请参考以下文章

对于 Java 内存不足错误,我应该增加哪种类型的 Spark 内存?

GPU存储器架构-- 全局内存 本地内存 寄存器堆 共享内存 常量内存 纹理内存

Android 逆向函数拦截 ( CPU 高速缓存机制 | CPU 高速缓存机制 导致 函数拦截失败 )

谁决定内存访问模式?

有效访问时间和平均访问时间

在 Python 中访问 GPU 硬件规范?