mcpField
T
tilegym-adding-cutile-kernel

tilegym-adding-cutile-kernel

github.com/nvidia开发3.8k1.8k 次安装今天更新
代码仓库

关于 tilegym-adding-cutile-kernel

面向 TileGym 开发者的流程技能,指导添加 cuTile 算子,涵盖从注册分发接口到测试、基准测试和验证的步骤。

主要功能

  1. 01按序执行的集成清单
  2. 02注册算子分发接口
  3. 03实现并注册 cuTile 后端
  4. 04测试与基准测试指南
  5. 05运行 pytest 和 lint 验证

适用场景

  1. 01为 TileGym 添加 cuTile 算子
  2. 02测试并基准评估新算子
  3. 03提交前检查算子实现

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/nvidia/skills --skill tilegym-adding-cutile-kernel

安装后在 Claude Code 中输入/tilegym-adding-cutile-kernel确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

BENCHMARK.md
skill-card.md
skill.oms.sig
evals/
evals.json

详细介绍

在 TileGym 中添加 cuTile 内核

使用 cuTile 后端添加新算子(例如 my_op)的端到端工作流。

执行规则

必须严格遵循以下规则:

  1. 编写任何代码之前,使用 TodoWrite 创建下方清单
  2. 按顺序执行步骤——不得跳步或合并步骤
  3. 每完成一项待办后,将其标记为 completed;开始执行时标记为 in_progress
  4. 如果某个步骤不适用(例如没有 cuTile 实现),请附注并将其标记为 completed,不得悄悄跳过
  5. 每个步骤都必须写入文件或明确决定跳过,不得默默遗漏

说明

开始时必须将此清单复制到 TodoWrite:

- [ ] Step 1: Register dispatch interface in ops.py
- [ ] Step 2: Implement cuTile backend
- [ ] Step 3: Register in __init__.py (cutile)
- [ ] Step 4: Add tests
- [ ] Step 5: Add benchmark to tests/benchmark
- [ ] Step 6: Verify (run pytest + lint)

步骤 1:注册分发接口

文件:src/tilegym/ops/ops.py

添加一个 @dispatch 函数——这是所有后端的唯一入口点。

@dispatch(
    "my_op",
)
def my_op(
    input: torch.Tensor,
    out: Optional[torch.Tensor] = None,
    **kwargs: Any,
):
    """
    Description of my_op.

    Args:
        input: Input tensor
        out: Optional preallocated output tensor
        **kwargs: Additional arguments for backend-specific configurations

    Returns:
        torch.Tensor
    """
    raise NotImplementedError(f"my_op is not implemented for {get_current_backend()}")

关键规则:

  • 函数体只能抛出 NotImplementedError
  • 使用 **kwargs 接收特定于后端的参数

参考:参阅 src/tilegym/ops/ops.py 中现有的算子(例如 silu_and_mul、softmax)

步骤 2:实现 cuTile 后端

文件:src/tilegym/ops/cutile/my_op.py

文件结构遵循以下模板:

import torch
import cuda.tile as ct

from tilegym.backend import register_impl


@ct.kernel
def my_op_kernel_ct(x, output, n_elements: ct.Constant[int], BLOCK_SIZE: ct.Constant[int]):
    bid = ct.bid(0)
    indices = bid * BLOCK_SIZE + ct.arange(0, BLOCK_SIZE)
    x_val = ct.gather(x, indices)
    # ... compute ...
    ct.scatter(output, indices, result)


@register_impl("my_op", backend="cutile")
def my_op(input: torch.Tensor, out: torch.Tensor = None, **kwargs) -> torch.Tensor:
    n = input.numel()
    if out is None:
        out = torch.empty_like(input)
    grid = ((n + 1023) // 1024,)
    ct.launch(stream, grid, kernel, (some args, ...))
    return out

参考:src/tilegym/ops/cutile/silu_and_mul.py

步骤 3:在 __init__.py 中注册(关键)

缺少此步骤会导致 cuTile 后端实现永远不会被加载。

文件:src/tilegym/ops/cutile/__init__.py

在 if is_backend_available("cutile"): 代码块中添加(按字母顺序排列):

from . import my_op

并在函数导入部分添加:

from .my_op import my_op

并将 "my_op" 添加到 __all__。

步骤 4:添加测试

文件:tests/ops/test_my_op.py

关键:始终从 tilegym.ops 导入,切勿从 tilegym.ops.cutile.my_op 导入。

import pytest
import torch

from tilegym.backend import is_backend_available, set_backend
from .. import common

_backends = ["cutile"]


class Test_MY_OP(common.PyTestCase):
    @staticmethod
    def reference(input):
        """Reference implementation using PyTorch."""
        return torch.some_reference(input)

    @pytest.mark.parametrize("shape, dtype", [
        ((1024,), torch.float16),
        ((1024, 512), torch.float32),
        ((64, 64, 64), torch.bfloat16),
    ])
    @pytest.mark.parametrize("backend", _backends)
    def test_op(self, shape, dtype, backend, arch):
        if backend == "cutile" and not is_backend_available("cutile"):
            pytest.skip("Cutile backend not available")
        try:
            set_backend(backend)
        except Exception as e:
            pytest.skip(f"Backend is not supported: {e}")

        self.setUp()

        from tilegym.ops import my_op

        A = torch.randn(*shape, dtype=dtype, device="cuda")
        self.assertCorrectness(
            my_op, self.reference, {"input": A},
            atol=1e-3, rtol=1e-3,
        )

关键模式:

  • _backends = ["cutile"]
  • test_op:使用 try-except 调用 set_backend(backend),并调用 self.setUp()

参考:tests/ops/test_silu_and_mul.py

以下是常见错误。

1. Missing _backends list (inside class)
2. test_op / test_op_xxx — missing @pytest.mark.parametrize("backend", _backends), backend parameter, and tilegym.is_backend_available / tilegym.set_backend pattern

步骤 5:在 tests/benchmark 中添加基准测试

文件:tests/benchmark/bench_my_op.py

benchmark_rules.md 中的关键规则:

  • 通过 tilegym.ops.my_op(a, b, ..., backend=backend) 调用算子——不要使用 set_backend。
  • 定义 ALL_BACKENDS(至少包含 cutile 和 torch),并使用 get_supported_backends() 进行筛选。
  • 实现 reference_my_op(...),并注册:register_impl("my_op", "torch")(reference_my_op)。
  • 使用 create_benchmark_config() 构建 triton.testing.Benchmark 配置(例如按形状/数据类型配置)。
  • 在 bench_my_op(...) 上使用 @triton.testing.perf_report([...]);在基准测试函数内部,先用 torch.testing.assert_close(fn(), ref(), ...) 检查正确性,然后调用 ms = triton.testing.do_bench(fn)(或 do_bench_cudagraph),计算 GB/s 或 TFLOPS,并返回该指标。
  • 入口:if __name__ == "__main__": bench_my_op.run(print_data=True)。

模板结构:

import torch
import triton
import triton.testing

import tilegym
from tilegym.backend import is_backend_available, register_impl

ALL_BACKENDS = [
    ("cutile", "cuTile", ("orange", "-")) if is_backend_available("cutile") else None,
    ("torch", "PyTorch", ("green", "-")),
]

def get_supported_backends():
    return [p for p in ALL_BACKENDS if p is not None]

def reference_my_op(input: torch.Tensor, out: torch.Tensor = None, **kwargs):
    """Reference implementation using PyTorch."""
    ...

register_impl("my_op", "torch")(reference_my_op)

def create_benchmark_config(datatype, ...):
    available_backends = get_supported_backends()
    if not available_backends:
        return None
    backends, names, styles = zip(*available_backends)
    return triton.testing.Benchmark(
        x_names=["M"],  # or other dimension names
        x_vals=[...],
        line_arg="backend",
        line_vals=list(backends),
        line_names=list(names),
        styles=list(styles),
        ylabel="GB/s",  # or TFLOPS
        plot_name="my-op-...",
        args={"datatype": datatype, ...},
    )

@triton.testing.perf_report([
    create_benchmark_config(datatype, ...)
    for datatype in [torch.float16, torch.float32]
    for ... in [...]
])
def bench_my_op(M, backend, datatype, ..., device="cuda"):
    x = torch.randn(..., dtype=datatype, device=device)

    fn = lambda: tilegym.ops.my_op(x, backend=backend)
    ref = lambda: reference_my_op(x)
    torch.testing.assert_close(fn(), ref(), rtol=1e-2, atol=1e-2)

    ms = triton.testing.do_bench(fn)  # or do_bench_cudagraph(fn)
    # Compute metric (e.g. GB/s or TFLOPS) from ms and problem size
    return metric

if __name__ == "__main__":
    bench_my_op.run(print_data=True)

基准测试图表名称:必须包含 -TFLOPS 或 -GBps 后缀

  • 示例:plot_name=f"persistent-layer-norm-M{num_rows}-{dtype_name}-GBps"

步骤 6:验证

# Run tests
pytest tests/ops/test_my_op.py -v

# Run benchmark (optional)
python tests/benchmark/bench_my_op.py

# Lint
pre-commit run -a

评论

支持 Markdown