Optimization 矢量化数据的OpenCL(AMD GCN)全局内存访问模式:跨步与连续

Optimization 矢量化数据的OpenCL(AMD GCN)全局内存访问模式:跨步与连续,optimization,opencl,gpgpu,amd-gcn,Optimization,Opencl,Gpgpu,Amd Gcn,我将改进OCL内核性能,并想澄清内存事务是如何工作的,以及什么内存访问模式真正更好(以及为什么)。 内核由定义为array:int v[8]的8个整数向量组成,这意味着在进行任何计算之前,必须将整个向量加载到GPRs中。因此,我认为这段代码的瓶颈是初始数据加载 首先,我考虑一些理论基础。 目标硬件是Radeon RX 480/580,具有256位GDDR5内存总线,在该总线上,突发读/写事务具有8字粒度,因此,一个内存事务读取2048位或256字节。我相信CL_DEVICE_MEM_BASE_A

我将改进OCL内核性能,并想澄清内存事务是如何工作的,以及什么内存访问模式真正更好(以及为什么)。 内核由定义为array:int v[8]的8个整数向量组成,这意味着在进行任何计算之前,必须将整个向量加载到GPRs中。因此,我认为这段代码的瓶颈是初始数据加载

首先,我考虑一些理论基础。

目标硬件是Radeon RX 480/580,具有256位GDDR5内存总线,在该总线上,突发读/写事务具有8字粒度,因此,一个内存事务读取2048位或256字节。我相信CL_DEVICE_MEM_BASE_ADDR_ALIGN指的是:

Alignment (bits) of base address: 2048.
因此,我的第一个问题是:128字节缓存线的物理意义是什么?它是否保留通过单次突发读取获取但未真正请求的数据部分?如果我们请求(比如)32或64个字节,那么剩余部分会发生什么情况?因此,剩余部分超过了缓存线的大小?(我想,它将被丢弃——那么,哪一部分:头、尾……?)

现在回到我的内核,我认为缓存在我的例子中并没有扮演重要的角色,因为一个突发读取64个整数->一个内存事务理论上可以一次提供8个工作项,没有额外的数据要读取,并且内存总是合并在一起的

但是,我仍然可以使用两种不同的访问模式放置数据:

1) 相邻的

    a[i] = v[get_global_id(0) * get_global_size(0) + i];
(实际表现为)

2) 交错

    a[i] = v[get_global_id(0) + i * get_global_size(0)];
我希望在我的例子中,continuous会更快,因为如上所述,一个内存事务可以用数据完全填充8个工作项。然而,我不知道计算单元中的调度器是如何工作的:它是否需要为所有SIMD通道准备所有数据,或者只需要为4个并行SIMD元素准备第一部分就足够了?尽管如此,我认为只要CU可以独立执行命令流,就可以首先完全提供至少一个CU的数据。 而在第二种情况下,我们需要执行8*global_size/64事务以获得完整的向量

那么,我的第二个问题是:我的假设正确吗

现在,实践

实际上,我将整个任务分成两个内核,因为其中一个部分的寄存器压力比另一个小,因此可以使用更多的工作项。首先,我研究了存储在内核之间的数据转换模式(使用vload8/vstore8或casting to int8获得相同的结果),结果有点奇怪:以连续方式读取数据的内核的工作速度大约快10%(在CodeXL和操作系统时间测量中),但是连续存储数据的内核执行速度惊人地慢。两个内核的总时间大致相同。在我看来,两者的行为至少应该是一样的——要么慢一点,要么快一点,但这些相反的结果似乎无法解释


我的第三个问题是:有人能解释这样的结果吗?或者是我做错了什么?(还是完全错了?

请查看中的第2.1章。它主要关注老一代卡,但GCN架构没有完全改变,因此仍应适用于您的设备(polaris)

一般来说,AMD卡有多个内存控制器,在每个时钟周期内存请求都分配到这些控制器。例如,如果访问列主逻辑中的值而不是行主逻辑中的值,则性能会更差,因为请求被发送到同一内存控制器。(column major是指矩阵中的一列被当前时钟周期中执行的所有工作项一起访问,这就是您所说的合并与交织)。如果在一个时钟周期内访问一行元素(意味着合并)(意味着所有工作项都访问同一行中的值),那么这些请求应该分发到不同的内存控制器,而不是相同的内存控制器

关于对齐和缓存线大小,我想知道这是否真的有助于提高性能。如果我是在你的情况下,我会尝试看看我是否可以优化算法本身,或者我是否经常访问这些值,将它们复制到本地内存是有意义的。但是,在不知道内核执行什么的情况下,很难说清楚

致以最良好的祝愿


Michael

请看一下中的第2.1章。它主要关注老一代卡,但GCN架构没有完全改变,因此仍应适用于您的设备(polaris)

一般来说,AMD卡有多个内存控制器,在每个时钟周期内存请求都分配到这些控制器。例如,如果访问列主逻辑中的值而不是行主逻辑中的值,则性能会更差,因为请求被发送到同一内存控制器。(column major是指矩阵中的一列被当前时钟周期中执行的所有工作项一起访问,这就是您所说的合并与交织)。如果在一个时钟周期内访问一行元素(意味着合并)(意味着所有工作项都访问同一行中的值),那么这些请求应该分发到不同的内存控制器,而不是相同的内存控制器

关于对齐和缓存线大小,我想知道这是否真的有助于提高性能。如果我是在你的情况下,我会尝试看看我是否可以优化算法本身,或者我是否经常访问这些值,将它们复制到本地内存是有意义的。但是,在不知道内核执行什么的情况下,很难说清楚

致以最良好的祝愿

Michael

嗯,并不是真的回答了我所有的问题,但在浩瀚的互联网中发现的一些信息让事情变得更加清晰,至少对我来说是这样(不像上面提到的AMD优化指南,它看起来不清楚,有时令人困惑):

«硬件执行一些协同工作
    a[i] = v[get_global_id(0) + i * get_global_size(0)];