Option
HeimHeim Skill Entwicklertools tilegym-adding-cutile-kernel

tilegym-adding-cutile-kernel

NVIDIA/skills NVIDIA/skills

Füge einen neuen cuTile-GPU-Kernel-Operator zu TileGym hinzu, der die Registrierung für den Dispatch, die Backend-Implementierung, Exporte, Tests und Benchmarks umfasst.

...Alle erweitern
0
Zeit aktualisiert 27. September 2026

Hinzufügen eines cuTile-Kernels zu TileGym

End-to-End-Workflow zum Hinzufügen eines neuen Operators (z. B. my_op) mit cuTile-Backend.

Ausführungsregeln

Diese Regeln MÜSSEN strikt befolgt werden:

  1. Verwenden Sie „TodoWrite“, um die unten stehende Checkliste zu erstellen, BEVOR Sie Code schreiben
  2. Führen Sie die Schritte der Reihe nach aus – überspringen Sie KEINE Schritte und kombinieren Sie sie NICHT
  3. Markieren Sie jede Aufgabe nach Abschluss als „abgeschlossen“ und zu Beginn als „in Bearbeitung“
  4. Wenn ein Schritt nicht zutrifft (z. B. keine cuTile-Implementierung), markieren Sie ihn mit einem Hinweis als „abgeschlossen“ und überspringen Sie ihn NICHT stillschweigend
  5. Jeder Schritt MUSS zu einem Dateischreibvorgang oder einer expliziten Entscheidung zum Überspringen führen – keine stillschweigenden Auslassungen

Anweisungen

Diese Checkliste MUSS zu Beginn in „TodoWrite“ kopiert werden:

- [ ] Schritt 1: Dispatch-Schnittstelle in ops.py registrieren
- [ ] Schritt 2: cuTile-Backend implementieren
- [ ] Schritt 3: In __init__.py (cutile) registrieren
- [ ] Schritt 4: Tests hinzufügen
- [ ] Schritt 5: Benchmark zu tests/benchmark hinzufügen
- [ ] Schritt 6: Überprüfen (pytest + lint ausführen)

Schritt 1: Dispatch-Schnittstelle registrieren

Datei: src/tilegym/ops/ops.py

Fügen Sie eine @dispatch -Funktion hinzu – dies ist der einzige Einstiegspunkt für alle Backends.

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

    Argumente:
        input: Eingabetensor
        out: Optionaler, vorab zugewiesener Ausgabetensor
        **kwargs: Zusätzliche Argumente für backend-spezifische Konfigurationen

    Rückgabewert:
        torch.Tensor
    """
    raise NotImplementedError(f"my_op ist für {get_current_backend()} nicht implementiert")

Wichtige Regeln:

  • Der Funktionskörper löst nur einen NotImplementedError aus
  • Fügen Sie **kwargs für backend-spezifische Parameter ein

Referenz: Siehe vorhandene Operationen in src/tilegym/ops/ops.py (z. B. silu_and_mul, softmax)

Schritt 2: cuTile-Backend implementieren

Datei: src/tilegym/ops/cutile/my_op.py

Die Dateistruktur folgt dieser Vorlage:

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)
    # ... Berechnung ...
    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

Referenz: src/tilegym/ops/cutile/silu_and_mul.py

Schritt 3: Registrierung in __init__.py (WICHTIG)

Wird dieser Schritt übersprungen, wird die cuTile-Backend-Implementierung niemals geladen.

Datei: src/tilegym/ops/cutile/__init__.py

Fügen Sie innerhalb des Blocks `if is_backend_available("cutile")` Folgendes ein ( in alphabetischer Reihenfolge):

from . import my_op

Und im Abschnitt „import“ der Funktion:

from .my_op import my_op

Und fügen Sie „my_op“ zu __all__ hinzu.

Schritt 4: Tests hinzufügen

Datei: tests/ops/test_my_op.py

WICHTIG: Importiere immer aus `tilegym.ops`, NIEMALS aus `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):
        """Referenzimplementierung mit 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 nicht verfügbar")
        try:
            set_backend(backend)
        except Exception as e:
            pytest.skip(f"Backend wird nicht unterstützt: {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,
        )

Schlüsselmuster:

  • _backends = ["cutile"]
  • test_op: Verwende set_backend(backend) mit try-except, rufe self.setUp() auf

Referenz: tests/ops/test_silu_and_mul.py

Nachfolgend sind die häufigsten Fehler aufgeführt.

1. Fehlende _backends-Liste (innerhalb der Klasse)
2. test_op / test_op_xxx – fehlendes @pytest.mark.parametrize("backend", _backends), fehlender „backend“-Parameter sowie fehlendes Muster tilegym.is_backend_available / tilegym.set_backend

Schritt 5: Füge einen Benchmark zu „tests/benchmark“ hinzu

Datei: tests/benchmark/bench_my_op.py

Wichtige Regeln aus „benchmark_rules.md“:

  • Rufe die Operation über `tilegym.ops.my_op(a, b, ..., backend=backend) ` auf – verwende nicht `set_backend`.
  • Definieren Sie ALL_BACKENDS (einschließlich mindestens cutile und torch) und filtern Sie mit get_supported_backends().
  • Implementieren Sie `reference_my_op(...)` und registrieren Sie es: `register_impl("my_op", "torch")(reference_my_op)`.
  • Verwende create_benchmark_config(), um triton.testing.Benchmark -Konfigurationen zu erstellen (z. B. nach shape/dtype).
  • Verwenden Sie @triton.testing.perf_report([...]) auf bench_my_op(...); innerhalb der „bench“-Funktion: Korrektheitsprüfung mit `torch.testing.assert_close(fn(), ref(), ...)`, anschließend `ms = triton.testing.do_bench(fn) ` (oder `do_bench_cudagraph`), Berechnung von GB/s oder TFLOPS und Rückgabe der Metrik.
  • Einstiegspunkt: if __name__ == "__main__": bench_my_op.run(print_data=True).

Vorlagenstruktur:

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):
    """Referenzimplementierung mit 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"],  # oder andere Dimensionsnamen
        x_vals=[...],
        line_arg="backend",
        line_vals=list(backends),
        line_names=list(names),
        styles=list(styles),
        ylabel="GB/s",  # oder 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)  # oder do_bench_cudagraph(fn)
    # Berechne die Metrik (z. B. GB/s oder TFLOPS) aus ms und der Problemgröße
    return metric

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

Namen für Benchmark-Diagramme: Müssen die Endung -TFLOPS oder -GBps enthalten

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

Schritt 6: Überprüfen

# Tests ausführen
pytest tests/ops/test_my_op.py -v

# Benchmark ausführen (optional)
python tests/benchmark/bench_my_op.py

# Lint
pre-commit run -a
Auf GitHub ansehen
---
name: tilegym-adding-cutile-kernel
description: Add a new cuTile GPU kernel operator to TileGym, covering dispatch registration, backend implementation, exports, tests, and benchmarks.
license: CC-BY-4.0 AND Apache-2.0
---

# Adding 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:**
1. Use TodoWrite to create the checklist below BEFORE writing any code
2. Execute steps **in order** — do NOT skip ahead or combine steps
3. Mark each todo as `completed` after finishing, `in_progress` when starting
4. If a step is not applicable (e.g., no cuTile impl), mark it `completed` with a note, do NOT silently skip
5. 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.

```python
@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 `**kwargs` for 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:

```python
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):

```python
from . import my_op
```

And in the function import section:

```python
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`.

```python
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`: use `set_backend(backend)` with try-except, call `self.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** use `set_backend`.
- Define `ALL_BACKENDS` (include at least `cutile` and `torch`), filter with `get_supported_backends()`.
- Implement `reference_my_op(...)` and register it: `register_impl("my_op", "torch")(reference_my_op)`.
- Use `create_benchmark_config()` to build `triton.testing.Benchmark` configs (e.g. by shape/dtype).
- Use `@triton.testing.perf_report([...])` on `bench_my_op(...)`; inside the bench function: correctness check with `torch.testing.assert_close(fn(), ref(), ...)`, then `ms = triton.testing.do_bench(fn)` (or `do_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:

```python
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

```bash
# 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
```

tilegym-adding-cutile-kernel installieren

Laden Sie die Skill-Dateien herunter und entpacken Sie sie in Ihr Verzeichnis „.claude/skills/“.

ZIP herunterladen

Klonen Sie das Repository und kopieren Sie die Skill-Dateien in Ihr Projekt.

git clone https://github.com/NVIDIA/skills/tree/main/skills/tilegym-adding-cutile-kernel # Copy SKILL.md to your .claude/skills/ directory

Kopieren Kopieren
Schnelle Einrichtung: Kopiere den Skill-Ordner nach .claude/skills/ Claude erkennt den Skill automatisch und nutzt ihn.
Repository NVIDIA/skills

Ähnliche Skills

algorithmic-art
Zeit aktualisiert 27. August 2026
systematic-debugging
Zeit aktualisiert 3. September 2026
tech-debt-tracker
Zeit aktualisiert 29. August 2026
continual-learning
Zeit aktualisiert 10. September 2026
OR