在缓冲区的不同部分合并读写

时间:2018-12-30 08:28:29

标签: cuda

假设我们有32个线程。第一个线程在偏移量0处读取128位(uint4),第二个线程在偏移量16处读取128位,因此直到第32个线程在偏移量496处读取128位。它们全部合并为一个读取。

现在,让我们说一些线程在0和512之间的偏移量(对齐16个字节)上读取16位字节上对齐的128位值,而其他线程在512和1024之间的偏移量(也对齐16个字节)上读取128位值。

是否合并了缓冲区第一部分的访问权,并且合并了第二部分的访问权也导致了两次读取。

或者有32个读数?

1 个答案:

答案 0 :(得分:2)

在第二种情况下,“读取”数目将介于16到32之间。但是为了理解,我们应该更加谨慎地使用术语。

推销过程的工作原理如下。

  1. LD / ST单元收到请求。假设我们正在谈论读请求(即LD指令)。读请求由LD指令加上经线中每个线程生成的地址组成。

  2. 在根据缓存行或内存段进行查看时,将处理该请求以确定每个地址相对于其他地址的位置。对于此讨论,假设任何缓存中都没有命中,因此我们必须针对内存段合理化请求。内存段是全局内存空间的固定细分,对应于可分配给DRAM子系统的最小事务大小。在我熟悉的所有CUDA GPU上,内存/ DRAM段的大小为32字节。经纱中每个线程生成的地址与DRAM段模式的映射关系将确定必须检索内存中的哪些实际段才能满足此LD请求。

  3. 内存控制器将检索这些段。对于DRAM,每个检索段的请求都是事务

  4. 检索到的段数据将用于适当地填充缓存行,并满足整个扭曲的原始LD请求。

推销基本上发生在步骤2中。由于跨经发布的地址被映射到DRAM段的基础模式,因此,如果多个地址落入单个段中,则该段将不会被多次请求。它只会被请求一次。这是合并的中心思想。

现在,通过上面的描述,让我们看一下您的特定示例。

在第一个示例中,您声明“它们全部合并为一个读取”。好吧,他们肯定是从一个读取请求开始的。但是,满足每个线程16字节的非重叠全翘曲读取(32个线程)的32字节DRAM事务的最小数量为512字节,即512/32 = 16段。取决于您在何处或如何进行测量,它也可以称为4个全局事务,因为全局装入事务的最大宽度为128字节。但是,无论我们如何/在何处进行测量,这都是完全结合的,最佳的100%最佳交易集,因为生成了满足此类请求所需的最少交易次数,并且至少使用了从内存中检索到的每个字节经线中的一个线程请求。

在第二个示例中,如果不知道经纱中的线程生成的实际地址模式,就无法确定精确的活动。对于读取0到512之间位置的线程,此范围内最多有512/32 = 16个段。并且有16个线程。因此,您可能处于最坏的情况(对于这种特定安排),其中每个线程都需要自己的段。或者,如果线程地址未在32字节边界处完美地隔开,则前16个线程所需的DRAM事务数可能少于16,可能低至8。同样,对于第二组16个线程,以及第二组512字节的内存。

因此,对于最佳情况模式,第二个示例将仅向DRAM发出16个事务,就生成的DRAM事务的数量以及总体效率(100%利用率)而言,它与第一个示例完全匹配。对于最坏的情况(每个线程地址间隔为32个字节),则需要32个段(因此需要32个DRAM事务)来满足翘曲读取请求。

举一个代码示例,以下顺序将使每个扭曲生成32个DRAM事务:

__global__ void k(float4 *d){
  int idx = threadIdx.x+blockDim.x*blockIdx.x;
  float4 temp = d[idx*2];
  ...
  }

在上面的示例中,每个线程生成的基础字节地址将以32字节边界完美间隔。前16个线程将从内存中的第一个512字节区域请求数据,而后16个线程将从内存中的第一个512字节区域请求数据。该请求的总体效率为50%(将从内存中请求1024字节,但warp中的线程仅需要512字节)。

以下顺序将为第一个扭曲生成16个DRAM事务:

__global__ void k(float4 *d){
  int idx = threadIdx.x+blockDim.x*blockIdx.x;
  float4 temp = d[idx + (idx/16)*16];
  ...
  }

在上面的示例中,对于前16个线程(在第一个扭曲中),每个线程生成的基础字节地址将为0,16,32,48 ...,252。对于后16个线程(在第一个线程束中),地址将为512,528,544 .....,764。前16个线程将从内存中的第一个512字节区域请求数据,而后16个线程将从内存中的第一个512字节区域请求数据。但是,前16个线程仅需要8个DRAM事务,而后16个线程仅需要8个DRAM事务。该请求的总体效率为100%(对于经线中的线程需要512字节,将从内存中请求512字节)。