tilegym-adding-cutile-kernel について
TileGym に cuTile オペレーターを追加する開発スキルです。ディスパッチ登録からバックエンド実装、テスト、ベンチマーク、検証まで順に案内します。
主な機能
- 01ディスパッチインターフェースの登録
- 02cuTile バックエンドの実装と登録
- 03オペレーターテストの追加
- 04性能ベンチマークの作成
- 05テストと lint の実行
活用シーン
- 01TileGym への新しいオペレーターの追加
- 02cuTile 実装のテストとベンチマーク
- 03オペレーター統合の検証
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ promptingcompany/ nv-skills --skill tilegym-adding-cutile-kernelインストール後、Claude Code で/tilegym-adding-cutile-kernelを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
BENCHMARK.mdskill-card.mdskill.oms.sigevals/evals.json詳細
TileGym に cuTile カーネルを追加する
cuTile バックエンドを使って新しい演算子(例:my_op)を追加するためのエンドツーエンドのワークフローです。
実行ルール
以下のルールに必ず従ってください:
- コードを書く前に、以下のチェックリストを TodoWrite で作成します
- 手順を順番どおりに実行します。先に進んだり、手順をまとめたりしないでください
- 各 TODO は完了後に
completed、開始時にin_progressとしてマークします - 手順が該当しない場合(例:cuTile の実装がない場合)は、黙って省略せず、注記を添えて
completedとしてマークします - 各手順では、必ずファイルへの書き込み、または明示的なスキップの判断を行います。黙って省略してはいけません
手順
開始時に、このチェックリストを必ず 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
さらに、__all__ に "my_op" を追加します。
手順 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