cute(4)第一个Kernel
第 4 课:第一个 GPU Kernel
对应代码:
04_first_kernel.cu需要 GPU。
核心概念
在 GPU kernel 中使用 CuTe Tensor:
make_gmem_ptr包装全局内存指针local_tile按 block 切分工作- 每个线程用一维 index 访问 tile 中的元素
代码解析
Kernel 1:向量加法 C = A + B
1 | // ============================================================ |
1 | template <int kBlockSize> |
第一步:包装全局内存指针
1 | auto gA = make_tensor(make_gmem_ptr(A), make_shape(N)); |
make_gmem_ptr(A)把原始 CUDA 指针标记为"全局内存指针"- CuTe 用这个标记来选择正确的内存访问指令(global load vs shared load)
- 创建了一个一维 tensor,shape =
(N,)
第二步:local_tile 切分
1 | auto tA = local_tile(gA, make_tile(Int<kBlockSize>{}), make_coord(blockIdx.x)); |
- 把长度 N 的数组按 kBlockSize=256 切成
N/256个 tile - 取第
blockIdx.x个 tile - 结果 tA 的 shape =
(256,),就是当前 block 负责的 256 个元素
第三步:线程分配
1 | int elements_per_thread = kBlockSize / blockDim.x; // 256/256 = 1 |
这里手动用 threadIdx.x 计算每个线程负责的 index。每个线程处理 kBlockSize/blockDim.x 个元素。
当 kBlockSize == blockDim.x 时(本例就是),每个线程只处理 1 个元素。
Launch 配置:
1 | constexpr int kBlock = 256; |
Kernel 2:矩阵转置
1 | // ============================================================ |
1 | __global__ void transpose_kernel(float* dst, const float* src, int M, int N) { |
全局 Tensor 创建:
1 | auto gSrc = make_tensor(make_gmem_ptr(src), make_shape(M, N), make_stride(N, 1)); |
- src 是
(M, N)row-major:stride =(N, 1) - dst 是
(N, M)row-major:stride =(M, 1) - 转置后 dst 的行数=N,列数=M
local_tile 切分:
1 | auto tileSrc = local_tile(gSrc, make_tile(Int<kTileM>{}, Int<kTileN>{}), |
注意坐标和 tile 大小的对应关系:
- src 用
(blockIdx.y, blockIdx.x)取 M 方向第 y 个、N 方向第 x 个 tile - dst 用
(blockIdx.x, blockIdx.y)← 行列互换,因为转置后 N 和 M 交换了
线程分配 + 转置:
1 | int tid = threadIdx.y * blockDim.x + threadIdx.x; // 线程编号 |
1024 个元素 / 256 个线程 = 每个线程处理 4 个元素。
转置的核心就一行:tileDst(n, m) = tileSrc(m, n),交换 m 和 n 的位置。
Launch 配置:
1 | dim3 block(32, 8); // 256 线程 |
关键 API
| API | 作用 |
|---|---|
make_gmem_ptr(ptr) |
标记指针为全局内存,CuTe 据此选择访问指令 |
make_tensor(gmem_ptr, shape, stride) |
在全局内存上创建 tensor |
local_tile(tensor, tile, coord) |
按 tile 切分并取指定 tile |
tensor(i) / tensor(i, j) |
一维/二维访问元素 |
04 vs 05 的线程分配方式对比
| 04(手动分配) | 05(TiledCopy) | |
|---|---|---|
| 线程映射 | 手动算 threadIdx.x + i * blockDim.x |
get_slice(threadIdx.x) 自动映射 |
| partition | 无,自己算 index | partition_S/D 自动切分 |
| copy | 逐元素赋值 | cute::copy(tiled_copy, src, dst) |
| 灵活性 | 简单场景够用 | 复杂场景(128bit 向量化等)必须用 |
04 是手动挡,05 开始进入自动挡。
- 标题: cute(4)第一个Kernel
- 作者: 鱿鱼圈
- 创建于 : 2026-06-09 22:13:32
- 更新于 : 2026-06-14 23:33:01
- 链接: https://yuyanqi.com/2026/06/09/cute(4)第一个Kernel/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论