Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Warp Tiling 中的疑问 #58

Open
muyuuuu opened this issue Dec 9, 2024 · 2 comments
Open

Warp Tiling 中的疑问 #58

muyuuuu opened this issue Dec 9, 2024 · 2 comments

Comments

@muyuuuu
Copy link
Contributor

muyuuuu commented Dec 9, 2024

这一节好像很考察对 cuda 结构体系的理解

  • 印象中,多个线程块会被分到 cuda 中执行,一个线程块内的所有线程只能分配到一个 SM,但一个 SM 可以被分配多个线程块。
  • 在执行的时候,线程块会被划分为线程束,线程束有 32 个线程,由线程束调度器调度执行,这 32 个线程是并行执行的。
  • 如果每个线程束占用的资源并不是很多,SM 能同时执行多个线程束。

我对这份代码的理解是:128 个线程,划分为 4 个 32x32 的区域,循环执行。那么是怎么和 readme 中的概念对应呢?

Block Tile: 不同的块可以在不同的 SM 上并行执行。

这个本来就可以?

Warp Tile: 不同的 warps 可以在不同的 warp 调度器上并行执行,并且同时在同一个 warp 调度器上执行
Thread Tile: 指令可以在同一个 CUDA 核心上并行执行(即指令级并行性,又称 ILP)

这两者如何体现在代码中呢?

@AndSonder
Copy link
Collaborator

一个 Warp 是由 32 个线程组成的基本执行单元
一个 Block(线程块)由若干个线程组成,这些线程全部在同一个 SM 中执行
不同的 Block 可以分配到不同的 SM(Streaming Multiprocessor) 上并行执行

问题1:

“128 个线程,划分为 4 个 32x32 的区域”
在优化矩阵乘法时,我们常说的 “Block Tile”、“Warp Tile” 指的是数据处理的逻辑切分(即将大矩阵分块处理),而不是 GPU 中线程的实际物理布局本身。简单来说,“128 个线程” 是指这个线程块中拥有总计 128 个线程,而将其 “划分为 4 个 32x32 的区域” 则通常是指在数据空间(例如矩阵的行列)上将要处理的矩阵区域分成若干子块,每个子块为 32×32 大小的元素子矩阵

假设有一个矩阵分块大小(Block Tile)为 128×128 个元素,这意味着一个线程块要负责处理 128 行 × 128 列的矩阵元素。为了更好地利用并行性,我们可以把这个 128×128 的数据块再细分成 4 个 32×32 的子块(每个子块对应 32 行 × 32 列的元素)

问题2:

Warp 尺度的索引计算代码如下:

const uint warp_idx = threadIdx.x / WARPSIZE;
const uint warp_col = warp_idx % (BN / WN);
const uint warp_row = warp_idx / (BN / WN);

这里通过 warp_idx 明确区分出当前线程属于哪个 warp,并利用 warp_row, warp_col 来给 warp 分配数据子块。这让我们可以在 Block 中进一步细分数据,在 Warp 层次上进行分块(Warp Tile),使每个 Warp 负责处理数据块的一部分。

Warp 级的循环结构,在代码中,会有类似:

for (uint warp_sub_row_idx = 0; warp_sub_row_idx < WMITER; ++warp_sub_row_idx) {
    for (uint warp_sub_col_idx = 0; warp_sub_col_idx < WNITER; ++warp_sub_col_idx) {
        // Warp 级别的数据处理和计算
    }
}

WMITER、WNITER 分别代表在 Warp 尺度上进一步对数据进行迭代处理的次数。这些循环使得 Warp 成为一层独立的分解层次:我们先在 Block 中分好大 Tile,再在 Warp 层面将 Tile 细化成更小的子块,从而在 Warp 内并行处理数据。这样,每个 Warp 内的 32 个线程协作处理这块子数据,映射到硬件上就是利用了多 Warp 调度器以及 Warp 并行执行的特性。

Thread Tile 在代码中的体现也是类似的,可以再结合源码理解一下

@muyuuuu
Copy link
Contributor Author

muyuuuu commented Dec 13, 2024

Image

是上图我理解的意思吗?以 tiled2d 的 kernel 为例,手动划分好 warp 而不是自动划分。在 warp tile 之前,也会以线程束为基本单位去执行这个 block。如果是这样的话,warp tile 的优势是?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants