CUDA

4      Gestion de la mémoire

4.1      Mémoire Globale

CUDA est capable de lire et d’écrire sur la mémoire embarquée dans la carte graphique.
Ces opérations portent, respectivement, les noms de gathering et de scattering comme nous l’avons vu dans la section 1.3.4 de l’article précédent

Exemple d’accès mémoire de type Gathering
Exemple d’accès mémoire de type Gathering
Exemple d'accès mémoire de type Scattering
Exemple d'accès mémoire de type Scattering

La mémoire globale est accessible depuis n’importe quel endroit dans CUDA, avec les mêmes performances.
Les données de cette mémoire ne sont pas mises en cache. Les temps de latence sont donc extrêmement longs : de 400 à 600 cycles. Il est à noter que durant chaque demande d’accès à cette mémoire, le multiprocesseur reste inactif.
La mémoire utilisée pour la mémoire globale est de la DRAM. Cette mémoire bon marché (1.34$ pour une puce de 1Go DDR3 en juillet 2009 pour les intégrateurs). Celle-ci est utilisée comme mémoire principale de nos ordinateurs et a l’avantage d’être compacte. Cependant le défaut de cette mémoire est sa latence pouvant atteindre 30 ns (environ 30 cycles), sans compter le temps de transfert entre la mémoire et le multiprocesseur…

4.2      Mémoire locale

Tout comme la mémoire globale, la mémoire locale n’est pas cachée et souffre de temps de latence importants. Cette mémoire est propre à chaque thread. Elle est utilisée automatiquement pour les structures de grandes tailles ainsi que pour les tableaux de grande taille ou de taille inconnue.

4.3      Mémoire constante

La mémoire constante est une zone mémoire de 8Ko accessible en lecture uniquement. Cette mémoire est cachée, ce qui lui assure une latence d’1 instruction. Pour les threads d’un demi warp, l’accès à cette mémoire est aussi rapide qu’à celle d’un registre, tant que les threads accèdent au même emplacement mémoire. Le coût de lecture augmente linéairement avec le nombre d’adresses différentes demandées par les threads.

4.4      Mémoire texture

Cet espace mémoire est optimisé pour les espaces à deux dimensions. Cette mémoire est cachée, et optimisée pour des accès en lecture. La latence est donc constante et très faible.

4.5      Mémoire partagée

Cette mémoire est présente sur le chipset, ce qui lui permet d’être assez rapide, plus que la mémoire locale.
Elle est commune à un bloc de threads, chaque block de threads possédant sa propre zone de mémoire partagée.
En fait, pour tous les threads d’un warp, accéder à cette mémoire est aussi rapide que d’accéder à un registre, tant qu’il n’y a pas de conflit entre les threads.

Pour permettre une bande-passante assez élevée, la mémoire partagée est divisée en modules de mémoire, les banques, qui peuvent être accédée simultanément. Ainsi, n lectures ou écritures qui tombent dans des banques différentes peuvent être exécutées simultanément dans un warp, ce qui permet d’augmenter sensiblement la bande passante, qui devient n fois plus élevée que celle d’un module.

Cependant, si deux demandes tombent dans la même banque, il y a un conflit de banques, et l’accès doit être sérialisé. Le matériel divise ces requêtes problématiques en autant de requêtes que nécessaire pour qu’aucun problème n’ait lieu, ce qui diminue la bande passante d’un facteur équivalent au nombre de requêtes total à effectuer.

Pour des performances maximales, il est donc très important de comprendre comment les adresses mémoires sont reliées aux banques, pour pouvoir prévoir les requêtes, et, ainsi, minimiser les conflits.

4.6      Registres

Généralement, l’accès à un registre se fait en un seul cycle.
Le compilateur et l’organisateur de threads organisent les instructions pour des performances optimales.
Le meilleur moyen d’obtenir de bonnes performances est d’utiliser un multiple de 64 comme nombre de threads par bloc.
Chaque multiprocesseur dispose de 8192 registres.

4.7      Mémoire système

Depuis les GT200 (GeForce GTX 260 à 295), il est désormais possible d’utiliser la mémoire principale du système, alias RAM, grâce à CUDA 2.2.
Les appels à cette mémoire ne peuvent être fréquents : ils sont encore plus lents que les appels à la mémoire locale (700 à 800 cycles de latence !). Mais la RAM est disponible, de nos jours, en quantités plus grandes que celle disponible sur les GPU.

Représentation des différents types de mémoires
Représentation des différents types de mémoires

Mémoire

Localisation

Cachée

Accès

Portée

Registre

chipset

Non

Lecture/Ecriture

1 seul thread

Locale

DRAM

Non

Lecture/Ecriture

1 seul thread

Partagée

chipset

Non indiqué

Lecture/Ecriture

Tous les threads du bloc

Globale

DRAM

Non

Lecture/Ecriture

Tous les threads + hôte

Constante

DRAM

Oui

Lecture

Tous les threads + hôte

Texture

DRAM

Oui

Lecture

Tous les threads + hôte

Récapitulatif des caractéristiques des mémoires

4.8 La mémoire, en chiffres, pour une Geforce 8800 GTX

Dans le cas d’une Geforce 8800 GTX, les multiprocesseurs disposent de 8192 registres à partager entre tous les threads de tous les blocs actifs sur ce multiprocesseur.
Le nombre de blocs actifs par multiprocesseur pour sa part ne peut pas dépasser les 8, tandis que le nombre de warps étant pour sa part limité à 24 (24 warps x 32 threads par warp = 768 threads).
Un multi processeur est constitué de deux processeurs, ce qui donne un nombre total de threads :
8 multiprocesseurs x 2 x 768 threads par processeurs = 12.288 threads
Nous disposons donc de 12.288 threads en cours de traitement à chaque instant.

Connaître ces limites peut sembler rébarbatif, mais est très utile pour bien dimensionner son application en fonction des ressources disponibles.

Optimiser un programme CUDA consiste donc essentiellement à équilibrer au mieux le nombre de blocs et leur taille : plus le nombre de threads par blocs sera important moins la latence des opérations mémoire se fera ressentir, mais cela diminue le nombre de registres par threads.
Il est à noter qu’un bloc de 512 threads serait particulièrement peu efficace, car un seul bloc pourrait être actif sur un multiprocesseur, gâchant ainsi potentiellement 256 threads ( 768 threads max par warp – 512 threads actifs).

NVIDIA conseille donc d’utiliser des blocs de 128 à 256 threads qui offrent le meilleur compromis entre masquage de la latence et le nombre de registres suffisants pour la plus part des kernels.

Représentation des différents types de mémoires (vision globale)
Représentation des différents types de mémoires (vision globale)

6 réflexions sur « CUDA »

  1. Bonjour,
    je souhaite savoir quels sont les outils et comment faire les configurations s’il y a lieu de faire, à fin programmer en java en utilisant CUDA?
    j’ai un windows xp pack2
    carte graphique NVIDIA GeForce Go 7300
    1 Go de RAM
    merci à l’avance.
    Cordialement.

  2. Bonjour, il y a une faute en première page, je cite :
    « le code devant s’exécuter sur le GPU est en fait exécuté sur le GPU »
    -> est en fait exécuté sur le CPU.

    Voila merci pour l’article sinon 🙂

  3. pouvez vous nous indiquer les étapes à suivre pour utiliser cuda avec Visual studio 2010 ou bien 2008 et merci d’avance

Laisser un commentaire