cute(5)tiled_copy
前置阅读
5 Tiled Copy
对应代码:
05_tiled_copy.cu需要 GPU。
核心概念
CuTe 的数据搬运体系:
- Copy_Atom:一条拷贝指令的最小单元
- make_tiled_copy:把 atom 扩展到多线程协作
- partition_S / partition_D:自动计算每个线程的读/写位置
- get_slice:从 tiled_copy 中取出"当前线程"的那一份
整体数据流
1 | global memory (src) ──copy──> shared memory ──copy──> global memory (dst) |
目的:验证 tiled_copy 机制能正确搬运数据。src 和 dst 内容应完全一致。
代码解析
1 | // ============================================================ |
1. 全局 Tensor + local_tile
1 | auto gSrc = make_tensor(make_gmem_ptr(src), make_shape(M, N), make_stride(N, Int<1>{})); |
- 全局 tensor
(M, N)row-major local_tile取当前 block 的(32, 32)tileblockIdx.y对应 M 方向,blockIdx.x对应 N 方向
2. Shared Memory Tensor
1 | __shared__ float smem_buf[kTileM * kTileN]; |
make_smem_ptr标记为 shared memory 指针(类似make_gmem_ptr)- layout 是
(32, 32):(32, 1)row-major
3. 构造 TiledCopy(核心)
1 | using CopyAtom = Copy_Atom<UniversalCopy<float>, float>; |
三个参数的含义:
| 参数 | 含义 | 本例 |
|---|---|---|
| CopyAtom | 一条指令能搬多少 | 1 个 float(4 字节) |
| 线程 layout | 128 个线程在 tile 上怎么排列 | 32 行 × 4 列,row-major |
| 值 layout | 每个线程每次负责多少元素 | 1×1 |
一次能覆盖多少:32×4 × 1×1 = 128 个元素。
tile 总共多少:32×32 = 1024 个元素。
每个线程要跑多少轮:1024 / 128 = 8 轮。
4. get_slice —— 取出当前线程的工作描述
1 | auto thr_copy = tiled_copy.get_slice(threadIdx.x); |
get_slice(threadIdx.x) 做了什么:
从 128 线程的 tiled_copy 中,根据 threadIdx.x 找到"我是第几号线程",返回一个 thread-level 的 copy 对象。
这个对象知道当前线程在 tile 中的位置,之后可以用它来切分 Source 和 Destination。
类比:tiled_copy 是"全队的工作分配表",get_slice 是"从表里找到我的那一行"。
5. partition_S / partition_D —— 自动计算读写位置
1 | auto tSrc = thr_copy.partition_S(src_tile); // 从 global 切出"我要读的" |
partition_S(Source):把 src_tile 按线程分配,返回当前线程要读的部分partition_D(Destination):把 sA 按线程分配,返回当前线程要写的部分
结果 shape:(CPY, CPY_M, CPY_N) = (1, 1, 8)
1 | CPY = 1 ← 每次拷 1 个元素 |
6. 每个线程的具体工作
线程 layout (32, 4):(4, 1) row-major,线程号到 tile 位置的映射:
1 | threadIdx.x = row * 4 + col |
tile 是 32×32,线程一次覆盖 32×4,N 方向要跑 8 轮:
1 | 32×32 tile 的分配: |
Thread 0(row=0, col=0)负责的 8 个元素:
1 | 第 0 轮:tile(0, 0) |
Thread 5(row=1, col=1)负责的 8 个元素:
1 | 第 0 轮:tile(1, 1) |
7. 执行拷贝
1 | // global → shared |
cute::copy 对当前线程遍历所有 8 个元素逐个拷贝,等价于:
1 | for (int i = 0; i < 8; ++i) { |
__syncthreads() 确保所有线程都写完 smem 后再读。
反方向拷贝时,Source 变成 smem,Destination 变成 global。
make_tiled_copy 参数详解
1 | make_tiled_copy(CopyAtom, ThreadLayout, ValLayout) |
一次覆盖的 tile 大小 = ThreadLayout的shape × ValLayout的shape
1 | ThreadLayout = (32, 4) |
如果把 ValLayout 改成 (1, 4):
- 每个线程每次拷 4 个 float(128bit 向量化)
- 一次覆盖
(32×1, 4×4) = (32, 16) = 512个元素 - 只需 2 轮就能覆盖
32×32tile
高性能 kernel 中常见配置:
1 | // 128bit 异步拷贝 |
API 总结
| API | 作用 |
|---|---|
Copy_Atom<Op, T> |
定义一条拷贝指令的最小单元 |
make_tiled_copy(atom, thr_layout, val_layout) |
创建多线程协作的 tiled copy |
tiled_copy.get_slice(threadIdx.x) |
取出当前线程的工作描述 |
thr_copy.partition_S(tensor) |
切分 Source:当前线程要读哪些 |
thr_copy.partition_D(tensor) |
切分 Destination:当前线程要写哪些 |
cute::copy(tiled_copy, src, dst) |
执行拷贝 |
make_smem_ptr(ptr) |
标记为 shared memory 指针 |
与 04 的对比
| 04(手动) | 05(TiledCopy) | |
|---|---|---|
| 线程→元素映射 | 手动算 index | get_slice + partition 自动 |
| 拷贝操作 | 逐元素赋值 | cute::copy |
| 向量化 | 自己实现 | 换 CopyAtom 即可(如 uint128_t) |
| 适用场景 | 简单 kernel | 复杂数据搬运(G2S, S2R, R2S, S2G) |
从 05 开始,CuTe 自动管理线程分配和数据搬运,你只需描述"怎么排线程"和"每次搬多少",剩下的全由 partition 自动计算。
- 标题: cute(5)tiled_copy
- 作者: 鱿鱼圈
- 创建于 : 2026-06-10 22:13:32
- 更新于 : 2026-06-14 23:33:01
- 链接: https://yuyanqi.com/2026/06/10/cute(5)tiled_copy/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。