Préparation à la migration cuPyNumeric
Objectif
Utilise cette skill AVANT la migration, non pendant. Réponds à une seule question : Parmi les APIs NumPy existantes de l'utilisateur, lesquelles vont passer à l'échelle sur cuPyNumeric, et lesquelles nécessitent une refonte, avant qu'il ne commette des semaines d'ingénierie au portage ? Pour répondre : lis la source, classe chaque idiome NumPy selon son scaling multi-GPU attendu sur la pile GPU Legate/NVIDIA, fais la référence croisée avec le manifeste API-support fourni, et produis un verdict structuré avec raisonnement par constat et pointeurs de recettes.
C'est une évaluation statique, en lecture seule. Inspecte la source de l'utilisateur avec Read, Grep et Glob. Ne pas exécuter le code de l'utilisateur, modifier ou écrire des fichiers, ou afficher des variables d'environnement ou secrets. Les commandes legate et cuPyNumeric Doctor affichées ci-dessous sont des suggestions pour que l'utilisateur les exécute — ce n'est pas une action que cette skill effectue.
Si cette skill n'a jamais été vue avant, commence par references/getting-started.md.
Quand utiliser cette skill
Utilise-la quand l'utilisateur est sur le point de migrer du code NumPy vers le GPU et demande s'il va passer à l'échelle sur cuPyNumeric / GPU, s'il doit migrer, quelles parties en bénéficieront, ce qui doit changer avant le portage, ou si le portage en vaut la peine — ou mentionne évaluation pré-migration, analyse de scaling, analyse d'idiomes, planification de refonte GPU, ou identification d'anti-patterns NumPy pour le GPU.
Refuse et redirige quand la demande n'est pas une évaluation pré-migration :
- Performance / profilage post-migration (« déjà porté, pourquoi c'est lent ? ») → pointe vers
legate --profileet la walkthrough upstream profiling and debugging. - Authoring CUDA / kernel personnalisé (« écris/optimise un kernel CUDA »)
Un workload graphe / sparse / ML / NLP que l'utilisateur demande de migrer est toujours dans le périmètre : évalue-le et retourne NOT RECOMMENDED via Gate 4. C'est un verdict, pas un refus.
Instructions
Exécute les cinq étapes ci-dessous, dans l'ordre. Lis le code de l'utilisateur et raisonne-le sémantiquement ; n'émets pas un verdict en prose en une seule fois.
Étape 1 — Gather context
Élicite avant de scanner le code. Chaque item ci-dessous a une valeur par défaut adaptée au workload typique — utilise la valeur par défaut quand l'utilisateur n'en fournit pas ; ne bloque pas sur les questions.
- Localisation de la source. Déduis-la du répertoire de travail courant quand aucun chemin n'est donné.
- Tailles approximatives des tableaux du hot-path au runtime. Défaut : 30–50 millions d'éléments. Mappe les chiffres de l'utilisateur (ou ce défaut) aux tiers Gate 2 (plancher 65 K par GPU ; 10 M+ pour un vrai speedup mono-GPU ; 100 M+ pour multi-GPU).
- Hardware cible. Défaut : 1–4 GPUs, un seul nœud. Confirme avant de supposer multi-nœud. Pour des exécutions CPU seulement, demande la RAM par nœud au lieu de FBMEM.
- Pattern de calcul dominant. Stencil / GEMM / Monte Carlo / réductions / mélange-avec-SciPy. Demande à l'utilisateur de le nommer ; sinon déduis-le du code à l'étape 3.
Énonce les défauts que tu as appliqués en haut de l'évaluation pour que l'utilisateur puisse les corriger. Si une valeur est indéterminable, dis-le clairement et procède avec une évaluation qualitative uniquement — ne fabrique pas de chiffres au-delà des défauts ci-dessus.
Étape 2 — Load the API support manifest
Lis assets/api-support.md, le snapshot committé du tableau de comparaison NumPy-vs-cuPyNumeric upstream. Pour chaque API NumPy que le code appelle, trouve sa ligne et lis le glyphe principal :
✓✓ numpy.X— implémenté et fonctionne sur multi-GPU (le meilleur chemin).✓ numpy.X— implémenté mais mono-GPU/CPU seulement (caveats multi-nœud).🟡 numpy.X — <note>— support partiel ; lis la note.✗ numpy.X— non implémenté sur le chemin distribué cuPyNumeric. Le comportement à l'appel est spécifique à la version (certaines APIs non supportées passent par NumPy host, d'autres lèvent une exception) — de toute façon, l'utilisation hot-path est un blocker de migration. Ne promets pas aux utilisateurs un fallback silencieux vers host-NumPy.
Si la ligne Fetched: date de plus de ~90 jours, rafraîchis le snapshot — voir la section Available Scripts.
Étape 3 — Read the code semantically
Parcoure les fichiers de l'utilisateur avec Read et Grep et classe chaque région de mathématiques de tableaux par rapport à references/idioms-that-scale.md et references/idioms-that-block.md (les raisons complètes et R-codes vivent là). Lis sémantiquement, pas par regex : avant de signaler, confirme que arr remonte à un tableau cupynumeric (ou np.* aliasé à lui) et vérifie si l'accès se situe à l'intérieur d'une boucle hot. Applique ces règles :
- Signale les boucles d'éléments (
for i in range(n): arr[i] = ...) comme blockers ; traite une boucle epoch/step/fichier avec un corps vectorisé comme correct — distingue les deux. - Signale la synchronisation scalaire —
.item()/float()/int()/bool()/complex()sur un tableau cuPyNumeric à l'intérieur d'une boucle hot (sync host par itération) ; autorise-la à la limite. - Signale les conditions réductrices —
if/whilesur une réduction de tableau (while np.max(err) > tol:) synchronise chaque itération. - Signale l'allocation hoistable dans une boucle comme inefficacité réparable.
- Signale
mpi4pydans le code runtime qui partitionne/communique les données de tableau aux côtés decupynumeric(R108) — mais confirme d'abord qu'il émet des appels MPI sur un hot path ; ignore un grep hit dans un README, script de build, ou lanceur alt. - Signale
order=surreshape/asarray/flattencomme R109 — toujours, indépendamment de si la version avertit ou ignore silencieusement. - Cite toujours R304 en INFO pour
np.random.*sous multi-GPU : la reproductibilité bit-identique cross-GPU est impossible par défaut (--gpus N/LEGATE_GPUSest l'argument du lanceur Legate). - Signale les builtins Python sur les tableaux (
sum/max/min/any/iter(arr)) — fallback itération host (R110; meilleures pratiques upstream). Autoriselen(arr)(shape lookup ; préfèrearr.shape[0]/arr.sizepour la sécurité 0-d). - Signale
cupymélangé aveccupynumericdans une boucle hot (R111); les runtimes ne partagent pas la mémoire GPU, donc chaque passage passe par NumPy host. - Cherche chaque API NumPy que le code appelle dans
assets/api-support.md(légende glyphes à l'étape 2).
Pour le fond « pourquoi », lis references/gpu-stack.md (mémoire, SM, communication, dispatch) et references/execution-model.md (lazy execution, sync points, mapper).
Étape 4 — Produce a structured assessment
Livre le rapport dans cet ordre. Cite file:line pour chaque constat pour que l'utilisateur puisse naviguer.
- Verdict en une seule phrase — voir « Verdict framework » ci-dessous.
- Ce qui fonctionne (constats SCALES) — cite les lignes représentatives pour que l'utilisateur voie ce qui va accélérer après le swap d'import.
- Ce qui bloque (constats BLOCKS) — chacun lié à
idioms-that-block.mdet une recette dansrefactor-recipes.md. - Ce qui est réparable (constats REFACTOR) — groupe par recette ; une recette répare souvent de nombreux sites.
- Notes de compatibilité / coût (constats INFO) — limites SciPy, linalg / FFT mono-device, RNG layout vs
--gpus N. - Lacunes de support API — APIs que le code appelle qui ne sont pas implémentées ou mono-GPU seulement selon le manifeste.
- Résumé du framework de décision — Gates 1–6 de
references/decision-framework.md, marqués pass / fail / uncertain. - Prochaines étapes recommandées — quelles recettes appliquer en premier, s'il faut porter un module d'abord, et quand impliquer cuPyNumeric Doctor.
Les 8 sections doivent toutes apparaître, même quand le verdict est READY ou NOT RECOMMENDED. Sous une section vide écris "None for this code" ou "n/a — see verdict" en une ligne — NE SUPPRIME PAS la rubrique ; les rubriques sont le contrat structurel sur lequel le rapport est évalué. Voir assets/sample_report.md pour les rapports exercés.
Étape 5 — Hand off to cuPyNumeric Doctor for runtime validation
Dirige l'utilisateur pour exécuter cuPyNumeric Doctor une fois qu'il a appliqué les recettes et que le code s'exécute :
CUPYNUMERIC_DOCTOR=1 CUPYNUMERIC_DOCTOR_FORMAT=json CUPYNUMERIC_DOCTOR_FILENAME=doctor-report.json legate --gpus 1 main.py
cuPyNumeric Doctor détecte au runtime ce que l'examen de la source peut manquer (accès item scalaire, itération ndarray, indexation avancée, mésusage nonzero, import mpi4py, ops in-place sur les vues). Termine l'évaluation par : « maintenant exécute avec cuPyNumeric Doctor activé ; voici ce qu'il faut chercher dans sa sortie. »
Verdict framework
Assigne le verdict qualitativement, à partir des sortes de constats, pas un score :
| Verdict | Quand | Action |
|---|---|---|
| READY | Pas de BLOCKS ; peu/pas de REFACTOR | Swap l'import ; benchmark |
| LIGHT REFACTOR | Quelques patterns fixables par recette (R201–R206), ou un ou deux BLOCKS simples | Applique 1–3 recettes de refactor-recipes.md ; re-marche vers READY |
| SIGNIFICANT REFACTOR | Plusieurs BLOCKS dans les hot paths, ou n'importe quel R108 (mpi4py) — réécritures, pas disqualifications |
Vrai projet ; budget 1–3 semaines d'ingénierie par module |
| NOT RECOMMENDED | Seulement deux défaillances : Gate 2 (tableaux sous le plancher 65 536) ou Gate 4 (mauvais pattern de calcul). Un pile de BLOCKS ne débarque pas ici | Restructure d'abord ou utilise un runtime différent |
Applique ces règles dans l'ordre ; la première correspondance gagne :
- Gate 4 échoue (sparse / graphe / ML / séquentiel / string) → NOT RECOMMENDED.
- Gate 2 échoue (hot-path arrays < 65 536 éléments/GPU, aucun chemin de batching réaliste) → NOT RECOMMENDED.
- N'importe quel R108 (
mpi4py) → SIGNIFICANT REFACTOR (la récriture de la couche parallélisme est le coût, pas une disqualification). - Plusieurs BLOCKS (R101–R111) sur les hot paths → SIGNIFICANT REFACTOR (le count ne dépasse pas cela — chaque BLOCKS a une recette documentée).
- Un ou deux BLOCKS fixables par recette (p.ex., R101–R104 element-loop / sync) → LIGHT REFACTOR.
- Seulement les patterns REFACTOR (R201–R206) → LIGHT REFACTOR ; les recettes sont mécaniques.
- Pas de BLOCKS, pas de REFACTOR → READY.
- APIs manquantes du manifeste sur le hot path → rétrograde d'un tier (SIGNIFICANT reste SIGNIFICANT, jamais NOT RECOMMENDED). Les APIs mono-GPU seulement importent pour multi-nœud.
Pèse les sortes de constats, pas leur count. Un R101 dans une boucle hot surpasse dix R001s — il détruit le scaling que les R001s auraient livré. Inversement une pile de BLOCKS + R108 est toujours SIGNIFICANT, pas NOT RECOMMENDED — les tiers mesurent le coût d'ingénierie, pas le désespoir. NOT RECOMMENDED nécessite une défaillance de taille ou pattern-de-calcul. Framework complet : references/decision-framework.md.
Ce qui passe à l'échelle vs ce qui bloque (aperçu rapide)
- SCALES (garde tel quel) — elementwise vectorisé, réductions, matmul / einsum,
np.where, grande slicing stencil par-GPUarr[1:-1, 1:-1],out=, boolean-mask indexing. - BLOCKS (retire avant migration) — boucles d'éléments,
np.vectorize,for row in arr,.item()/.tolist()/bool(arr)dans une boucle hot, réductionif/whiledans une boucle,arr[::2],dtype=object,mpi4py,order=,min/max/sum(arr). - REFACTOR (applique une recette) — alloc dans une boucle,
x = x + yrebind dans une boucle,vstack/hstack/concatenatedans une boucle,np.nonzero()+ indexing, mutation de vues dediag/flip/flatten,reshapedans une boucle hot. - INFO (note de coût, pas un blocker) — imports SciPy, mono-device
linalg.qr/svd, mono-transformfft.*,linalg.solve/choleskyavec seuil de taille.
Taxonomie complète dans idioms-that-scale.md et idioms-that-block.md. Passe silencieusement sur n'importe quelle API que le manifeste ne liste pas (hors périmètre du tableau upstream — la signaler serait du bruit).
Ordre de lecture
Le guide canonique, à lire dans l'ordre, réside dans references/getting-started.md — lis-le une fois pour l'orientation.
Pour une évaluation non triviale, les lectures essentielles sont idioms-that-block.md, refactor-recipes.md, et decision-framework.md ; le reste (idioms-that-scale.md, gpu-stack.md, execution-model.md, partitioning-and-balance.md, case-studies.md) se lis à la demande.
Limitations
- Ne s'exécute pas cuPyNumeric. Aucun runtime requis ; c'est le pré-port check. La mesure de speedup réelle a lieu après la migration.
- Ne génère pas automatiquement le code refactorisé. Il identifie ce qui doit changer et pointe vers les recettes ; l'utilisateur (ou un agent de suivi) les applique.
- Ne profile pas le workload. Pour la mesure runtime utilise
legate.timing.time()et le guide upstream profiling and debugging. - Ne remplace pas le jugement. La correspondance de pattern manque les syncs implicites à l'intérieur de logging, les décorateurs qui cachent
.tolist(), les mésappariements de partition dépendants de données runtime. Lis aussi la source, surtout dans les cas limites.
Exemples
Une évaluation exercée des fixtures bundled assets/examples/ (un exemple, pas un template) :
Verdict: LIGHT REFACTOR.
scales_well.pyse traduit proprement ;needs_refactor.pya besoin d'une allocation hoistée ;blocks_scaling.pysynchronise chaque itération via.item().Ce qui fonctionne:
scales_well.py:23-31(stencil R005),:40-44(reduction R002),:18-22(elementwise R001). Ce qui bloque:blocks_scaling.py:51-58(R104 —.item()dans boucle hot) → RR-sync. Ce qui est réparable:needs_refactor.py:21-28(R201 — alloc dans boucle) → RR-alloc. Prochaine: applique les recettes ; re-marche vers READY ; activeCUPYNUMERIC_DOCTOR=1sur la première vraie exécution.
Le rapport exercé complet est dans assets/sample_report.md.
Références authoritative upstream
- Tableau de comparaison (source pour
assets/api-support.md) : https://nv-legate.github.io/cupynumeric/api/comparison.html (miroir, le plus courant) /.../latest/api/comparison.htmlsur docs.nvidia.com (canonique) - Meilleures pratiques, Doctor, profilage, différences avec NumPy, lanceur Legate — sous https://docs.nvidia.com/cupynumeric/latest/ (
user/practices.html,user/doctor.html,user/profiling_debugging.html,user/differences.html) et https://docs.nvidia.com/legate/latest/manual/usage/running.html - Source : https://github.com/nv-legate/cupynumeric
Scripts disponibles
| Script | Objectif | Arguments |
|---|---|---|
scripts/fetch_api_support.py |
Scrape le tableau de comparaison upstream dans assets/api-support.md. Stdlib Python uniquement ; autonome. |
--default-path (écris le assets/api-support.md committé) ; --docs-nvidia-url (utilise l'URL canonique docs.nvidia.com au lieu du miroir GitHub Pages par défaut) |
L'utilisateur l'exécute pour rafraîchir le manifeste (python scripts/fetch_api_support.py --default-path).
Références bundled et assets
Les fichiers references/ sont énumérés sous Ordre de lecture requis ci-dessus (plages R-code : idioms-that-scale.md = R001–R007 / R301–R305 ; idioms-that-block.md = R101–R111 / R201–R206). Assets : assets/api-support.md (snapshot API committé, chargé à l'étape 2), assets/sample_report.md et assets/examples/*.py (rapport exercé et fixtures).
Troubleshooting
| Symptôme | Cause | Correction |
|---|---|---|
Ligne Fetched: dans le manifeste > ~90 jours |
Snapshot obsolète | Exécute fetch_api_support.py --default-path (exécution user) |
| Manifeste manquant ou scraper échoue | HTML upstream changé | WebFetch le tableau de comparaison pour cette évaluation |
| NOT RECOMMENDED pour beaucoup de BLOCKS réparables | Heuristiques appliquées hors ordre | Réapplique l'ordre : Gate 4 → Gate 2 → R108 → BLOCKS → REFACTOR ; pèse les sortes, pas le count |
| Authoring de kernel ou profilage post-migration | Hors périmètre | Refuse et redirige (voir « When to use ») — pas de verdict |