我将改进OCL内核性能,并且想要阐明内存事务如何工作以及哪种内存访问模式真的更好(以及为什么)。 内核提供了8个整数的向量,这些整数被定义为数组:int v [8],这意味着,在进行任何计算之前,必须将整个向量加载到GPR中。所以,我相信这个代码的瓶颈是初始数据加载。向量化数据的OpenCL(AMD GCN)全局内存访问模式:跨步与连续
首先,我考虑一些理论基础知识。
目标硬件是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];
(至极实际上perfomed为)
*(int8*)a = *(int8*)v;
2)交错
a[i] = v[get_global_id(0) + i * get_global_size(0)];
我期望在我的情况下连续会更快,因为如上所述,一个内存事务可以完全填充8个工作项与数据。但是,我不知道,计算单元中的调度程序如何在物理上工作:是否需要为所有SIMD通道准备好所有数据,或者只需要4个并行SIMD元素的第一部分就足够了?尽管如此,我认为只要CU可以独立执行命令流,它就足够聪明,可以首先完全提供至少一个CU的数据。 在第二种情况下,我们需要执行8 * global_size/64个事务来获取完整的向量。
所以,我的第二个问题:我的假设是正确的吗?
现在,练习。
实际上,我将整个任务分成两个内核,因为一个部分的注册压力低于另一个,因此可以使用更多的工作项目。所以我首先用模式演示了如何在内核之间转换时存储的数据(使用vload8/vstore8或转换为int8给出相同的结果),结果有点奇怪:以相邻方式读取数据的内核工作速度快10% CodeXL和操作系统时间测量),但连续存储数据的内核执行速度惊人地更慢。两个内核的总体时间大致相同。在我的想法中,两者必须至少以相同的方式行事 - 要么更慢,要么更快,但是这些相反的结果似乎无法解释。
而我的第三个问题是:任何人都可以解释这样的结果吗?或者可能是我做错了什么? (或完全错误?)
谢谢你的回答。但是我不是在谈论合并vs交错访问。可能是我的着作不那么清楚,但访问总是合并 - 区别仅在于读取数据矢量明智与元素明智。为了澄清一点,我纠正了这个问题。 – qpdb
@qpdb从内核的角度来看,你称之为连续的东西是连续的,并且在给定的周期内从内存的角度进行交错,因此读取每个工作项的第一个元素可以缓存剩余的数据。但是在写作时,没有这种行为,所以它变得更慢。由于读/写调度器(或者读/写组合的任何部分)可以提供n个工作项,这些工作项可以在大量相邻元素上统一读写,所以称为“交错”的内容在内存的给定周期内实际上是连续的。 –
顺便说一句,再次感谢指出文档。我从那里了解到:“南岛设备不支持合并写入;但是,工作组内的连续地址提供了最佳性能。”这个信息看起来很奇怪,因为我的实验给出了完全不同的结果。还是我完全相反地理解“合并”的整个概念? – qpdb