【问题标题】:In an elementwise add() CUDA kernel, why is the stride blockDim.x * gridDim.x? [closed]在 elementwise add() CUDA 内核中,为什么步幅是 blockDim.x * gridDim.x? [关闭]
【发布时间】:2017-05-28 02:32:13
【问题描述】:

我正在学习 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 的不同内存中?

【问题讨论】:

  • 您的编辑都是关于 CPU 的,但您的标志是 cudagpgpu... 您从哪里获得 CPU 版本?您是否读过有关如何在 CPU 上并行化代码的文章?如果您粘贴教程中的代码,请添加指向教程的链接。你看过programming guide of Nvidia吗?它非常紧凑,但 imo 易于理解,并且不是从大步开始而是使用更简单的add 内核。

标签: cuda gpgpu


【解决方案1】:

在 CUDA 编程模型中,计算是由“线程”的“块”执行的。每个线程在块内都有一个线程 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 执行加法操作,并且现在想要继续处理其他元素,我们可以提高它们的索引以确保它们中的每一个都有一个新的和不同的索引来工作开,并且没有发现任何元素。步幅为 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;
    }
}

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

【讨论】:

    猜你喜欢
    • 2017-12-25
    • 2021-11-11
    • 1970-01-01
    • 1970-01-01
    • 2014-08-31
    • 2016-10-13
    • 2017-11-04
    • 2022-09-29
    • 2010-12-13
    相关资源
    最近更新 更多