Intro-ops/notebooks/01_copy_kernel.ipynb

246 lines
6.8 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 01 — Copy 算子\n",
"\n",
"## 学习目标\n",
"\n",
"1. 理解 **grid-stride loop**:为什么它可以处理任意大小的 tensor\n",
"2. 完成 NVIDIA CUDA kernel 的 TODO\n",
"3. 完成 TileLang kernel 的 TODO\n",
"4. 验证正确性 + 查看 benchmark 结果\n",
"\n",
"Copy 是最简单的算子——把数据从 `src` 搬到 `dst`,不涉及任何计算。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 概念导入Grid-Stride Loop\n",
"\n",
"GPU 有成千上万个线程,但 tensor 大小可能更大。Grid-stride loop 让每个线程处理多个元素:\n",
"\n",
"```python\n",
"# 伪代码\n",
"for i in range(thread_idx, N, grid_total_threads):\n",
" dst[i] = src[i]\n",
"```\n",
"\n",
"- `grid_total_threads = gridDim.x * blockDim.x`(所有线程总数)\n",
"- 步长 = grid_total_threads每个线程“跨步”处理\n",
"- 循环条件 `i < N` 保证不越界\n",
"\n",
"详细图示见 [docs/diagrams/grid-stride-loop.md](../docs/diagrams/grid-stride-loop.md)。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## PyTorch 参考实现\n",
"\n",
"先看看用 PyTorch 怎么做 copy——训练营的目标就是实现和它一样的功能"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import torch\n",
"\n",
"# PyTorch 版 copy\n",
"def pytorch_copy(src: torch.Tensor) -> torch.Tensor:\n",
" return src.clone()\n",
"\n",
"# 测试\n",
"src = torch.randn(1024, device=\"cuda\", dtype=torch.float32)\n",
"expected = pytorch_copy(src)\n",
"print(f\"输入: {src[:5]}\")\n",
"print(f\"输出: {expected[:5]}\")\n",
"print(f\"一致: {torch.allclose(src, expected)}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## NVIDIA CUDA Kernel\n",
"\n",
"打开 `ops/copy/nvidia/kernel.cuh`,你会看到这个骨架。请填写 `TODO` 部分:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# 以下代码来自 ops/copy/nvidia/kernel.cuh仅供参考——请直接编辑该文件\n",
"\n",
"KERNEL_SKELETON = \"\"\"\n",
"template <typename T>\n",
"__global__ void copy_contiguous_kernel(T *dst, const T *src, int64_t n) {\n",
" // TODO: implement a grid-stride loop copy kernel.\n",
" //\n",
" // Suggested steps:\n",
" // 1. Compute the global thread index.\n",
" // int64_t idx = blockIdx.x * blockDim.x + threadIdx.x;\n",
" // 2. Compute the grid-wide stride.\n",
" // int64_t stride = gridDim.x * blockDim.x;\n",
" // 3. Loop over i = idx; i < n; i += stride.\n",
" // 4. Copy src[i] to dst[i].\n",
"}\n",
"\"\"\"\n",
"print(KERNEL_SKELETON)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 你的任务\n",
"\n",
"编辑 `ops/copy/nvidia/kernel.cuh`,完成 TODO 后,重新构建并运行下面的验证:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import subprocess, sys\n",
"\n",
"result = subprocess.run([\n",
" sys.executable, \"-m\", \"pytest\",\n",
" \"tests/op_tests/test_copy.py\", \"-v\",\n",
" \"--backend\", \"nvidia\"\n",
"], capture_output=True, text=True)\n",
"\n",
"print(result.stdout)\n",
"if result.returncode == 0:\n",
" print(\"✓ NVIDIA copy kernel 测试全部通过!\")\n",
"else:\n",
" print(result.stderr[-500:] if result.stderr else \"\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## TileLang Kernel\n",
"\n",
"打开 `ops/copy/tilelang/kernel.py`,填写 TODO"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"TILELANG_SKELETON = \"\"\"\n",
"import tilelang\n",
"import tilelang.language as T\n",
"\n",
"@tilelang.jit\n",
"def copy_kernel(src, BLOCK_N: int, dtype):\n",
" N = T.const(\"N\")\n",
" src: T.Tensor((N,), dtype)\n",
" out = T.empty((N,), dtype)\n",
"\n",
" # TODO: implement a tile-wise copy kernel.\n",
" #\n",
" # Suggested steps:\n",
" # 1. Launch one TileLang kernel over the N // BLOCK_N tiles.\n",
" # for i in T.Parallel(N // BLOCK_N):\n",
" # 2. Use T.copy to move one tile from src to out.\n",
" # tile = T.copy(src[i * BLOCK_N : (i + 1) * BLOCK_N])\n",
" # out[i * BLOCK_N : (i + 1) * BLOCK_N] = tile\n",
"\n",
" return out\n",
"\"\"\"\n",
"print(TILELANG_SKELETON)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# 验证 TileLang 版本\n",
"result = subprocess.run([\n",
" sys.executable, \"-m\", \"pytest\",\n",
" \"tests/op_tests/test_copy.py\", \"-v\",\n",
" \"--backend\", \"tilelang\"\n",
"], capture_output=True, text=True)\n",
"\n",
"print(result.stdout)\n",
"if result.returncode == 0:\n",
" print(\"✓ TileLang copy kernel 测试全部通过!\")\n",
"else:\n",
" print(result.stderr[-500:] if result.stderr else \"\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 对比:你的实现 vs PyTorch"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from operator_runtime import copy\n",
"\n",
"src = torch.randn(1024, device=\"cuda\", dtype=torch.float32)\n",
"my_out = copy(src, backend=\"nvidia\")\n",
"torch_out = src.clone()\n",
"\n",
"print(f\"输入: {src[:5]}\")\n",
"print(f\"我的 kernel: {my_out[:5]}\")\n",
"print(f\"PyTorch: {torch_out[:5]}\")\n",
"print(f\"一致: {torch.allclose(my_out, torch_out)}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 检查清单\n",
"\n",
"- [ ] 理解 grid-stride loop 原理\n",
"- [ ] 理解 global memory 合并访问coalesced access\n",
"- [ ] NVIDIA `kernel.cuh` TODO 完成\n",
"- [ ] TileLang `kernel.py` TODO 完成\n",
"- [ ] 两种后端测试全部通过\n",
"- [ ] benchmark 跑通,带宽数据合理"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python",
"version": "3.12.0"
}
},
"nbformat": 4,
"nbformat_minor": 5
}