CUDA

Voici la seconde partie de mon article sur les GPU.
Celui-ci vous permettra de découvrir CUDA de NVIDIA

1      Présentation

CUDA ou Computer Unified Device Architecture est une librairie de développement créée par NVIDIA en 2007. Celle-ci associée à une carte graphique compatible, permet d’utiliser la puissance de calculs des GPU d’architectureG80 et plus de NVidia (à partir des GeForce 8800 et déclinaisons).
Par abus de langage nous parlerons aussi de langage CUDA.
CUDA est aussi le premier langage à exploiter l’unification des shaders : les processeurs de la carte ne sont pas différentiés en unités pour le traitement des vertex ou des fragments, chacun de ceux-ci peut être assigné à n’importe quelle tâche.

L’API CUDA supporte plusieurs langages tels que le C, le C++ et le Fortran.
Ces trois langages peuvent être utilisés simultanément pour écrire diverses fonctions exécutées par le GPU.

Il existe cependant des « wrapper », des passerelles, servant de liaison entre ces langages bas-niveau et des langages de plus haut niveaux tels que Python, Java et .Net.

CUDA est constitué d’un pilote (driver), déjà intégré aux drivers les plus récents, d’un runtime, et de quelques librairies.

Les différents composants de CUDA
Les différents composants de CUDA

Il existe 3 niveaux de programmation pour CUDA, chacune disposant de sa propre API :

–       Utilisation d’une librairie externe.
Celle-ci, possède le code de toute les fonctions à exécuter par le GPU, et sert de liaison entre le programme et le GPU.
Le développeur ne peut utiliser que certaines fonctions prédéfinies, et ne peut contrôler directement le GPU.
Les deux librairies les plus connues sont CUBLAS qui offre un ensemble d’éléments pour réaliser des calculs d’algèbre linéaires sur le GPU, ainsi que CUFFT qui permet le calcul de transformée de Fourrier.

–       l’API de haut niveau : l’API CUDA Runtime ou appelée plus communément C for CUDA.
Cette API est implémentée « au dessus » de l’API bas niveau. Chaque appel à une fonction du runtime est décomposé en instructions plus basiques gérées par l’API driver.
Ces 2 API sont exclusives : le programmeur doit utiliser soit l’une soit l’autre. Il est impossible de mélanger des appels de fonctions de l’une et de l’autre.
Lorsque l’on parle d’API de haut niveau, il convient de relativiser : l’API runtime reste ce que beaucoup considèreraient comme déjà de très bas niveau. Cependant elle offre des fonctions bien pratiques pour l’initialisation ou la gestion des contextes.
Cette API sera la plus couramment utilisée.

–       l’API de bas niveau : l’API CUDA Driver.
Cette API est plus complexe à gérer, elle demande plus de travail pour lancer un traitement sur le GPU, mais en contrepartie elle est plus flexible, offrant un contrôle supplémentaire au programmeur qui le désire.
Cette API a l’avantage de pouvoir charger des portions de code en tant que module à partir de code binaires CUDA ou de code assembleur, et permet aussi d’inspecter les paramètres, …
Cette API est beaucoup plus difficile à écrire, à débugger et nécessite beaucoup plus de lignes de code à écrire, mais elle offre de meilleure performances.

Les API Runtime et Driver sont capables toutes les deux de communiquer avec des ressources OpenGL ou Direct3D. L’utilité est évidente : CUDA pourrait être utilisé pour générer des ressources (géométrie, textures procédurales, etc.) qui seraient ensuite passées à l’API graphique ou à l’inverse on pourrait imaginer que l’API 3D pourrait envoyer le résultat du rendu à CUDA qui serait dans ce cas utilisé pour effectuer un post traitement. Les exemples d’interactions sont nombreux et l’avantage est que les ressources restent stockées dans la mémoire RAM du GPU sans avoir à passer par le goulot d’étranglement du bus PCI-Express.

CUDA est fourni avec un « émulateur » : le code devant s’exécuter sur le GPU est en fait exécuté sur le GPU. Les performances sont alors bien moindres, mais permettent de débugger l’application et de tester celle-ci lorsqu’on ne dispose pas de GPU compatible.