tilegym-cutile-python

Par nvidia · skills

Assistant expert en programmation cuTile. Rédige des kernels GPU haute performance en utilisant le modèle de programmation basé sur les tuiles de cuTile, avec validation et optimisation appropriées. Prend en charge l'orchestration d'agents avancée pour les tâches multi-kernels complexes.

npx skills add https://github.com/nvidia/skills --skill tilegym-cutile-python

Skill de programmation cuTile en Python

Vous êtes un expert en programmation cuTile, spécialisé dans l'écriture de kernels GPU haute performance utilisant le modèle de programmation basé sur des tuiles de cuTile. Cette skill fournit une guidance complète pour créer, déboguer et optimiser les kernels cuTile.

Overview

cuTile est un modèle de programmation parallèle pour les GPUs NVIDIA avec un DSL basé sur Python qui exploite automatiquement les capacités matérielles avancées comme les tensor cores. Cette skill vous aide à écrire du code cuTile efficace et correct.

Quand utiliser cette skill

Invoquez cette skill quand vous avez besoin de :

  • Écrire des kernels GPU cuTile à partir de zéro
  • Convertir des opérations tensorielles en implémentations cuTile
  • Déboguer ou corriger du code kernel cuTile
  • Optimiser les kernels cuTile pour la performance
  • Comprendre l'API cuTile et les patterns de programmation
  • Valider les implémentations cuTile
  • Trouver et adapter des exemples à partir des sources de référence disponibles

Spécifiez optionnellement lors de l'invocation :

  • Les formes de tenseurs cibles
  • Les types de données (défaut : float16)
  • Les exigences de performance
  • Toute contrainte spéciale

Documentation de référence

Spécification du langage cuTilehttps://docs.nvidia.com/cuda/cutile-python. Couvre le modèle d'exécution, les modèles de données et de mémoire, le débogage, la compilation, et toutes les ops publiques (load/store, factories, reductions, scans, matmul, selection, math, bitwise, comparisons, atomics, metaprogramming, classes, enums, autotuning).

Directives d'implémentation (dans le répertoire guidelines/) :

Exemples

Avant de démarrer tout travail de programmation cuTile, recherchez toujours les exemples existants en premier. TileGym est la référence principale ; le répertoire examples/ packagé le complète pour les ops que TileGym ne couvre pas encore (convolution, pooling, scan, GEMV, matmul 4D, split-k GEMM, group_norm).

La skill supporte deux contextes d'installation :

  • À l'intérieur d'une copie locale de TileGym (<repo>/skills/tilegym-cutile-python/, ou <repo>/.agents/skills/tilegym-cutile-python/ / <repo>/.claude/skills/tilegym-cutile-python/ via les liens symboliques de compatibilité rétro) — Les ops TileGym sont à <repo>/src/tilegym/ops/cutile/.
  • Installée ailleurs (ex. ~/.agents/skills/tilegym-cutile-python/, ~/.claude/skills/tilegym-cutile-python/, ou dans un dépôt différent) — clonez TileGym une fois dans ${TILEGYM_SKILL_CACHE_DIR:-~/.cache/tilegym}/TileGym et utilisez src/tilegym/ops/cutile/.

Consultez examples/tilegym_and_examples_guide.md pour l'ordre de recherche complet, la disposition des répertoires, et la procédure de décision cache-vs-repo.

Quand clarifier avant l'implémentation

Pour les tâches complexes ou ambiguës, présentez les options d'approche à l'utilisateur avant de coder. Cela évite les efforts gaspillés sur une mauvaise implémentation.

Clarifier pour ces types de tâches

Type de tâche Pourquoi clarifier Questions exemple
Demandes d'optimisation « Rends ceci plus rapide » a plusieurs chemins Quel goulot d'étranglement ? Limité par la mémoire ou le calcul ? Speedup cible ?
Changements d'architecture Les décisions structurelles affectent tout Parallélisme de données vs parallélisme de modèle ? Kernel persistant vs standard ?
Opérations ambiguës Même nom, différentes implémentations Flash attention vs standard ? Causal vs bidirectionnel ? Conv groupée vs depthwise ?
Tradeoffs performance vs correctness L'utilisateur doit choisir Utiliser TF32 pour la vitesse ? Math approximatif ? Accumulation en précision réduite ?
Contraintes manquantes Impossible d'optimiser sans cibles Formes de tenseurs cibles ? Plage de taille de batch ? Budget mémoire ?

Agir directement pour ces types de tâches

  • Demandes claires et spécifiques : « Écrire un kernel ReLU pour la forme (1024, 1024) »
  • Corrections de bugs avec reproduction : « Ce kernel plante à la ligne 42 »
  • Questions sur l'API : « Comment utiliser ct.gather ? »
  • Adaptations d'exemples : « Adapter le softmax TileGym pour mes formes »

Comment clarifier

Quand une clarification est nécessaire :

  1. Expliquez brièvement pourquoi plusieurs approches existent
  2. Présentez 2-3 options concrètes avec leurs tradeoffs
  3. Recommandez une option s'il y a un choix clairement meilleur
  4. Demandez à l'utilisateur de choisir avant de continuer

Exemple :

Votre demande « optimiser ce matmul » pourrait aller dans plusieurs directions :

1. **Kernel persistant** - Meilleur pour les petites matrices, plus rapide, code plus complexe
2. **Tuning de taille de tuile** - Gains modérés, changements de code minimaux
3. **Prefetching TMA** - Meilleur pour les grandes matrices, nécessite GPU Hopper+

Je recommande l'option 2 pour une première passe. Quelle approche préférez-vous ?

Évaluation de complexité : workflow simple vs orchestré

Avant de commencer l'implémentation, évaluez la complexité de la demande pour choisir le bon workflow.

Utiliser le workflow simple (étapes 0-6 ci-dessous) quand :

  • Tâche à kernel unique (ex. ReLU, softmax, un matmul)
  • Correction de bug ou optimisation d'un kernel existant
  • Question sur l'API ou adaptation d'exemple
  • Demande claire d'opération unique

Utiliser le workflow d'orchestration d'agents profonds quand L'UN de ces points s'applique :

  • 3+ opérations distinctes nécessitant des kernels séparés (ex. « implémenter un bloc transformer avec attention, FFN, et layer norm »)
  • Multiples fonctions définies par l'utilisateur dans le code d'entrée (ex. custom_activation(), custom_norm())
  • Dépendances de données inter-kernels où la sortie d'un kernel alimente un autre
  • nn.Module PyTorch avec multiples couches dans forward()
  • Demande de décomposition explicite (ex. « diviser ceci en kernels fusionnés »)

Quand l'orchestration est nécessaire, suivez la section Deep Agent Orchestration Workflow. Sinon, continuez avec les Instructions ci-dessous.

Deep Agent Orchestration Workflow

Pour les tâches complexes nécessitant 3+ kernels, dépendances inter-kernels, ou décomposition multi-couches de nn.Module, utilisez le pipeline orchestré multi-agents. L'agent principal agit en tant qu'orchestrateur (pas un codeur) — les sous-agents gèrent la lecture de références et la génération de code.

Pipeline : Op Tracer (optionnel) → Analyzer → Kernel Agents (parallèle) → Composer → Main Agent valide

Pour le workflow complet étape par étape (étapes O-0 à O-4), les templates de prompt, et la gestion d'erreurs, consultez orchestration/workflow.md.

Pour l'architecture d'orchestration, la hiérarchie d'agents, et le format de spécification de kernel, consultez orchestration/overview.md.


Instructions

Suivez ces étapes lors de l'écriture de kernels cuTile (workflow simple pour les tâches à kernel unique).

REMARQUE : Ignorer cette section entière si vous utilisez le Deep Agent Orchestration Workflow ci-dessus. Le workflow d'orchestration a ses propres étapes (O-0 à O-4). NE PAS combiner les deux workflows - cela mène à l'agent principal lisant tous les fichiers de référence ET spawning des sous-agents, ce qui gaspille du contexte.

Étape 0 : Rechercher des exemples et consulter les références (OBLIGATOIRE)

Objectif : Trouver les exemples existants et consulter la documentation pertinente

Recherche d'exemples (stratégie en deux étapes) :

  1. Rechercher d'abord dans TileGym (src/tilegym/ops/cutile/) pour des patterns de kernels cuTile similaires.
  2. Si TileGym n'a pas de correspondance, chercher dans le répertoire examples/ packageé (partie de cette skill).
  3. Lire les fichiers d'exemples pertinents pour comprendre les patterns d'implémentation.

Traduction d'algorithmes complexes (flash attention, ops fusionnées, etc.) : Lors de l'implémentation d'algorithmes complexes, suivez cette approche systématique :

  1. Analyser l'implémentation PyTorch : Comprendre les opérations mathématiques, le flux de données, les patterns de calcul clés, les patterns d'accès mémoire, et toute optimisation ou contrainte spéciale.
  2. Étudier les exemples cuTile pertinents : Consulter les exemples pour des opérations similaires — les exemples existants fournissent souvent exactement les patterns dont vous avez besoin. Copier et adapter les patterns fonctionnels plutôt que de réinventer la roue.
  3. Implémenter la version cuTile : Mapper les opérations PyTorch aux primitives cuTile, appliquer la fusion de kernels où approprié, assurer l'indexage de tuiles correct et la gestion de mémoire, et valider contre la référence PyTorch.

Documentation de référence :

Étape 1 : Comprendre le problème

Objectif : Définir clairement ce que le kernel doit calculer

  • Identifier les tenseurs d'entrée/sortie et leurs formes/dtypes
  • Comprendre les opérations mathématiques requises
  • Déterminer les dépendances de données et le flux de calcul
  • Analyser les patterns d'accès mémoire pour les opportunités d'optimisation

Travailler avec les implémentations de référence fournies par l'utilisateur :

  1. Préserver le code de référence : Garder l'implémentation PyTorch originale intacte. Supprimer uniquement le code clairement redondant ou inutile.
  2. Approche conservative : Ne pas modifier ou réécrire l'implémentation de référence sauf si explicitement requis. La référence sert de vérité terre pour la validation de correctness.
  3. Chercher une clarification : Si vous êtes incertain sur la correctness ou l'intention de toute partie du code de référence, demander une clarification à l'utilisateur avant de continuer.
  4. Maintenir la fonctionnalité : Tout changement au code de référence doit préserver la fonctionnalité et le comportement originaux.

Étape 2 : Concevoir l'architecture du kernel

Objectif : Planifier la structure du kernel

  • Déterminer les tailles de bloc/tuile optimales pour la parallélisation (considérer les multiples de 32)
  • Calculer les dimensions de grille basé sur les tailles de tenseurs en utilisant ct.cdiv(size, block)
  • Concevoir une stratégie d'indexage de bloc en utilisant ct.bid()
  • Gérer les cas limites où la taille du tenseur n'est pas divisible par la taille du bloc

Étape 3 : Préparer le système de types et les constantes

Objectif : Assurer les annotations de type appropriées

  • Identifier toutes les valeurs constantes qui nécessitent des annotations de type
  • Ajouter les annotations de type appropriées en utilisant ct.Constant[type] pour toutes les constantes
  • Choisir les dtypes cuTile appropriés (ct.float32, ct.float16, ct.int32, etc.)
  • Assurer que les tailles de bloc et autres paramètres sont correctement typés

Étape 4 : Implémenter le kernel

Objectif : Écrire la fonction kernel cuTile

  • Créer une fonction kernel décorée avec @ct.kernel avec signature appropriée
  • Ajouter les paramètres requis (tenseurs d'entrée, tenseur de sortie, constantes typées)
  • Implémenter l'indexage de bloc avec des appels ct.bid() appropriés
  • Utiliser ct.load() pour l'accès aux tenseurs d'entrée avec indexation appropriée et formes de tuiles
  • Effectuer les opérations sur les tuiles chargées en utilisant les opérations de tuile cuTile
  • Utiliser ct.store() pour l'écriture de tenseur de sortie avec l'indexation correcte

Étape 5 : Préparer et lancer

Objectif : Configurer les entrées de tenseurs et lancer le kernel

  • Assurer que tous les tenseurs d'entrée sont sur le device CUDA en utilisant .cuda() ou .to("cuda")
  • Vérifier que les dtypes de tenseurs sont compatibles avec cuTile
  • Gérer les exigences de contigüité de tenseurs en utilisant .contiguous() si nécessaire
  • Lancer le kernel avec les dimensions de grille appropriées

Étape 6 : Valider et tester

Objectif : Assurer la correctness

  • Vérifier que le kernel compile sans erreurs
  • Tester avec différentes tailles de tenseurs (alignées et non alignées à la taille de tuile)
  • Valider les résultats contre l'implémentation de référence si disponible
  • Vérifier les conditions limites et les cas limites

Boucle de validation (OBLIGATOIRE)

IMPORTANT : Après avoir généré du code cuTile, vous DEVEZ l'exécuter pour vérifier la correctness. Ne pas juste écrire le fichier - l'exécuter et corriger tout problème.

Workflow de validation

┌─────────────────────────────────────────────────────────────┐
│  1. Générer du code                                         │
│     - Écrire le kernel cuTile avec validation inline        │
│       vers un fichier                                       │
│                                                             │
│  2. Exécuter le code                                        │
│     - Exécuter : python <filename>.py                       │
│                                                             │
│  3. Vérifier les résultats                                  │
│     ├─ Erreur de compilation ? → Corriger syntaxe/types    │
│     │  → Réessayer                                          │
│     ├─ Erreur runtime ? → Corriger logique kernel           │
│     │  → Réessayer                                          │
│     ├─ Validation FAIL ? → Corriger problèmes numériques    │
│     │  → Réessayer                                          │
│     └─ Validation PASS ? → Terminé ✓                        │
└─────────────────────────────────────────────────────────────┘

Étapes d'exécution

  1. Écrire le code généré vers un fichier .py
  2. Exécuter le fichier en utilisant Bash : python <filename>.py
  3. Analyser la sortie :
    • Si erreur de compilation : Lire le message d'erreur, corriger le code (vérifier les annotations de type, la syntaxe, l'utilisation de l'API)
    • Si erreur runtime : Vérifier les formes de tenseurs, les dimensions de grille, les patterns d'accès mémoire
    • Si validation FAIL : Vérifier les différences numériques, les tolerances, la correctness d'algorithme
    • Si validation PASS : Signaler le succès à l'utilisateur
  4. Itérer jusqu'à PASS : Corriger les problèmes et réexécuter jusqu'à ce que la validation passe (max 3 tentatives)

Bonnes pratiques de sortie de validation

  • Ne pas afficher de grands tenseurs - Imprimer uniquement le contenu des tenseurs quand la validation échoue
  • Imprimer des statistiques de synthèse - Afficher PASS/FAIL, différence max, forme de tenseur
  • Exemple de pattern de validation :
    is_close = torch.allclose(cutile_output, reference_output, atol=1e-3, rtol=1e-3)
    if is_close:
        print("✓ Validation PASSED")
    else:
        max_diff = (cutile_output - reference_output).abs().max().item()
        print(f"✗ Validation FAILED - max diff: {max_diff}")
        print(f"  Expected: {reference_output}")
        print(f"  Got:      {cutile_output}")

Problèmes courants et corrections

Type d'erreur Cause typique Correction
TypeError: missing Constant annotation ct.Constant[int] manquant Ajouter annotation de type à toutes les constantes
ValueError: tile dimension not power of 2 Taille de tuile non puissance de 2 Utiliser 2**((size-1).bit_length())
IndexError / CUDA error Dimensions de grille ou indices incorrects Vérifier utilisation de ct.cdiv, indices de tuile vs élément
Validation FAIL: max diff = X Mismatch numérique Vérifier algorithme, augmenter tolerance, ou corriger logique

Valeurs de tolerance par défaut

Consultez guidelines/03_concepts.md → « Default Rules When User Does Not Specify » pour les valeurs de tolerance, les dtypes par défaut, et les formes de tenseurs par défaut.

Checklist de test

  • ✓ Vérifier que la sortie cuTile correspond à l'implémentation de référence dans la tolerance
  • ✓ Tester avec différentes tailles de tenseurs (alignées et non alignées à la taille de tuile)
  • ✓ Tester les conditions limites et les cas limites
  • ✓ Assurer que tous les tenseurs sont sur le device CUDA avant le lancement du kernel
  • ✓ Vérifier la cohérence dtype sur les entrées et sorties

Exigences critiques

Quatre exigences essentielles pour tous les kernels cuTile :

  1. Forward path pur cuTile : Chaque op compute dans forward()/composed_function() doit passer par @ct.kernel + ct.launch. Ne pas appeler nn.Conv2d()(x), F.conv2d(x, w), F.linear(x, w), ou toute autre op compute nn.*/F.* en tant qu'opération runtime dans le forward path.
    • Permis dans forward() : torch.empty, torch.zeros, torch.ones (allocation) ; tensor.reshape, tensor.view, tensor.permute, tensor.contiguous (réarrangement) ; torch.cat, torch.stack (concaténation) ; torch.sqrt, .sum(), .mean() (ops scalaires simples entre lancements de kernels).
    • Permis dans __init__() : Utiliser nn.Conv2d, nn.Linear, etc. uniquement pour l'initialisation et le stockage de poids est fine — tant que forward() extrait les poids (ex. self.conv.weight.data) et les passe à ct.launch au lieu d'appeler self.conv(x).
    • Voir Rule 15 et Rule 17 dans guidelines/02_code_generation_rules.md pour les violations courantes et les exemples détaillés.
  2. Indices de tuile, pas indices d'élément : ct.load(A, index=(bid_m, k), shape=(BLOCK_M, K)) ✅ pas (bid_m * BLOCK_M, k)
  3. Toutes les dimensions de tuile doivent être des puissances de 2 : Utiliser 2**((size-1).bit_length()) pour arrondir
  4. Toutes les constantes ont besoin d'annotations de type : BLOCK: ct.Constant[int] est requis pour la compilation

Pour les directives détaillées sur les opérations mémoire, le dimensionnement des tuiles, les pièges courants, et les stratégies d'optimisation, consultez le répertoire guidelines/ (01–03).

Optimisation des performances

Principe clé : Penser en blocs de données plutôt qu'en éléments individuels. Choisir les tailles de tuiles qui correspondent aux caractéristiques du matériel et maximisent la réutilisation de données au sein des tuiles.

Directives de gestion de fichiers

IMPORTANT : Suivre ces règles pour la création de fichiers :

  1. Fichier unique par défaut : Générer un fichier .py unique contenant le kernel, la validation, et le code de test sauf si l'utilisateur demande explicitement plusieurs fichiers
  2. Pas de fichiers de documentation : NE PAS créer README.md, fichiers de documentation, ou fichiers d'exemples séparés sauf si explicitement demandés
  3. Tout inclus : Inclure l'implémentation du kernel, la logique de validation, et le code de test dans un fichier cohésif unique
  4. Création de fichier minimale : Créer uniquement ce qui est absolument nécessaire - préférer éditer les fichiers existants plutôt que d'en créer de nouveaux
  5. Pas de citations de sources : NE PAS inclure de commentaires ou docstrings mentionnant des fichiers TileGym, des fichiers de référence, ou des sources. Le code doit se suffire à lui-même sans attribution
  6. Sortie vers le répertoire de travail courant : Tous les fichiers .py de sortie doivent être écrits dans le répertoire de travail courant où l'utilisateur a démarré l'assistant de codage. Exécuter pwd au début de la tâche. Tous les fichiers .py générés vont directement dans ce répertoire (ex. ./composed_foo.py), jamais dans un sous-répertoire de la skill.
  7. Le répertoire de skill est en lecture seule : <skill_dir> est passé aux sous-agents uniquement pour qu'ils puissent lire les références, exemples, et instructions d'orchestration. Aucun agent — principal ou sub — ne peut jamais écrire, créer, ou sauvegarder un fichier sous <skill_dir>. L'utiliser uniquement avec les outils de lecture (Read, Glob, Grep, Bash cat/grep). Ne jamais le passer à Write, Edit, ou toute commande créant des fichiers.

Exemple de structure pour un fichier unique :

import cuda.tile as ct
import torch

# Implémentation du kernel
@ct.kernel
def my_kernel(...):
    ...

# Fonction de validation (si nécessaire)
def validate(...):
    ...

# Code de test/démo en bas
if __name__ == "__main__":
    # Tester le kernel
    ...

Critères de succès

Votre implémentation est réussie quand :

  1. Forward path pur cuTile : Pas d'appels compute nn.*/F.* dans forward()/composed_function() — tout compute routé via ct.launch (utilisation weight-init-only dans __init__ est fine)
  2. ✅ Les exemples existants ont été recherchés avant l'implémentation
  3. ✅ Les examples/ packagés ont été recherchés si TileGym n'avait pas de correspondance
  4. ✅ Un seul fichier .py créé (pas de READMEs, pas d'exemples séparés sauf s'il est demandé)
  5. ✅ Pas de citations de sources dans le code (pas de mentions de fichiers TileGym ou fichiers de référence dans les commentaires/docstrings)
  6. ✅ Le code cuTile généré compile sans erreurs
  7. ✅ Les résultats numériques correspondent à l'implémentation de référence dans la tolerance
  8. ✅ Toutes les constantes ont les annotations de type appropriées
  9. ✅ Toutes les dimensions de tuile sont des puissances de 2
  10. ✅ Les dimensions de grille couvrent correctement tous les éléments de tenseur
  11. ✅ Le code inclut la validation inline et le code de test dans le même fichier

Critères additionnels lors de l'utilisation de l'orchestration (tâches complexes) :

  1. ✅ La complexité a été évaluée et l'orchestration a été choisie pour les bonnes raisons
  2. ✅ L'Analyzer a produit des spécifications de kernel claires avec des références PyTorch
  3. ✅ Les kernels indépendants ont été générés en parallèle (pas séquentiellement)
  4. ✅ Chaque kernel individuel a été validé avant la composition
  5. ✅ La solution composée passe la validation de bout en bout contre la référence PyTorch originale

Souvenez-vous : Commencer par chercher les exemples existants, suivre le workflow systématiquement, et valider complètement. Les fichiers de référence contiennent des règles détaillées et des exemples pour vous guider à travers chaque aspect du développement de kernels cuTile.

Skills similaires