Différence entre un CPU et GPU

a marqué ce sujet comme résolu.

Bonjour,

Je m’intéresse de près à CUDA en ce moment et j’aimerai connaître la différence entre un CPU et un GPU. J’ai lu de nombreux articles sur internet comme quoi un GPU est spécialisé dans le rendu graphique et le CPU est généraliste. Mais aujourd’hui on retrouve des GPU dans des calculs de simulations, imagerie médicale…

Quelle est en 2021 la différence entre un GPU et un CPU ? Excepté le nombre de cœurs

Merci

Ce sont des processeurs, donc ils font des calculs. C’est à peu près la seule ressemblance.

Il faut comprendre qu’un GPU et un CPU, ça n’a pas le même but. Ils ont des jeux d’instructions différents (pas publique pour les GPU) et chaque processeur est optimisé pour certaines instructions propre à son but (alors même que le jeu d’instructions n’est pas le même).

Le CPU est optimisé pour le calcul de manière général, le GPU pour les calculs graphiques.

Après, il se trouve que c’est un processeur comme un autre, les instructions optimisés pour le graphisme peuvent être ré-utilisées dans d’autres domaines proches de l’imagerie (Imagerie médicale) ou d’autres moins proche qui demandent beaucoup de calculs en parallèle (Cassage de mot de passe, Simulations numériques complexe, …).

En contre partis, le GPU n’est absolument pas adapté dans beaucoup de cas.

Pour répondre simplement: La différence est essentiellement dans les instructions qui ont été optimisées. Et ça, ça joue beaucoup sur comment tu (enfin « tu », l’SE je veux dire) l’utilises ensuite.

+3 -0

Outre les points énoncés par Ache, la différence ne se situe pas tellement dans les capacités calculatoires du cœur, mais surtout dans l’architecture et la manière dont est approché le problème.

Le point central du GPU est que tout est orienté pour créer du parallélisme massif. Cela se traduit par des unités plus "simples" que ce que tu peux retrouver sur CPU. Tout plein de mécanismes qui ont été créés spécifiquement dans le but d’accélérer les performances du CPU ont été tout simplement rejetées par design (prefetching, branch prediction, …). Cela permet d’occuper moins de place, d’être moins consommateur en énergie, on peut alors mettre une quantité astronomique d’unités de calculs pour un moindre coût.

L’unité primordiale de calcul sur GPU est le core, il est conçu pour exécuter 32 threads en simultané, une warp, donc pour chaque instruction décodée, elle est appliquée sur chacun des threads (c’est l’idée du SIMD, mais on préfère parler de SIMT dans ce cadre). Cela a deux conséquences:

  • Les branchements conditionnels font que tu es obligé d’exécuter chacune des branches à la suite, en évinçant les résultats de tous les threads qui ont suivi l’autre branchement.
  • Une instruction est donc appliquée sur 32 données (au maximum) en même temps, et donc d’aller chercher énormément de données en mémoire - je reviendrai sur ce point.

A plus haut niveau, tu as les thread blocks, il s’agit un ensemble de warp exécutés sur un même streaming processor. En approximation, ce sont des warps qui sont schédulés ensemble sur les mêmes core et qui peuvent communiquer par le biais d’une mémoire __local__.

Enfin, l’ensemble des thread blocks forme un kernel qui est exécuté sur ton device et qui partage toute la VRAM de ta carte graphique, qui est __global__.

La relation entre le niveau d’exécution et la mémoire employée est capitale dans le processus GPU. Plus tu essayes d’accéder à de la mémoire "éloignée" et globale, plus ta pénalité est élevée. Les facteurs étaient originellement extrêmement élevés (facilement un facteur 1000 entre les registres et la __global__) - la situation s’est nettement améliorée à ce niveau mais la mémoire registre reste toujours un petit bolide.

Pour palier à cette lenteur, à cette latence qui existe entre la demande d’une donnée et la capacité de pouvoir la traiter, la solution est apportée par le scheduling des warps, la nature même massivement multithreaded fait qu’il existe toujours une warp qu’on peut exécuter en attendant la donnée. Attention que comme tu peux avoir 32 threads qui demandent chacun d’accéder à 32 données différentes et placées à divers endroits en mémoire, cela peut prendre un temps conséquent avant de pouvoir continuer. C’est pour ça qu’on préconise d’accéder à la mémoire par le biais de bank, qui correspondent à des cache lines, des paquets de 128 octets, tu peux accéder à un bank par cycle dans l’idée.

Comme la mémoire et la capacité à fournir l’information suffisamment rapidement est le nerf de la guerre sur GPU, cela se traduit également par deux phénomènes embêtants:

  • Tu n’as pas de stack. Le stack consiste (approximativement) en 32 registres de 4 bytes, si tu as besoin de davantage de mémoire pour stocker tes variables locales ou, pire, ton pointer stack pour savoir dans quelle fonction tu es, tu es obligé d’aller emprunter de la mémoire locale qui est nettement plus lente.
  • la "synchronisation" des données. Vu le niveau de concurrence proposé sur GPU, il devient très simple de commettre des erreurs de synchronisation et d’accéder à de la mémoire qui est en train d’être employée par d’autres, et ce même au niveau des registres ! Tu dois forcer des fences pour garantir la vision globale des données présentes en mémoire locale ou globale, ce qui est particulièrement lent.
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte