不明白为什么在CUDA中列添加比行添加要快

时间:2019-11-09 15:32:29

标签: cuda

我从CUDA开始,编写了两个用于实验的内核。 乳清都接受3个指向n * n(矩阵仿真)和n数组的指针。

__global__
void th_single_row_add(float* a, float* b, float* c, int n) {
  int idx = blockDim.x * blockIdx.x * n + threadIdx.x * n;
  for (int i = 0; i < n; i ++) {
    if (idx + i >= n*n) return;
    c[idx + i] = a[idx + i] + b[idx + i];
  }
}

__global__
void th_single_col_add(float* a, float* b, float* c, int n) {
  int idx = blockDim.x * blockIdx.x + threadIdx.x;
  for (int i = 0; i < n; i ++) {
    int idx2 = idx + i * n;
    if (idx2 >= n*n) return;
    c[idx2] = a[idx2] + b[idx2];
  }
}

th_single_row_add中,每个线程求和n个元素上的行;在th_single_col_add中,每个线程求和列。 这是n = 1000上的配置文件(1000万个元素)

986.29us  th_single_row_add(float*, float*, float*, int)
372.96us  th_single_col_add(float*, float*, float*, int)

如您所见,列的求和速度快三倍。 我以为,因为在column变体中,循环中的所有索引都相距较远,所以应该变慢,我在哪里弄错了?

1 个答案:

答案 0 :(得分:4)

CUDA中的线程不是单独起作用的,它们是grouped together in warps of 32 threads。这32个线程通常按步执行。发给一个线程的指令在同一时钟周期内同时发给所有32个线程。

例如,如果该指令是读取存储器的指令,则可能需要/请求多达32次独立读取。满足这些读取操作所需的地址的确切模式由您编写的代码确定。如果这些地址在内存中全部“相邻”,那将是一个有效的读取。如果这些地址以某种方式“分散”在内存中,那将是低效的读取,并且速度会变慢。

刚刚描述的这个基本概念在CUDA中称为“合并访问”。您的列求和情况允许在一个warp中合并访问,因为warp中每个线程生成的地址在相邻的列中,并且位置在内存中是相邻的。您的行总和情况打破了这一点。经线中每个线程生成的地址不是相邻的(它们是“列”,彼此之间按数组的宽度分开),因此不“合并”。

性能上的差异是由于内存访问效率上的差异。

您可以通过研究CUDA优化的介绍性方法来研究CUDA中的合并行为,例如here,尤其是幻灯片44-54。