Skip to content
Améliorer les performances des noyaux CUDA avec le spilling vers la mémoire partagée (CUDA 13.0)
Source: developer.nvidia.com

Améliorer les performances des noyaux CUDA avec le spilling vers la mémoire partagée (CUDA 13.0)

Sources: https://developer.nvidia.com/blog/how-to-improve-cuda-kernel-performance-with-shared-memory-register-spilling, https://developer.nvidia.com/blog/how-to-improve-cuda-kernel-performance-with-shared-memory-register-spilling/, NVIDIA Dev Blog

TL;DR

  • CUDA 13.0 ajoute une optimisation qui spill les registres vers la mémoire partagée en premier, en utilisant la mémoire locale seulement si la mémoire partagée est insuffisante.
  • Lorsque l’espace est disponible, la mémoire partagée on‑chip réduit la latence et la pression sur le cache L2 par rapport aux spills vers la mémoire locale.
  • Avant CUDA 13.0, tous les spills allaient vers la mémoire locale; cela pouvait provoquer l’éviction du cache et une perte de performance dans les zones à forte pression.
  • Activez cette fonctionnalité via un pragma PTX dans une fonction : enable_smem_spilling. Le pragma est valide uniquement dans le scope de la fonction.
  • Les résultats réels, notamment dans les workloads QUDA (lattice QCD), montrent des gains typiques d’environ 5–10% dans les noyaux soumis à une forte pression de registre.

Contexte et arrière‑plan

Un noyau CUDA peut nécessiter plus de registres que ce qui est disponible sur un SM. Lorsque cela se produit, le compilateur déverse les variables excédentaires dans la mémoire locale, située physiquement dans la mémoire globale hors车. Avant CUDA 13.0, tous les spills allaient vers la mémoire locale. Même avec des tailles de cache L1 plus grandes, les données spillées pouvaient manquer et être écrites dans le cache L2, ce qui pouvait évincer des lignes de cache utiles et dégrader les performances dans les boucles et les chemins critiques à forte pression de registres. De plus, une portion importante de mémoire partagée par bloc pouvait rester inutilisée, surtout lorsque les bornes de lancement ou l’occupation étaient limitantes ou lorsque le noyau n’exploitait pas pleinement la mémoire partagée. Le billet CUDA explique comment le spill et l’occupation interagissent et pourquoi la granularité des bornes de lancement peut influencer les performances globales. L’exemple montre un noyau volontairement sous‑pression de registres pour illustrer l’overhead des spills et l’inefficacité d’un non‑usage de mémoire partagée lorsque cela est possible. L’idée clé est que garder les spills sur le chip, lorsque c’est possible, rapproche les données spillées du SM et réduit le trafic mémoire hors puce.

Ce qui est nouveau

CUDA 13.0 introduit une optimisation PTXAS qui permet de spill des registres vers la mémoire partagée on‑chip pour les noyaux CUDA. Lorsque cette option est activée, le compilateur priorise les spills dans la mémoire partagée disponible. Si l’espace manque, les spills restants retournent en mémoire locale, en préservant la correction. Ce changement apporte un avantage de performance clair en tirant parti de la mémoire on‑chip de moindre latence pour les données spillées, lorsque c’est possible. Comparé aux toolkits antérieurs, où les spills résidaient dans la mémoire globale et pouvaient impacter fortement les performances, le chemin de spilling vers la mémoire partagée réduit la latence et allège la pression sur le cache L2 dans de nombreux scénarios. L’optimisation a été démontrée sur des noyaux dérivés de la bibliothèque QUDA (utilisée pour les calculs de lattice QCD sur GPUs). Dans ces travaux, l’activation du spilling vers la mémoire partagée a typiquement généré des gains de performance dans la plage de 5–10%, dus à la réduction ou à l’élimination des spills locaux. Pour activer, les développeurs visant CUDA 13.0+ doivent insérer un pragma PTX à l’intérieur de la fonction, juste après la déclaration de la fonction :

// à l’intérieur du corps de la fonction, après la déclaration
.pragma_enable_smem_spilling

Sous la forme d’Assembly inline, la directive s’écrit comme suit :

#pragma enable_smem_spilling

La fonctionnalité est valable uniquement dans le scope d’une fonction. Elle ne doit pas être utilisée lorsque les bornes de lancement ne sont pas explicitement spécifiées : si les bornes ne sont pas connues, le PTXAS peut supposer le nombre maximum de threads par bloc, ce qui peut surestimer ou sous‑estimer l’allocation de mémoire partagée et réduire l’occupation. Pour un comportement plus prévisible et de meilleures performances, utilisez cette fonctionnalité uniquement lorsque les bornes de lancement sont explicitement définies. La fonctionnalité est disponible à partir de CUDA 13.0 et n’était pas présente dans les versions antérieures. Lorsque les spills sont redirigés vers la mémoire partagée, l’utilisation mémoire par bloc peut se refléter dans le ressenti, comme indiqué par l’exemple montrant 46080 octets de mémoire partagée utilisés.

Pourquoi cela compte (impact pour les développeurs/entreprises)

Pour les développeurs travaillant sur des noyaux GPU à hautes performances, en particulier ceux soumis à une forte pression de registres, cette optimisation propose une voie pragmatique pour améliorer le débit sans changer l’algorithme. En dirigeant les spills vers la mémoire partagée on‑chip, la latence d’accès peut diminuer et la pression sur le cache L2 peut être réduite, se traduisant par des gains temporels dans les boucles serrées et les chemins critiques. Cependant, les bénéfices ne sont pas universels. Le gain dépend de bornes de lancement précises et d’un usage constant de la mémoire partagée pour tirer pleinement parti du spill vers mémoire partagée. Si le budget mémoire partagé n’est pas dimensionné correctement, l’occupation peut souffrir et annuler les gains. Pour les entreprises déployant des bibliothèques et applications CUDA volumineuses, cette option offre un levier supplémentaire pour optimiser les performances, particulièrement dans des domaines comme les calculs de lattice QCD où la pression de registre est élevée. Les gains observés dans QUDA (5–10%) suggèrent une amélioration tangible dans ce type de workload et peuvent être observés dans d’autres charges similaires.

Détails techniques ou Implementations (comment ça marche)

  • L’idée centrale est de prioriser les spilling vers la mémoire partagée on‑chip par défaut, dès que l’espace est suffisant par bloc.
  • Si l’espace partagé est insuffisant, le spilling passe à la mémoire locale, tout en conservant la correction du programme.
  • L’optimisation est introduite comme une fonctionnalité PTXAS dans CUDA 13.0; l’activer nécessite une instruction explicite d’opt‑in dans le scope de la fonction via le pragma PTX enable_smem_spilling.
  • Les contraintes incluent la nécessité de spécifier des bornes de lancement afin d’estimer précisément la mémoire; sinon, le compilateur peut supposer le nombre maximum de threads par bloc, ce qui peut affecter l’occupation si les lancements réels sont plus petits.
  • L’impact est dépendant du workload. Les évaluations QUDA rapportent des gains typiques dans la fourchette 5–10%.
  • Le mécanisme maintient la correction du programme tout en rapprochant les données spillées du SM lorsque c’est possible.

Tableau: cibles de spilling et implications

| Aspect | Avant CUDA 13.0 | Spill mémoire partagée (CUDA 13.0) |---|---|---| | Cible de spilling | Mémoire locale (off‑chip) | Mémoire partagée (on‑chip) lorsque disponible; fallback mémoire locale sinon |Latence | Plus élevée dû à l’accès hors puce | Plus faible lorsque les spills restent en mémoire partagée |Pression L2/cache | Éviction possible de lignes utiles | Réduite lorsque les spills résident en mémoire partagée |Occupation | Liée indirectement aux spills | Doit être gérée via des bornes de lancement explicites |

Exemple de référence du billet NVIDIA

L’article mentionne un noyau avec 46080 octets de mémoire partagée alloués, illustrant l’utilisation des spills vers la mémoire partagée dans un contexte pratique. Cet exemple montre comment l’optimisation peut se refléter dans l’empreinte mémoire par bloc lorsque activée.

Points clés à retenir

  • Spill vers mémoire partagée est une option à activer dans CUDA 13.0 via un pragma de fonction.
  • Les spills vont en priorité vers la mémoire partagée on‑chip, avec fallback vers la mémoire locale si l’espace n’est pas suffisant.
  • Les gains réels varient selon le workload; les démonstrations QUDA indiquent typiquement 5–10% d’amélioration.
  • Pour éviter des régressions d’occupation, déclarez des bornes de lancement explicitement lors de l’utilisation de cette option.
  • Cette optimisation conserve la validité et offre une amélioration potentielle des performances dans les kernels soumis à une forte pression de registres.

FAQ

  • Qu’est‑ce que le spilling des registres vers mémoire partagée?

    C’est une fonctionnalité de CUDA 13.0 qui privilégie le spilling des registres vers la mémoire partagée on‑chip, réduisant la latence et la pression sur le L2, avec un fallback vers la mémoire locale selon les besoins.

  • Comment l’activer?

    Utilisez le pragma PTX `enable_smem_spilling` à l’intérieur de la fonction, juste après sa déclaration. Le pragma s’applique uniquement au scope de la fonction.

  • uand devrais‑je l’utiliser?

    Lorsque votre noyau a des bornes de lancement bien définies et une utilisation régulière de la mémoire partagée, afin de réduire les spills locaux dans les chemins critiques à forte pression de registres. Assurez‑vous que la mémoire partagée est dimensionnée correctement pour éviter des régressions d’occupation.

  • uelles améliorations puis‑je attendre?

    Les évaluations QUDA indiquent typiquement des gains de l’ordre de 5–10%, dus à la réduction ou l’élimination des spills locaux via le chemin de spilling vers mémoire partagée.

Références

More news