在元素方面的add()CUDA内核中,为什么stride blockDim.x * gridDim.x?

时间:2017-05-28 02:32:13

标签: cuda gpgpu

我正在学习CUDA编程,而且我无法理解这个元素添加内核中的步幅:

// performs vector addition
// a, b, c are vectors and added values are stored in a and b, while the results are stored in c.

#define N 10

__global__ void add(int* a, int* b, int* c) {
    int tid = threadIdx.x + blockIdx.x*blockDim.x;
    while(tid < N) {
        c[tid] = a[tid] + b[tid];
        tid += blockDim.x * gridDim.x;
    }
}

以下是此版本的CPU版本:

void add(int* a, int* b, int* c) {
    int tid = 0;    // start from CPU 0
    while(tid < N) {
        c[tid] = a[tid] + b[tid];
        tid += 2;   // assume there are 2 CPUs
    }
}

我经历了几个教程,所有这些教程都从这个add内核开始。

我不明白tid的步伐来自哪里?

修改

现在我得到了步幅的价值意味着什么。我认为这意味着如果我有 2 CPU,当两个正在运行的线程中的一个完成时,我必须将tid加2,因为tid + 1由另一个核处理。

但问题是,CPU1中的tid与CPU2中的tid不同吗?我认为它们不能是同一个变量,而且这些值存储在CPU的不同存储器中?

1 个答案:

答案 0 :(得分:1)

在CUDA编程模型中,计算由&#34;块&#34;执行。 &#34;线程&#34;。每个线程在块中都有一个线程id和一个块id。所以,就像在你的CPU示例中一样,如果你用2块大小为3的块启动你的内核,你就会有6个线程:

  • 块0,线程0
  • 块0,线程1
  • 块0,线程2
  • 块1,线程0
  • 块1,线程1
  • 块1,线程2

tid变量将是网格中的整体线程ID,它将块和块内线程ID组合在一起。在示例中,请注意这些线程的tid值将完全覆盖0..5的范围。

现在,如果6个线程各自对数组元素0..5执行add操作,现在想要继续使用其他元素,我们可以推进它们的索引以确保它们中的每一个都获得一个新的和不同的索引来工作在,并没有发现任何元素。大步为6(在我们的例子中)这样做:线程将处理6..11,12..17,18..23等等。因此第一个线程将在0,6,12,18等上工作,第二个线程将在1,7,13,19等上工作,依此类推。

如果编写如下内核,内核可能会更清晰:

__global__ void add(int* a, int* b, int* c) {
    int overall_thread_id = threadIdx.x + blockIdx.x*blockDim.x;
    int overall_num_threads = blockDim.x * gridDim.x;
    int pos = overall_thread_id;
    while(pos < N) {
        c[pos] = a[pos] + b[pos];
        pos += overall_num_threads;
    }
}

至于你的&#34; CPU版本&#34;代码 - 它不起作用,因为即使你有不同的线程(可能在不同的核心上)执行它,它们都将从tid开始为0并以相同的方式前进 - 不像GPU&#34;线程& #34;,每个以不同的tid开头。如果你有一个CPU函数初始化tid = index_of_thread_among_Workers()和2个工作线程,第一个线程将在0,2,4,6等工作,第二个工作线程(索引1)将在1,3,5上工作,7等。