Compare commits

..

No commits in common. "v0.03" and "main" have entirely different histories.
v0.03 ... main

12 changed files with 7 additions and 302 deletions

5
.idea/.gitignore vendored
View File

@ -1,5 +0,0 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/

View File

@ -1,15 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="jdk" jdkName="C:\Users\creat\miniconda3" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="PyDocumentationSettings">
<option name="format" value="PLAIN" />
<option name="myDocStringFormat" value="Plain" />
</component>
<component name="TestRunnerService">
<option name="PROJECT_TEST_RUNNER" value="py.test" />
</component>
</module>

View File

@ -1,24 +0,0 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="PyCompatibilityInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ourVersions">
<value>
<list size="4">
<item index="0" class="java.lang.String" itemvalue="3.14" />
<item index="1" class="java.lang.String" itemvalue="3.13" />
<item index="2" class="java.lang.String" itemvalue="3.12" />
<item index="3" class="java.lang.String" itemvalue="3.11" />
</list>
</value>
</option>
</inspection_tool>
<inspection_tool class="PyPep8NamingInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<option name="ignoredErrors">
<list>
<option value="N806" />
</list>
</option>
</inspection_tool>
</profile>
</component>

View File

@ -1,6 +0,0 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Black">
<option name="sdkName" value="C:\Users\creat\miniconda3" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="C:\Users\creat\miniconda3" project-jdk-type="Python SDK" />
</project>

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/Intro-ops.iml" filepath="$PROJECT_DIR$/.idea/Intro-ops.iml" />
</modules>
</component>
</project>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@ -5,81 +5,15 @@ import tilelang.language as T
@tilelang.jit
# 按readme环境配完了来实现第一个 tilelang kernel - copy_kernel
# 基本概念:
# 1.@tilelang.jit 装饰器:将函数编译为 GPU 代码
# tilelang 库:提供 TileLang 语言的 API
# 包括所有 TileLang “原语”,用于定义和编译 kernel
# TileLang 原语TileLang 语言的基本操作,如 T.copy、T.empty、T.const 等
# TileLang kernelTileLang 语言的函数,用于在 GPU 上并行执行数据操作
# TileGPU 处理的基本数据块,类似 CUDA 的 thread block
# 每个 tile 包含 BLOCK_N 个数据元素
# 2.函数参数src, BLOCK_N, dtype
# 3.函数返回值out
def copy_kernel(src, BLOCK_N: int, dtype):
"""
TileLang copy kernel - 将数据从 src 拷贝到 out
参数说明
- src: 输入张量形状为 (N,), 类型为 dtype
- BLOCK_N: 每个 tile 的大小编译时常量
- dtype: 数据类型 float32
- N: 全局编译时常量通过 T.const("N") 获取表示张量总长度
功能 src 的内容逐 tile 拷贝到 out
"""
N = T.const("N")
src: T.Tensor((N,), dtype)
out = T.empty((N,), dtype)
# 张量操作:
# # 1. 创建张量
# out = T.empty((N,), dtype) # 创建空张量
# out = T.fill(frag, 0.0) # 创建全零张量
# out = T.fill(frag, value) # 创建全 value 的张量
# out = T.alloc_fragment(shape, dtype) # 创建空 fragment分配内存值未定义
# # 2. 张量类型声明
# src: T.Tensor((N,), dtype) # 声明张量形状和类型
# DONE: implement a tile-wise copy kernel.
# TODO: implement a tile-wise copy kernel.
#
# Suggested steps:
# 1. Launch one TileLang kernel over the N // BLOCK_N tiles.
# 2. Use T.copy to move one tile from src to out.
# 计算 tile 数量
num_tiles = N // BLOCK_N
# 并行遍历所有 tiles
"""
两种循环
# 并行循环(每个 iteration 在不同 GPU 线程执行)
for idx in T.Parallel(num_tiles):
# 每个线程独立执行
T.copy(src[idx*BLOCK_N:(idx+1)*BLOCK_N],
out[idx*BLOCK_N:(idx+1)*BLOCK_N])
# 串行循环(在同一个线程内顺序执行)
for i in T.Serial(10):
# 顺序执行 10 次
...
"""
for tile_idx in T.Parallel(num_tiles):
# 计算当前 tile 的起始偏移
offset = tile_idx * BLOCK_N
# 使用 T.copy 搬运一个 tile 的数据
# # 3. 张量切片
srcTile = src[offset:offset+BLOCK_N] # 提取一个 tile
outTile = out[offset:offset+BLOCK_N] # 提取一个 tile
T.copy(srcTile, outTile) # 搬运一个 tile 的数据
# T.copy(srcTile, outTile) # 搬运一个 tile 的数据,等价于 T.copy(src[offset:offset+BLOCK_N], out[offset:offset+BLOCK_N])
# # 基础用法:将 src 的一部分拷贝到 out
# T.copy(src[start:end], out[start:end])
#
# # 示例:拷贝一个 tile
# T.copy(src[offset:offset+BLOCK_N],
# out[offset:offset+BLOCK_N])
return out
return out

View File

@ -15,7 +15,7 @@ def reduce_sum_kernel(src, BLOCK_N: int, BLOCK_M: int):
src: T.Tensor((N, M), dtype)
out = T.empty((N,), dtype)
# DONE: implement a tiled row-wise reduce_sum kernel.
# TODO: implement a tiled row-wise reduce_sum kernel.
#
# Suggested steps:
# 1. Launch one TileLang kernel over row tiles.
@ -26,32 +26,4 @@ def reduce_sum_kernel(src, BLOCK_N: int, BLOCK_M: int):
# 6. Call T.reduce_sum on the fragment and accumulate into the output fragment.
# 7. Copy the final output fragment back to global memory.
# 计算行和列的 tile 数量
num_row_tiles = N // BLOCK_N
num_col_tiles = M // BLOCK_M
for row_idx in T.Parallel(num_row_tiles):
# 计算当前 tile 的起始偏移
row_offset = row_idx * BLOCK_N
in_frag = T.alloc_fragment((BLOCK_N, BLOCK_M), dtype)
out_frag = T.alloc_fragment((BLOCK_N,), dtype)
row_sum_frag = T.alloc_fragment((BLOCK_N,), dtype)
# 将 out_frag 初始化为 0
out_frag = T.fill(out_frag, 0.0)
for col_idx in T.Serial(num_col_tiles):
col_offset = col_idx * BLOCK_M
# 将一个列 tile 加载到共享内存
T.copy(src[row_offset:row_offset + BLOCK_N, col_offset:col_offset + BLOCK_M], in_frag)
T.reduce_sum(in_frag, row_sum_frag) # 将每行的和写入 row_sum_frag
# 累加到输出 fragment
out_frag = out_frag + row_sum_frag
# 将最终结果写回全局内存
T.copy(out_frag, out[row_offset:row_offset + BLOCK_N])
return out

View File

@ -16,7 +16,7 @@ def softmax_kernel(src, BLOCK_N: int, BLOCK_M: int):
src: T.Tensor((N, M), dtype)
out = T.empty((N, M), dtype)
# DONE: implement a tiled row-wise softmax kernel.
# TODO: implement a tiled row-wise softmax kernel.
#
# Suggested steps:
# 1. Launch one TileLang kernel over row tiles.
@ -33,72 +33,4 @@ def softmax_kernel(src, BLOCK_N: int, BLOCK_M: int):
# - normalize with the final lse
# - copy the result tile to global memory
row_tiles = N // BLOCK_N
col_tiles = M // BLOCK_M
# Step 2: Allocate fragments
src_frag = T.alloc_fragment((BLOCK_N, BLOCK_M), dtype)
out_frag = T.alloc_fragment((BLOCK_N, BLOCK_M), dtype)
exp_frag = T.alloc_fragment((BLOCK_N, BLOCK_M), dtype)
# Running state fragments
running_max_frag = T.alloc_fragment((BLOCK_N,), dtype) # 跨tile累积的行最大值
running_sum_frag = T.alloc_fragment((BLOCK_N,), dtype) # 跨tile累积的指数和
# Per-tile fragments
tile_max_frag = T.alloc_fragment((BLOCK_N,), dtype)
tile_sum_frag = T.alloc_fragment((BLOCK_N,), dtype)
# Step 1: Launch parallel kernel over row tiles
for row_idx in T.Parallel(row_tiles):
row_offset = row_idx * BLOCK_N
# Step 3: Initialize running log-sum-exp state
running_max_frag = T.fill(running_max_frag, float('-inf'))
running_sum_frag = T.fill(running_sum_frag, 0.0)
# Step 4: First pass - compute running lse across column tiles
for col_idx in T.Serial(col_tiles):
col_offset = col_idx * BLOCK_M
# 4.1 Copy input tile
T.copy(src[row_offset:row_offset + BLOCK_N, col_offset:col_offset + BLOCK_M], src_frag)
# 4.2 Compute tile max
T.reduce_max(tile_max_frag, src_frag)
# 4.3 Compute exp2(x - tile_max) for numerical stability
exp_frag = T.exp2((src_frag - tile_max_frag) * log2_e)
# 4.4 Compute tile sum of exp values
T.reduce_sum(tile_sum_frag, exp_frag)
# 4.5 Update running max and sum
# 找到两个max中的较大值
max_val = T.max(running_max_frag, tile_max_frag) # 假设 T.max 存在
# 使用数学公式合并sum
term1 = running_sum_frag * T.exp2((running_max_frag - max_val) * log2_e)
term2 = tile_sum_frag * T.exp2((tile_max_frag - max_val) * log2_e)
sum_val = term1 + term2
running_max_frag = max_val
running_sum_frag = sum_val
# Compute final log-sum-exp: lse = max + log(sum)
final_lse_frag = running_max_frag + (T.log2(running_sum_frag) / log2_e)
# Step 5: Second pass - normalize each tile with final lse
for col_idx in T.Serial(col_tiles):
col_offset = col_idx * BLOCK_M
# 5.1 Copy input tile again
T.copy(src[row_offset:row_offset + BLOCK_N, col_offset:col_offset + BLOCK_M], src_frag)
# 5.2 Normalize: softmax(x) = exp(x - lse)
out_frag = T.exp2((src_frag - final_lse_frag) * log2_e)
# 5.3 Write result to global memory
T.copy(out_frag, out[row_offset:row_offset + BLOCK_N, col_offset:col_offset + BLOCK_M])
return out

View File

@ -1,34 +0,0 @@
from __future__ import annotations
import tilelang
import tilelang.language as T
@tilelang.jit
def vector_add_kernel(a, b, BLOCK_N: int, dtype):
N = T.const("N")
a: T.Tensor((N,), dtype)
b: T.Tensor((N,), dtype)
out = T.empty((N,), dtype)
# DONE: implement a tile-wise vector add kernel.
#
# Suggested steps:
# 1. Launch one TileLang kernel over the N // BLOCK_N tiles.
# 2. Compute the tile base offset.
# 3. Use T.Parallel(BLOCK_N) to fill out[base + i] = a[base + i] + b[base + i].
num_tiles = N // BLOCK_N
for tile_idx in T.Parallel(num_tiles):
# 计算当前 tile 的起始偏移
offset = tile_idx * BLOCK_N
# 使用 T.copy 搬运一个 tile 的数据
aTile = a[offset:offset+BLOCK_N] # 提取一个 tile
bTile = b[offset:offset+BLOCK_N] # 提取一个 tile
outTile = out[offset:offset+BLOCK_N] # 提取一个 tile
T.copy(aTile+bTile, outTile) # 搬运一个 tile 的数据
return out
# 或者也可以像上面那样注释一样写:
# for i in T.Parallel(BLOCK_N): # 内层并行
# out[offset + i] = a[offset + i] + b[offset + i]

View File

@ -1,44 +1,16 @@
from __future__ import annotations
import tilelang
import tilelang.language as T
@tilelang.jit
def vector_add_kernel(a, b, BLOCK_N: int, dtype):
"""
TileLang vector add kernel - 使用 T.copy 实现向量加法
核心逻辑
1. 将全局内存中的数据块tile加载到片上 fragment
2. 在片上执行加法运算
3. 将结果写回全局内存
"""
N = T.const("N")
a: T.Tensor((N,), dtype)
b: T.Tensor((N,), dtype)
out = T.empty((N,), dtype)
# 分配片上内存fragment
a_frag = T.alloc_fragment((BLOCK_N,), dtype)
b_frag = T.alloc_fragment((BLOCK_N,), dtype)
out_frag = T.alloc_fragment((BLOCK_N,), dtype)
num_tiles = N // BLOCK_N
# 并行处理每个 tile
for tile_idx in T.Parallel(num_tiles):
offset = tile_idx * BLOCK_N
# ========== T.copy 核心用法 ==========
# 1. 从全局内存 -> 片上内存(加载)
T.copy(a[offset:offset + BLOCK_N], a_frag)
T.copy(b[offset:offset + BLOCK_N], b_frag)
# 2. 片上计算(高效)
out_frag = a_frag + b_frag
# 3. 从片上内存 -> 全局内存(存储)
T.copy(out_frag, out[offset:offset + BLOCK_N])
# TODO: implement a tile-wise vector add kernel.
#
# Suggested steps:
@ -46,4 +18,4 @@ def vector_add_kernel(a, b, BLOCK_N: int, dtype):
# 2. Compute the tile base offset.
# 3. Use T.Parallel(BLOCK_N) to fill out[base + i] = a[base + i] + b[base + i].
return out
return out