NVIDIA GeForce 8800 GTX & 8800 GTS

Publié le 08/11/2006 (Mise à jour le 12/02/2007) par
Imprimer
Historique
Le rendu 3D moderne, repose depuis le départ sur le texturing. Il y a 10 ans, les pixels affichés à l’écran se voyaient simplement appliqué une texture de décoration. Au fil des années ce rendu 3D est devenu de plus en plus évolué, les shaders permettent d’appliquer des fonctions mathématiques complexes sur les pixels et les textures ne sont plus de simples décorations mais des tables de données dont les usages sont très variés. Très souvent, un shader moderne va contenir plusieurs dizaines d’instructions mathématiques par accès aux textures. Dès lors on peut se dire que la gestion efficace de ces accès aux textures n’est plus primordiale et qu’il vaut mieux se concentrer sur la puissance de calcul brute.


Ce n’est malheureusement pas aussi simple pour plusieurs raisons. La première est qu’un accès à une texture est souvent associé à un filtrage de celle-ci. Or, lorsque celui-ci est de qualité, même s’il ne requiert qu’une seule instruction, il peut prendre de nombreux cycles à s’exécuter. Cependant, nous laisserons de côté ce point pour le moment puisqu’au niveau du cœur de l’architecture, le second est beaucoup plus important. Ce second point concerne la latence de l’accès aux textures.

L’accès à la mémoire de la carte graphique peut prendre plus de 100 cycles et s’il fallait attendre ces 100 cycles pour passer à l’instruction suivante, nous en serions toujours à l’âge de pierre de la 3D. Le GPU est donc capable de précharger les données dans un petit cache pour que, la majorité du temps, la latence de l’accès à la mémoire ne soit pas un problème. Lorsque les textures étaient de simples décorations, cette tâche était très facile puisque le GPU savait à l’avance quelle zone de mémoire devait être préchargée. Mais avec l’évolution des techniques de rendu, les textures peuvent contenir des données très variées et leur accès n’est plus aussi clairement déterminé. Le GPU doit donc être capable de traiter des accès aux textures indéterminés sans faire payer le coût de la latence de la mémoire. Plusieurs solutions à ce problème existent.

Auparavant, la solution de Nvidia a été d’utiliser un très long pipeline dans lequel plus de 100-150 étages étaient uniquement destinés à masquer cette latence. Par exemple les GeForce 7 disposent de 2 unités de calcul par pipeline de traitement des pixel shaders. La première des unités commande l’accès aux textures, s’ensuit alors un très long "tunnel" dans lesquels les pixels dorment avant d’arriver à la seconde unité. Plus ce tunnel est long, plus il est probable que la donnée recherchée dans les textures soit arrivée à temps et donc qu’il ne soit pas nécessaire de bloquer le pipeline pour l’attendre. Le débit optimal peut donc être conservé. En contrepartie il faut pouvoir remplir ce pipeline avec autant de pixels qu’il y a d’étages, ce qui a obligé Nvidia à traiter les pixels par grosse quantité.

Ce n’était pas un problème jusqu’à l’arrivée des branchements dans les shaders. Dans un GPU, le flux d’instructions est géré par paquet d’éléments (de pixels dans ce cas) et il n’est pas possible qu’un élément reçoive une instruction différente qu’un autre. Dans le cas où le résultat d’un branchement diverge entre les pixels d’un même groupe le GPU doit exécuter les 2 branches pour tous les pixels avec un masque de manière à ne pas prendre en compte les instructions de la mauvaise branche. Mais elles sont traitées malgré tout ce qui est très mauvais pour les performances. Les GeForce 7900 et précédente, avec leur très long pipeline qui impose de travailler sur de gros groupes de pixels (+/- 1000 !), se retrouvent donc en difficulté. Un autre problème se présenté : la largeur du pipeline est fixe, tout comme sa longueur. Si le pipeline n’est pas assez large pour un pixel, soit si par étage le pipeline de propose pas assez de registres temporaires, un pixel doit occuper plusieurs étages, réduisant ainsi le débit. C’est de là que provient la limitation des registres des GeForce FX; 6 et 7 bien que sur ces dernières Nvidia ait élargi le pipeline de manière à ce que la majorité des pixels aient assez de place dans un étage.


La solution d’ATI à tous ces problèmes a été, avec les Radeon X1000, de découpler les pipelines de traitement des pixel shaders des unités de texturing. Le long pipeline n’est dès lors plus nécessaire puisque d’autres possibilités pour masquer la latence du texturing apparaissent. Il devient possible de travailler sur de très petits groupes de pixels et d’utiliser un nombre important de ces petits groupes, ou threads, pour obtenir le même résultat, sans les inconvénients. Dès qu’un petit groupe de pixels doit accéder à une texture, il sort de l’unité de traitement des pixel shaders et passe dans la file d’attente de l’unité de texturing. Parallèlement, un autre groupe est traité par l’unité de calcul des shaders. Dès qu’un groupe a reçu le résultat de l’accès à la texture demandée, il change de file d’attente et repasse dans celle des unités de calcul jusqu’à ce qu’il ait de nouveau besoin d’une texture, et ainsi de suite. Pour masquer la latence efficacement il faut pouvoir placer en file d’attente, ou au repos, un nombre important de threads et donc disposer d’une mémoire cache qui peut les accueillir. Plus elle est grande, plus une grande latence peut être tolérée. Par contre cette fois la file d’attente n’a plus une longueur fixe. Un nouveau thread est injecté dans le shader core uniquement si cela est nécessaire et bien entendu uniquement s’il reste de la place dans la mémoire cache pour l’accueillir.

Si la latence est faible, les threads passent rapidement d’une unité à l’autre et il n’est pas nécessaire d’en injecter un grand nombre dans le cœur de traitement des shaders. La mémoire cache qui les stocke est, elle, fixe bien entendu. Si elle est prévue pour contenir par exemple 128 threads mais que 32 suffisent à masquer la latence, 75% de la mémoire cache ne sert donc rien ? Nous en revenons au problème du nombre de registres temporaires. Le nombre qui en est utilisé est variable, ce qui veut dire que la taille des threads, en occupation mémoire est elle aussi variable. Vous l’aurez donc compris, ce type d’architecture permet de disposer d’un nombre élevé de registres temporaires sans réduire le débit des unités lorsqu’il n’y a pas une latence importante à masquer. Cette flexibilité permet au GPU d’utiliser le meilleur compromis entre le nombre de registres accessibles à pleine vitesse et la latence masquée alors que le long pipeline propose un modèle fixe en dehors duquel les performances chutent directement.
Vos réactions

Top articles