
Adding cuTile Kernel to TileGym
OfficialFreeStreamline the addition of cuTile operators in TileGym.
Free · Opens the source repo
What Adding cuTile Kernel to TileGym does
The Adding cuTile Kernel to TileGym skill provides a structured workflow for developers looking to integrate new cuTile GPU kernel operators into the TileGym framework. This skill guides users through the necessary steps to register a new operator, implement its backend, and ensure it is properly tested and benchmarked. It is particularly useful for developers involved in GPU programming and those who need to extend the functionality of TileGym with custom operations.
To begin, users must follow a strict checklist that outlines the steps required to add a new operator. This includes registering the dispatch interface in the ops.py file, implementing the cuTile backend in a designated Python file, and ensuring that the operator is properly registered in the __init__.py file. Each step is clearly defined, minimizing the risk of errors during the integration process.
Furthermore, the skill emphasizes the importance of testing and benchmarking the new operator. It provides templates and guidelines for creating tests that validate the correctness of the implementation and benchmarks that measure performance. This structured approach not only enhances the reliability of the new operator but also contributes to the overall performance of the TileGym framework.
This skill is ideal for developers and engineers working with GPU computing who want to extend TileGym's capabilities by adding new cuTile operators. By following the detailed instructions and adhering to the execution rules, users can efficiently implement and validate their custom kernels, ensuring they meet the performance and correctness standards required for production use.
When to use it
Use this skill when you need to add or implement a new cuTile operator in TileGym, ensuring a structured approach to the integration process.
When not to use it
This skill is not suitable for users who are not working with cuTile or TileGym, or for those who require a more general-purpose GPU programming solution.
What you can build with it
Adding a New cuTile Operator
A developer wants to extend TileGym's functionality by adding a new operator. They follow the skill's checklist to ensure proper registration and implementation.
Benchmarking GPU Performance
After implementing a new cuTile operator, a developer uses the skill to create benchmarks that measure its performance against existing operators.
Testing New Implementations
A team of engineers uses this skill to set up tests for their newly added cuTile operators, ensuring correctness and performance before deployment.
How to install Adding cuTile Kernel to TileGym
View source1. Install with the skills CLI
npx skills add nvidia/skills/tilegym-adding-cutile-kernel --agent claude-code2. Or install it manually
Download the skill folder and drop it into ~/.claude/skills/ for all projects, or .claude/skills/ to scope it to one repo. Restart Claude Code so it picks up the new skill.
Anthropic's agentic coding CLI, and the reference implementation of Agent Skills. Drop a skill folder into ~/.claude/skills and Claude Code loads it automatically whenever a task matches the skill's description. Claude Code docs
Inside SKILL.md
Written by nvidiaAdding a cuTile Kernel to TileGym
End-to-end workflow for adding a new operator (e.g., my_op) with cuTile backend.
Execution Rules
MUST follow these rules strictly:
- Use TodoWrite to create the checklist below BEFORE writing any code
- Execute steps in order — do NOT skip ahead or combine steps
- Mark each todo as
completedafter finishing,in_progresswhen starting - If a step is not applicable (e.g., no cuTile impl), mark it
completedwith a note, do NOT silently skip - Each step MUST result in a file write or explicit skip decision — no silent omissions
Instructions
MUST copy this checklist to TodoWrite at the start:
- [ ] 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)
Step 1: Register dispatch interface
File: src/tilegym/ops/ops.py
Add a @dispatch function — this is the single entry point for all backends.
@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()}")
Key rules:
- Function body only raises
NotImplementedError - Include
**kwargsfor backend-specific parameters
Reference: See existing ops in src/tilegym/ops/ops.py (e.g., silu_and_mul, softmax)
Step 2: Implement cuTile backend
File: src/tilegym/ops/cutile/my_op.py
The file structure follows this template:
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
Reference: src/tilegym/ops/cutile/silu_and_mul.py
Step 3: Register in __init__.py (CRITICAL)
Missing this step means the cuTile backend implementation never gets loaded.
File: src/tilegym/ops/cutile/__init__.py
Add inside if is_backend_available("cutile"): block (alphabetically):
from . import my_op
And in the function import section:
from .my_op import my_op
And add "my_op" to __all__.
Step 4: Add tests
File: tests/ops/test_my_op.py
CRITICAL: Always import from tilegym.ops, NEVER from 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,
)
Key patterns:
_backends = ["cutile"]test_op: useset_backend(backend)with try-except, callself.setUp()
Reference: tests/ops/test_silu_and_mul.py
Below is the common errors.
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
Step 5: Add benchmark to tests/benchmark
File: tests/benchmark/bench_my_op.py
Key rules from benchmark_rules.md:
- Call the op via
tilegym.ops.my_op(a, b, ..., backend=backend)— do not useset_backend. - Define
ALL_BACKENDS(include at leastcutileandtorch), filter withget_supported_backends(). - Implement
reference_my_op(...)and register it:register_impl("my_op", "torch")(reference_my_op). - Use
create_benchmark_config()to buildtriton.testing.Benchmarkconfigs (e.g. by shape/dtype). - Use
@triton.testing.perf_report([...])onbench_my_op(...); inside the bench function: correctness check withtorch.testing.assert_close(fn(), ref(), ...), thenms = triton.testing.do_bench(fn)(ordo_bench_cudagraph), compute GB/s or TFLOPS, and return the metric. - Entry point:
if __name__ == "__main__": bench_my_op.run(print_data=True).
Template structure:
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)
Benchmark Plot Names: Must include -TFLOPS or -GBps suffix
- Example:
plot_name=f"persistent-layer-norm-M{num_rows}-{dtype_name}-GBps"
Step 6: Verify
# 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
Frequently asked questions about Adding cuTile Kernel to TileGym
Similar skills
Python PyPI Package Builder
Streamline the process of creating and publishing Python packages.
Minecraft Plugin Development
Streamline your Minecraft server plugin creation.
MCP Server Builder
Easily build .NET MCP servers with the latest standards.
CommunityToolkit.Mvvm Messenger
Decoupled communication for ViewModels in .NET applications.
MVVM Toolkit DI
Streamline ViewModel integration with Dependency Injection in .NET.
MCP Apps Builder
Essential guidelines for MCP server development.
