中文内容
NVIDIA CUDA Tile(cuTile)是一种基于 tile 的编程模型,使开发者能够以 tile 级操作——加载、存储和矩阵乘加——来编写 GPU kernel,而无需手动协调线程、warp 和共享内存。
cuTile.jl 将同样基于 tile 的方法带到了动态编程语言 Julia。用户无需下探到 NVIDIA CUDA C++,即可编写自定义 GPU kernel。在 Julia 的科学计算生态中,自定义 kernel 往往至关重要,涵盖微分方程、概率编程和物理仿真等领域。
cuTile Python 拥有一个不断增长的优化 kernel 库,用于 GPU 加速。将这些 kernel 翻译为 cuTile.jl 的能力,使 Julia 生态能够立即访问经过实践检验的实现,而不必从头重写每一个。
本文介绍跨领域特定语言(DSL)的 GPU kernel 翻译,即从 cuTile Python kernel 移植到 cuTile.jl(Julia)。文章展示如何:
- 在 cuTile Python 与 cuTile.jl 之间翻译 GPU kernel:并排讲解一个完整的矩阵乘法示例。
- 避免会破坏朴素翻译的语义陷阱:索引、广播、内存布局和循环形式在这两种 DSL 之间都存在差异,而静默的不匹配会产生错误结果,而不是编译器错误。
- 构建可重复、由技能驱动的 AI 工作流:翻译知识被封装为 TileGym 中的一个 LLM skill,可一次性生成经过验证的 Julia kernel,将一次性的移植工作系统化。
跨 DSL GPU kernel 翻译
cuTile Python 和 cuTile.jl 前端共享同一种 tiled 抽象,因此翻译在很大程度上是算法化的。然而,两种语言在表层上的累积差异并不简单,如表 1 所示。
ct.bid(0))1-based (ct.bid(1))BroadcastingImplicit (a + b)Explicit dot syntax (a .+ b)Memory layoutRow-majorColumn-majorKernel definition@ct.kernel decoratorPlain function ... endConstantsparam: ct.Constant[int] in signatureparam::Int in signature, ct.Constant(val) at launchType conversiontile.astype(ct.float32)convert(ct.Tile{Float32}, tile)Matrix multiplyct.mma(a, b, acc=acc)muladd(a, b, acc)这些翻译在概念上都不难,但如果漏掉一个本应为 ct.bid(1) 的 ct.bid(0),就会导致静默数据损坏。使用 * 而不是 .* 进行逐元素乘法时,Julia 会静默执行矩阵乘法。这类 bug 会耗费数小时。
共享抽象加上一组有限且反复出现的陷阱,非常适合 AI 辅助工作流——前提是模型被教会要注意什么。
将 cuTile Python 翻译为 cuTile.jl
这个过程最好通过实际代码来理解。以下示例来自 TileGym,团队在其中将一组 cuTile Python kernel 移植到 cuTile.jl,并将其打包为一个自包含的 Julia 子项目。
矩阵乘法示例
贯穿示例使用 matmul,它足够复杂,能够展示关键翻译挑战。除了基本语法差异外,翻译还必须处理循环结构、TF32 tensor core 转换,以及从行优先布局到列优先布局的转换。
正文:cuTile Python:
@ct.kernel
def matmul_kernel(A, B, C, tm: ct.Constant[int], tn: ct.Constant[int],
tk: ct.Constant[int]):
bid_m = ct.bid(0)
bid_n = ct.bid(1)
num_k = ct.num_tiles(A, axis=1, shape=(tm, tk))
acc = ct.full((tm, tn), 0, dtype=ct.float32)
dtype = ct.tfloat32 if A.dtype == ct.float32 else A.dtype
for k in range(num_k):
a = ct.load(A, index=(bid_m, k), shape=(tm, tk),
padding_mode=ct.PaddingMode.ZERO)
b = ct.load(B, index=(k, bid_n), shape=(tk, tn),
padding_mode=ct.PaddingMode.ZERO)
a = a.astype(dtype)
b = b.astype(dtype)
acc = ct.mma(a, b, acc)
acc = ct.astype(acc, C.dtype)
ct.store(C, index=(bid_m, bid_n), tile=acc)
正文:cuTile.jl(Julia):
function matmul_kernel(A::ct.TileArray{T,2}, B::ct.TileArray{T,2}, C::ct.TileArray{T,2},
tm::Int, tn::Int, tk::Int) where {T}
bid_m = ct.bid(1)
bid_n = ct.bid(2)
num_k = ct.num_tiles(A, 2, (tm, tk))
acc = zeros(Float32, tm, tn)
U = T === Float32 ? ct.TFloat32 : T
for k in Int32(1):num_k
a = ct.load(A; index=(bid_m, k), shape=(tm, tk), padding_mode=ct.PaddingMode.Zero)
b = ct.load(B; index=(k, bid_n), shape=(tk, tn), padding_mode=ct.PaddingMode.Zero)
a = convert(ct.Tile{U}, a)
b = convert(ct.Tile{U}, b)
acc = muladd(a, b, acc)
end
acc = convert(ct.Tile{T}, acc)
ct.store(C; index=(bid_m, bid_n), tile=acc)
return
end
除了基本语法变化外,请注意以下几点:
- 布局会翻转:Python 中行优先的 A(M,K) 在 Julia 中变为列优先的 A_jl(K,M)。累加器、加载索引和存储索引都会相应改变。如果累加器形状弄错——例如使用 (TM, TN) 而不是 (TN, TM)——就会得到错误结果,且没有编译器警告。
- ct.mma → muladd:cuTile.jl 将矩阵乘加映射为 Julia 标准的 muladd,而 ct.PaddingMode.ZERO 变为 ct.PaddingMode.Zero(PascalCase)。
Softmax 示例
Softmax 进一步增加了复杂度。Julia 中实现了三种策略——tensor memory accelerator(TMA)单 tile、online 和 chunked——以处理不同张量大小。在 matmul 模式之外,softmax 函数还引入了广播点语法(ct.exp(ct.sub(a, b)) → exp.(a .- b))、重命名的归约(ct.max → maximum、ct.sum → sum、axis +1),以及逐元素 ct.maximum(a, b) → max.(a, b)。
但真正的挑战并不是语法,而是在翻译过程中保持正确的运行中 max/sum 统计。
使用 agent skill 生成工作流
该项目的主要成果并不是翻译后的 kernel,而是用于生成它们的 skill。

在此语境下,skill 是位于代码仓库中的结构化知识目录,可由 LLM agent 读取。这个特定 skill 的路径是:.claude/skills/converting-cutile-to-julia/。
.claude/skills/converting-cutile-to-julia/
├── SKILL.md # Entry point: workflow overview, top pitfalls
├── translations/
│ └── workflow.md # Step-by-step conversion with checklists
├── references/
│ ├── api-mapping.md # Bidirectional Python↔Julia API table
│ ├── critical-rules.md # 17 rules (indexing, broadcasting, loops, ...)
│ ├── debugging.md # Error diagnosis for MethodError, IRError, etc.
│ └── testing.md # Test patterns, tolerances per dtype
├── scripts/
│ └── validate_cutile_jl.py # Static checker for common anti-patterns
└── examples/
├── 01_add/ # Python→Julia for vector addition
├── 02_matmul/ # Python→Julia for matrix multiply
└── 03_softmax/ # Python→Julia for softmax (3 strategies)
仅 critical-rules.md 就记录了团队遇到的 17 个陷阱。表 2 详细列出了最常见的陷阱及其对应修复方法。
max(a, b) on tiles → IRErrorUse max.(a, b) (broadcast dot)2ct.load with order — index positions wrongorder remaps BOTH shape AND index此外还有一个静态验证脚本,可在 GPU 上运行之前捕获残留的 ct.bid(0)、kernel 内部的 for 循环以及 Python 风格类型名称等问题。具备这些内容后,模型不必每次都重新发现转换规则。它读取 skill,遵循检查清单,并应用规则。
TileGym 中的 AI agent skill
具体交付物是 TileGym 中 julia/ 目录下的一个 Julia 子项目,该项目是开源的:
julia/
├── Project.toml # Dependencies: CUDA.jl, cuTile.jl, NNlib.jl, Test
├── kernels/
│ ├── add.jl # 1D element-wise with alpha scaling
│ ├── matmul.jl # 2D tiled MMA with column-major layout
│ └── softmax.jl # 3 strategies: TMA, online, chunked
└── test/
├── runtests.jl # Test runner
├── test_add.jl
├── test_matmul.jl
└── test_softmax.jl
这三个 kernel 是有意选择的。Kernel add 是用于测试完整翻译面的最简单方法。Matmul 增加了循环结构、tensor core 和布局翻转。Softmax 引入了多遍算法,其中的不变量必须在翻译后仍然成立。每个 kernel 都有测试,会按各 dtype 容差与 CPU 参考实现进行比较,包括维度无法对齐到 tile 大小的边界情况。
结果与经验教训
有了 skill 后,每个 kernel 的工作流如下:
- 预检:扫描源代码,查找需要特殊处理的模式(for 循环、ct.mma、order= 等)。
- 转换:应用 API 映射和关键规则。
- 验证:运行静态检查器。
- 测试:针对参考实现运行 Julia 测试。
- 修复:如果出现失败,使用调试指南进行修复,然后重新运行。
对于一个具有代表性的通用矩阵乘法(GEMM)转换,在一个前沿 LLM 上,该过程耗时约 4 分钟,使用约 78K tokens,且没有人工干预。后续 kernel 更快,因为示例和规则已经在仓库中。
表 3 列出了移植过程中导致 bug 的陷阱,这些现在都已在 skill 中自动处理。
ct.bid(0) left unchangedWrong tile loaded, silent corruption0-based versus 1-based indexinga * b for element-wise multiplyMatrix multiply instead of element-wiseJulia * is matmul; need .*Accumulator shape (TM, TN)Wrong results in matmulColumn-major needs (TN, TM)ct.PaddingMode.ZEROUndefVarErrorJulia uses PascalCase: .Zero要点并不是 AI 编写了代码,而是能够把学到的东西捕获下来,使模型下次可以复用。一个提示词可以说:“注意索引。”一个 skill 可以说:“这里列出了会出错的 17 个具体事项、检查方法,以及一个能自动捕获它们的脚本。”
现在,未来的移植可以从一个已经拥有可工作示例、经过测试的 API 映射、静态验证器和调试指南的仓库开始。每一次所需工作量都会少于上一次。
更广泛的启示是,在系统工作中使用 AI 的挑战并不在于代码生成,而在于在编译器无法捕获语义错误的领域中生成正确代码。将领域规则与其描述的代码一起编码进版本控制,是应对这一问题的一种方式。
开始使用 agent skill 将 Python kernel 翻译为 Julia
使用以下代码尝试 Julia 子项目和转换 skill:
cd TileGym # Explore the Julia kernels ls julia/kernels/ # add.jl, matmul.jl, softmax.jl # Explore the conversion skill ls .claude/skills/converting-cutile-to-julia/ # Install Julia dependencies (requires Julia 1.12+, CUDA 13.1+ driver) julia --project=julia/ -e 'using Pkg; Pkg.instantiate()' # Run the Julia kernel tests julia --project=julia/ julia/test/runtests.jl
要求:
- Julia 1.12+ 和 NVIDIA CUDA 13.1+ driver
- NVIDIA Ampere、NVIDIA Ada 或 NVIDIA Blackwell GPU(compute capability 8.x、10.x、11.x、12.x)
- 具备文件系统访问权限的 LLM agent(例如 Claude Code)。若要将转换 skill 用于你自己的 kernel,请将 LLM agent 指向 .claude/skills/converting-cutile-to-julia/SKILL.md,提供一个 cuTile Python kernel 作为输入,然后开始将 Python kernel 翻译为 Julia。
标签
















