合并一组奇数

时间:2012-10-22 20:42:58

标签: cuda

我正在努力理解合并全局记忆 假设我想将一组奇怪的浮点数加载到全局内存中。每个线程将处理一组3个浮点数。假设这些花车是A,B和C.

A0,  B0,  C0
A1,  B1,  C1
A2,  B2,  C2
..          
A19, B19, C19

所以线程会抓住这样的数据:

Thread 0:  A0,  B0,  C0  
Thread 1:  A1,  B1,  C1  
Thread 2:  A2,  B2,  C2
..
Thread 19:  A19, B19, C19  

第一种方法:
我可以加载3个数组:float A[20]; float B[20]; floatC[20];我必须将cudaMemcpy()三次不同的时间加载到全局内存中。这种方法可能不会很好地融合。

第二种方法:
更好的方法是:

struct {float A, float B, float C} dataPt;
dataPt data[20];

我可以使用一个cudaMemcpy()加载数据,但我不确定内存访问是否会很好地合并。

第三种方法:

struct {float A, float B, float C, float padding} dataPt2;
dataPt2 data2[20];

struct __align__(16){float A, float B, float C} dataPt3;
dataPt3 data3[20];

我可以使用单个cudaMemcpy()将数据加载到全局内存,并且可以合并对数据的线程访问。 (以浪费的全球记忆为代价。)

1)第一种方法不会合并,因为每个线程可能需要3个总线周期才能加载输入数据 2)第二种方法将合并为许多线程,但是会有一些线程需要两个总线周期来获取输入数据。
3)第3种方法将合并所有线程。

这准确吗?第二个和第二个之间是否存在显着差异?第三种方法?有没有一种方法使用3个线程维度(threadIdx.x,threadIdx.y,threadIdx.z)?

2 个答案:

答案 0 :(得分:2)

放大@talonmies的答案。 我们假设我们的内核看起来像这样:

__global__ void kern(float *a, float *b, float *c){

  float local_a, local_b, local_c;
  int idx = threadIdx.x + (blockDim.x * blockIdx.x);

  local_a = a[idx];
  local_b = b[idx];
  local_c = c[idx];
}

忽略优化(这将导致空内核),并假设我们启动了1个32个线程的块:

  kern<<<1, 32>>>(d_a, d_b, d_c);

然后我们在锁步中执行32个线程(1个warp)。这意味着每个线程将处理以下内核代码行:

  local_a = a[idx];

在同一时间。合并负载(来自全局存储器)的定义是当warp加载一系列数据项时,这些数据项都在全局存储器中的单个128字节对齐边界内(对于CC 2.0设备)。具有100%带宽利用率的完美合并负载意味着每个线程在该128字节对齐区域内使用一个唯一的32位数量。如果线程零加载[0],则线程1加载[1]等,这可能是合并负载的典型示例。

因此,在第一种情况下,由于a []数组是连续且对齐的,并且[0..31]适合全局内存中的128字节对齐区域,因此我们得到了合并负载。线程0读取[0],线程1读取[1]等。

在第二种情况下,[0]与[1]不连续,此外元素a [0..31](均在同一代码行加载)不适合128字节全局记忆中的对齐序列。我将让你解析在你的第三种情况下发生的事情,但足以说明像第二种情况一样,元素a [0..31]不是连续的,也不包含在全局存储器中的单个128字节对齐区域内。虽然没有必要使数据项连续达到某种程度的合并,但是32线程扭曲的100%带宽利用率(“完美”)合并负载意味着每个线程使用唯一的32位项目,所有这些都是连续且包含在全局存储器中的单个128字节对齐序列中。

一个方便的心智模型是对比结构的Arrary(AoS)(对应于你的情况2和3)和一个数组结构(SoA),这基本上是你的第一个案例。 SoA通常比AoS提供更好的合并可能性。从nvidia webinar page您可能会发现this presentation有趣,特别是幻灯片11-22左右。

答案 1 :(得分:0)

“最佳做法指南”中的其他一些相关信息:

  

对于计算能力2.x的设备,要求可以是   很容易总结:一个线程的并发访问   warp将合并为多个等于该数字的事务   服务warp的所有线程所需的缓存行数。通过   默认情况下,所有访问都通过L1缓存,其为128字节行。   对于分散的访问模式,为了减少过度捕获,有时可以   仅在L2中缓存很有用,L2缓存较短的32字节段   (参见CUDA C编程指南)。

编译器标志:-Xptxas -dlcm=cg将禁用L1缓存。即仅使用L2,用于合并不良的数据。