Wikilivres
frwikibooks
https://fr.wikibooks.org/wiki/Accueil
MediaWiki 1.45.0-wmf.7
first-letter
Média
Spécial
Discussion
Utilisateur
Discussion utilisateur
Wikilivres
Discussion Wikilivres
Fichier
Discussion fichier
MediaWiki
Discussion MediaWiki
Modèle
Discussion modèle
Aide
Discussion aide
Catégorie
Discussion catégorie
Transwiki
Discussion Transwiki
Wikijunior
Discussion Wikijunior
TimedText
TimedText talk
Module
Discussion module
Wikijunior:Les félins/Les lynx
102
8430
745335
445209
2025-06-25T05:31:48Z
24.206.98.141
/* Comment les lynx éduquent-ils leurs petits ? */
745335
wikitext
text/x-wiki
{{Les félins}}
== Les lynx ==
Le lynx ressemble à un gros chat de 70 cm (pour le lynx roux ou ''bobcat'') à 130 cm (pour le lynx d'Eurasie) avec des pattes courtes et des pieds en forme de raquettes pour mieux se déplacer dans la neige.
Les lynx ne sont pas aussi nobles que les lions ou bien puissants comme des tigres. Ce sont des prédateurs moyens, trop grands pour s'amuser avec des souris et trop petits pour s'attaquer à un cerf commun adulte. Mais ils arrivent plutôt bien à garder le contrôle sur des lapins. Avec leurs longues jambes et leur courte queue, il sont très bien équipés pour attraper n'importe quoi d'un lapin minuscule à un lièvre énorme.
<!-- These odd-shaped, ruff-faced cats are not as lordly as the lion or powerful as the tiger. They are mid-sized predators, too big to bother with mice and too small to tackle a healthy adult deer. But they do one job rather well--keeping rabbits in control. With their long legs and short tails, they are superbly equipped to tackle anything from a tiny cottontail to a huge snowshoe hare. -->
=== Où vivent-ils ? ===
Le lynx vit en Asie, en Europe en Amérique, aux États-Unis et au Canada.
=== À quoi ressemblent-ils ? ===
Les lynx sont les seuls des félins à avoir une petite queue courte. Leur méthode de chasse est aussi très originale. Grâce à leurs yeux perçants -leurs yeux de lynx- ils voient leurs proies de loin et s'en approchent sans bruit jusqu'à pouvoir leur sauter dessus. Ils n'aiment pas courir. Le lynx est reconnaissable à sa queue courte et à ses poils au bout de ses oreilles.
=== Que mangent les lynx et comment chassent-ils ? ===
Il raffole des lapins, des lièvres, des écureuils, des serpents, des oiseaux qu'il attrape après un bond d'une détente foudroyante.
Il aime aussi les œufs d'oiseaux, qu'il n'hésite pas à aller chercher dans les endroits les plus inaccessibles.Parfois, poussé par la faim, le lynx attaque des cerfs ou des élans.
Après son repas, il part se reposer dans sa tanière.
Le lynx est un excellent chasseur et son attaque est rapide.Quand un animal passe, le lynx saute sur son dos et enfonce ses dents dans la chair.
=== Comment les lynx éduquent-ils leurs petits ? ===
C’est principalement la femelle qui s’occupe de l’éducation des petits. Après leur naissance, elle les allaite pendant plusieurs semaines grâce à ses mamelles. Ensuite, elle commence à leur apprendre à survivre en leur apportant des proies, comme des lièvres, des rongeurs, de jeunes chevreuils, des chamois, des oiseaux ou encore des lapins de garenne.
Les jeunes lynx observent leur mère et apprennent par imitation. Elle leur enseigne des techniques essentielles comme la chasse, le camouflage et la défense contre les prédateurs. Vers l’âge de 10 à 12 mois, ils deviennent capables de chasser seuls. C’est à ce moment-là qu’ils commencent à gagner en autonomie, même s’ils restent parfois un peu plus longtemps avec leur mère avant de devenir totalement indépendants.
=== Faits amusants ===
=== Classification ===
=== Plus d'informations ===
* {{WP|Lynx (mammifère)}}
[[Catégorie:Wikijunior:Les félins]]
[[ca:Viquijúnior:Grans felins/Linx]]
[[de:Wikijunior Großkatzen/ Luchs]]
[[en:Wikijunior:Big Cats/Lynx and bobcats]]
73hurwpjfhv2ushu2rtqdsx44rslcp0
745336
745335
2025-06-25T05:33:25Z
24.206.98.141
/* Que mangent les lynx et comment chassent-ils ? */
745336
wikitext
text/x-wiki
{{Les félins}}
== Les lynx ==
Le lynx ressemble à un gros chat de 70 cm (pour le lynx roux ou ''bobcat'') à 130 cm (pour le lynx d'Eurasie) avec des pattes courtes et des pieds en forme de raquettes pour mieux se déplacer dans la neige.
Les lynx ne sont pas aussi nobles que les lions ou bien puissants comme des tigres. Ce sont des prédateurs moyens, trop grands pour s'amuser avec des souris et trop petits pour s'attaquer à un cerf commun adulte. Mais ils arrivent plutôt bien à garder le contrôle sur des lapins. Avec leurs longues jambes et leur courte queue, il sont très bien équipés pour attraper n'importe quoi d'un lapin minuscule à un lièvre énorme.
<!-- These odd-shaped, ruff-faced cats are not as lordly as the lion or powerful as the tiger. They are mid-sized predators, too big to bother with mice and too small to tackle a healthy adult deer. But they do one job rather well--keeping rabbits in control. With their long legs and short tails, they are superbly equipped to tackle anything from a tiny cottontail to a huge snowshoe hare. -->
=== Où vivent-ils ? ===
Le lynx vit en Asie, en Europe en Amérique, aux États-Unis et au Canada.
=== À quoi ressemblent-ils ? ===
Les lynx sont les seuls des félins à avoir une petite queue courte. Leur méthode de chasse est aussi très originale. Grâce à leurs yeux perçants -leurs yeux de lynx- ils voient leurs proies de loin et s'en approchent sans bruit jusqu'à pouvoir leur sauter dessus. Ils n'aiment pas courir. Le lynx est reconnaissable à sa queue courte et à ses poils au bout de ses oreilles.
=== Que mangent les lynx et comment chassent-ils ? ===
Le lynx est un animal qui mange surtout de la viande. Il aime beaucoup les lapins, les lièvres, les écureuils, les oiseaux et parfois même les serpents. Il adore aussi les œufs d’oiseaux et n’hésite pas à grimper pour aller les chercher dans les nids, même dans les endroits difficiles à atteindre ! Quand il a très faim, il peut s’attaquer à de plus gros animaux, comme des chevreuils ou des cerfs.
Le lynx est un très bon chasseur. Il chasse seul, souvent le matin ou le soir. Il s’approche tout doucement de sa proie sans faire de bruit, puis saute d’un coup grâce à ses pattes très puissantes. Il attrape sa proie en lui sautant dessus et en la mordant pour l’attraper.
Une fois qu’il a bien mangé, le lynx va se reposer dans un endroit tranquille, souvent sa tanière.
=== Comment les lynx éduquent-ils leurs petits ? ===
C’est principalement la femelle qui s’occupe de l’éducation des petits. Après leur naissance, elle les allaite pendant plusieurs semaines grâce à ses mamelles. Ensuite, elle commence à leur apprendre à survivre en leur apportant des proies, comme des lièvres, des rongeurs, de jeunes chevreuils, des chamois, des oiseaux ou encore des lapins de garenne.
Les jeunes lynx observent leur mère et apprennent par imitation. Elle leur enseigne des techniques essentielles comme la chasse, le camouflage et la défense contre les prédateurs. Vers l’âge de 10 à 12 mois, ils deviennent capables de chasser seuls. C’est à ce moment-là qu’ils commencent à gagner en autonomie, même s’ils restent parfois un peu plus longtemps avec leur mère avant de devenir totalement indépendants.
=== Faits amusants ===
=== Classification ===
=== Plus d'informations ===
* {{WP|Lynx (mammifère)}}
[[Catégorie:Wikijunior:Les félins]]
[[ca:Viquijúnior:Grans felins/Linx]]
[[de:Wikijunior Großkatzen/ Luchs]]
[[en:Wikijunior:Big Cats/Lynx and bobcats]]
3mgstqljqb0iavkpzmf0t3egrwh7e0y
745337
745336
2025-06-25T05:35:42Z
24.206.98.141
/* Les lynx */
745337
wikitext
text/x-wiki
{{Les félins}}
== Les lynx ==
Le lynx ressemble à un très grand chat sauvage. Selon les espèces, il mesure entre 70 cm (comme le lynx roux ou bobcat) et 130 cm (comme le lynx d’Eurasie). Il a des pattes assez courtes mais très puissantes, et ses grands pieds ressemblent à des raquettes : cela l’aide à marcher facilement dans la neige sans s’enfoncer.
Le lynx n’est pas aussi grand que le lion ni aussi fort que le tigre, mais c’est un excellent chasseur. Il est trop grand pour jouer avec des souris et un peu trop petit pour attraper un grand cerf adulte. En revanche, il est parfait pour attraper des lapins, des lièvres et d’autres petits animaux.
Avec ses longues pattes, sa courte queue, ses oreilles pointues avec des touffes de poils noirs au bout, le lynx est rapide, agile et très discret. Cela lui permet de s’approcher sans bruit de ses proies… et de les attraper par surprise !
<!-- These odd-shaped, ruff-faced cats are not as lordly as the lion or powerful as the tiger. They are mid-sized predators, too big to bother with mice and too small to tackle a healthy adult deer. But they do one job rather well--keeping rabbits in control. With their long legs and short tails, they are superbly equipped to tackle anything from a tiny cottontail to a huge snowshoe hare. -->
=== Où vivent-ils ? ===
Le lynx vit en Asie, en Europe en Amérique, aux États-Unis et au Canada.
=== À quoi ressemblent-ils ? ===
Les lynx sont les seuls des félins à avoir une petite queue courte. Leur méthode de chasse est aussi très originale. Grâce à leurs yeux perçants -leurs yeux de lynx- ils voient leurs proies de loin et s'en approchent sans bruit jusqu'à pouvoir leur sauter dessus. Ils n'aiment pas courir. Le lynx est reconnaissable à sa queue courte et à ses poils au bout de ses oreilles.
=== Que mangent les lynx et comment chassent-ils ? ===
Le lynx est un animal qui mange surtout de la viande. Il aime beaucoup les lapins, les lièvres, les écureuils, les oiseaux et parfois même les serpents. Il adore aussi les œufs d’oiseaux et n’hésite pas à grimper pour aller les chercher dans les nids, même dans les endroits difficiles à atteindre ! Quand il a très faim, il peut s’attaquer à de plus gros animaux, comme des chevreuils ou des cerfs.
Le lynx est un très bon chasseur. Il chasse seul, souvent le matin ou le soir. Il s’approche tout doucement de sa proie sans faire de bruit, puis saute d’un coup grâce à ses pattes très puissantes. Il attrape sa proie en lui sautant dessus et en la mordant pour l’attraper.
Une fois qu’il a bien mangé, le lynx va se reposer dans un endroit tranquille, souvent sa tanière.
=== Comment les lynx éduquent-ils leurs petits ? ===
C’est principalement la femelle qui s’occupe de l’éducation des petits. Après leur naissance, elle les allaite pendant plusieurs semaines grâce à ses mamelles. Ensuite, elle commence à leur apprendre à survivre en leur apportant des proies, comme des lièvres, des rongeurs, de jeunes chevreuils, des chamois, des oiseaux ou encore des lapins de garenne.
Les jeunes lynx observent leur mère et apprennent par imitation. Elle leur enseigne des techniques essentielles comme la chasse, le camouflage et la défense contre les prédateurs. Vers l’âge de 10 à 12 mois, ils deviennent capables de chasser seuls. C’est à ce moment-là qu’ils commencent à gagner en autonomie, même s’ils restent parfois un peu plus longtemps avec leur mère avant de devenir totalement indépendants.
=== Faits amusants ===
=== Classification ===
=== Plus d'informations ===
* {{WP|Lynx (mammifère)}}
[[Catégorie:Wikijunior:Les félins]]
[[ca:Viquijúnior:Grans felins/Linx]]
[[de:Wikijunior Großkatzen/ Luchs]]
[[en:Wikijunior:Big Cats/Lynx and bobcats]]
ldh894tcy8eu8h0mi8odxfv5ys8xmro
745338
745337
2025-06-25T05:36:00Z
24.206.98.141
/* Les lynx */
745338
wikitext
text/x-wiki
{{Les félins}}
== Les lynx ==
Le lynx ressemble à un très grand chat sauvage. Selon les espèces, il mesure entre 70 cm (comme le lynx roux ou bobcat) et 130 cm (comme le lynx d’Eurasie). Il a des pattes assez courtes mais très puissantes, et ses grands pieds ressemblent à des raquettes : cela l’aide à marcher facilement dans la neige sans s’enfoncer.
Le lynx n’est pas aussi grand que le lion ni aussi fort que le tigre, mais c’est un excellent chasseur. Il est trop grand pour jouer avec des souris et un peu trop petit pour attraper un grand cerf adulte. En revanche, il est parfait pour attraper des lapins, des lièvres et d’autres petits animaux.
Avec ses longues pattes, sa courte queue, ses oreilles pointues avec des touffes de poils noirs au bout, le lynx est rapide, agile et très discret. Cela lui permet de s’approcher sans bruit de ses proies… et de les attraper par surprise !
=== Où vivent-ils ? ===
Le lynx vit en Asie, en Europe en Amérique, aux États-Unis et au Canada.
=== À quoi ressemblent-ils ? ===
Les lynx sont les seuls des félins à avoir une petite queue courte. Leur méthode de chasse est aussi très originale. Grâce à leurs yeux perçants -leurs yeux de lynx- ils voient leurs proies de loin et s'en approchent sans bruit jusqu'à pouvoir leur sauter dessus. Ils n'aiment pas courir. Le lynx est reconnaissable à sa queue courte et à ses poils au bout de ses oreilles.
=== Que mangent les lynx et comment chassent-ils ? ===
Le lynx est un animal qui mange surtout de la viande. Il aime beaucoup les lapins, les lièvres, les écureuils, les oiseaux et parfois même les serpents. Il adore aussi les œufs d’oiseaux et n’hésite pas à grimper pour aller les chercher dans les nids, même dans les endroits difficiles à atteindre ! Quand il a très faim, il peut s’attaquer à de plus gros animaux, comme des chevreuils ou des cerfs.
Le lynx est un très bon chasseur. Il chasse seul, souvent le matin ou le soir. Il s’approche tout doucement de sa proie sans faire de bruit, puis saute d’un coup grâce à ses pattes très puissantes. Il attrape sa proie en lui sautant dessus et en la mordant pour l’attraper.
Une fois qu’il a bien mangé, le lynx va se reposer dans un endroit tranquille, souvent sa tanière.
=== Comment les lynx éduquent-ils leurs petits ? ===
C’est principalement la femelle qui s’occupe de l’éducation des petits. Après leur naissance, elle les allaite pendant plusieurs semaines grâce à ses mamelles. Ensuite, elle commence à leur apprendre à survivre en leur apportant des proies, comme des lièvres, des rongeurs, de jeunes chevreuils, des chamois, des oiseaux ou encore des lapins de garenne.
Les jeunes lynx observent leur mère et apprennent par imitation. Elle leur enseigne des techniques essentielles comme la chasse, le camouflage et la défense contre les prédateurs. Vers l’âge de 10 à 12 mois, ils deviennent capables de chasser seuls. C’est à ce moment-là qu’ils commencent à gagner en autonomie, même s’ils restent parfois un peu plus longtemps avec leur mère avant de devenir totalement indépendants.
=== Faits amusants ===
=== Classification ===
=== Plus d'informations ===
* {{WP|Lynx (mammifère)}}
[[Catégorie:Wikijunior:Les félins]]
[[ca:Viquijúnior:Grans felins/Linx]]
[[de:Wikijunior Großkatzen/ Luchs]]
[[en:Wikijunior:Big Cats/Lynx and bobcats]]
p2ql1q681k3g4q6m0f9r38kzecufsdd
745339
745338
2025-06-25T05:36:59Z
24.206.98.141
/* Où vivent-ils ? */
745339
wikitext
text/x-wiki
{{Les félins}}
== Les lynx ==
Le lynx ressemble à un très grand chat sauvage. Selon les espèces, il mesure entre 70 cm (comme le lynx roux ou bobcat) et 130 cm (comme le lynx d’Eurasie). Il a des pattes assez courtes mais très puissantes, et ses grands pieds ressemblent à des raquettes : cela l’aide à marcher facilement dans la neige sans s’enfoncer.
Le lynx n’est pas aussi grand que le lion ni aussi fort que le tigre, mais c’est un excellent chasseur. Il est trop grand pour jouer avec des souris et un peu trop petit pour attraper un grand cerf adulte. En revanche, il est parfait pour attraper des lapins, des lièvres et d’autres petits animaux.
Avec ses longues pattes, sa courte queue, ses oreilles pointues avec des touffes de poils noirs au bout, le lynx est rapide, agile et très discret. Cela lui permet de s’approcher sans bruit de ses proies… et de les attraper par surprise !
=== Où vivent-ils ? ===
Les lynx vivent dans plusieurs parties du monde. On peut en trouver en Asie, en Europe et en Amérique du Nord, surtout aux États-Unis et au Canada.
Ils aiment vivre dans des endroits calmes, comme les forêts, les montagnes ou les régions froides avec beaucoup de neige. Ils choisissent souvent des lieux où il y a peu d’humains, pour être tranquilles et pouvoir chasser en paix.
=== À quoi ressemblent-ils ? ===
Les lynx sont les seuls des félins à avoir une petite queue courte. Leur méthode de chasse est aussi très originale. Grâce à leurs yeux perçants -leurs yeux de lynx- ils voient leurs proies de loin et s'en approchent sans bruit jusqu'à pouvoir leur sauter dessus. Ils n'aiment pas courir. Le lynx est reconnaissable à sa queue courte et à ses poils au bout de ses oreilles.
=== Que mangent les lynx et comment chassent-ils ? ===
Le lynx est un animal qui mange surtout de la viande. Il aime beaucoup les lapins, les lièvres, les écureuils, les oiseaux et parfois même les serpents. Il adore aussi les œufs d’oiseaux et n’hésite pas à grimper pour aller les chercher dans les nids, même dans les endroits difficiles à atteindre ! Quand il a très faim, il peut s’attaquer à de plus gros animaux, comme des chevreuils ou des cerfs.
Le lynx est un très bon chasseur. Il chasse seul, souvent le matin ou le soir. Il s’approche tout doucement de sa proie sans faire de bruit, puis saute d’un coup grâce à ses pattes très puissantes. Il attrape sa proie en lui sautant dessus et en la mordant pour l’attraper.
Une fois qu’il a bien mangé, le lynx va se reposer dans un endroit tranquille, souvent sa tanière.
=== Comment les lynx éduquent-ils leurs petits ? ===
C’est principalement la femelle qui s’occupe de l’éducation des petits. Après leur naissance, elle les allaite pendant plusieurs semaines grâce à ses mamelles. Ensuite, elle commence à leur apprendre à survivre en leur apportant des proies, comme des lièvres, des rongeurs, de jeunes chevreuils, des chamois, des oiseaux ou encore des lapins de garenne.
Les jeunes lynx observent leur mère et apprennent par imitation. Elle leur enseigne des techniques essentielles comme la chasse, le camouflage et la défense contre les prédateurs. Vers l’âge de 10 à 12 mois, ils deviennent capables de chasser seuls. C’est à ce moment-là qu’ils commencent à gagner en autonomie, même s’ils restent parfois un peu plus longtemps avec leur mère avant de devenir totalement indépendants.
=== Faits amusants ===
=== Classification ===
=== Plus d'informations ===
* {{WP|Lynx (mammifère)}}
[[Catégorie:Wikijunior:Les félins]]
[[ca:Viquijúnior:Grans felins/Linx]]
[[de:Wikijunior Großkatzen/ Luchs]]
[[en:Wikijunior:Big Cats/Lynx and bobcats]]
4fj6hfvpmgcbnj33legnqvh342l5t67
Wikijunior:Les félins/Le léopard blanc
102
49321
745340
560805
2025-06-25T05:40:42Z
24.206.98.141
/* Que mangent-ils et comment chassent-ils ? */
745340
wikitext
text/x-wiki
== Léopard des neiges ==
=== Où vivent-ils ? ===
<gallery>
Asie-fr.svg
</gallery>
Les léopards des neiges vivent dans des régions montagneuses d'Asie centrale : Mongolie, Russie, Chine ou Tibet.
=== À quoi ressemblent-ils ? ===
<gallery>
Leopard des neiges.jpg
</gallery>
Les léopards des neiges sont blancs avec des tâches noires.
=== Que mangent-ils et comment chassent-ils ? ===
Le léopard des neiges est un carnivore, ce qui veut dire qu’il mange de la viande. Il chasse surtout des animaux de montagne comme les moutons sauvages, les sangliers, les gazelles, les lièvres, les marmottes, les rennes, mais aussi des animaux plus rares comme les markhors, les tahrs ou les bodaks.
C’est un chasseur solitaire, très discret et très agile. Il s’approche doucement de sa proie sans se faire voir, puis bondit très vite pour l’attraper. Le léopard des neiges est capable de sauter très loin grâce à ses grandes pattes musclées !
Quand il a capturé un gros animal, il ne le mange pas en une seule fois. Il traîne sa proie dans un coin tranquille de son territoire et la garde pendant plusieurs jours pour en manger un peu chaque jour.
<gallery>
Snow leopard portrait-2010-07-09.jpg
</gallery>
lafqo7bakdhashn3jwk5cnq2ead95z6
745341
745340
2025-06-25T05:41:25Z
24.206.98.141
/* À quoi ressemblent-ils ? */
745341
wikitext
text/x-wiki
== Léopard des neiges ==
=== Où vivent-ils ? ===
<gallery>
Asie-fr.svg
</gallery>
Les léopards des neiges vivent dans des régions montagneuses d'Asie centrale : Mongolie, Russie, Chine ou Tibet.
=== À quoi ressemblent-ils ? ===
<gallery>
Leopard des neiges.jpg
</gallery>
Les léopards des neiges ont une belle fourrure blanche ou gris clair avec des taches noires en forme de rosettes. Leur pelage épais les protège du froid dans les montagnes où ils vivent.
Ils ont un corps musclé, une queue très longue et épaisse (presque aussi longue que leur corps !) qui leur sert à garder l’équilibre et à se réchauffer en l’enroulant autour d’eux comme une couverture.
Leur tête est ronde, avec de petites oreilles pour ne pas perdre trop de chaleur, et leurs yeux sont très perçants, ce qui les aide à bien voir leurs proies, même de loin.
=== Que mangent-ils et comment chassent-ils ? ===
Le léopard des neiges est un carnivore, ce qui veut dire qu’il mange de la viande. Il chasse surtout des animaux de montagne comme les moutons sauvages, les sangliers, les gazelles, les lièvres, les marmottes, les rennes, mais aussi des animaux plus rares comme les markhors, les tahrs ou les bodaks.
C’est un chasseur solitaire, très discret et très agile. Il s’approche doucement de sa proie sans se faire voir, puis bondit très vite pour l’attraper. Le léopard des neiges est capable de sauter très loin grâce à ses grandes pattes musclées !
Quand il a capturé un gros animal, il ne le mange pas en une seule fois. Il traîne sa proie dans un coin tranquille de son territoire et la garde pendant plusieurs jours pour en manger un peu chaque jour.
<gallery>
Snow leopard portrait-2010-07-09.jpg
</gallery>
eao0l4ai1p5gjvqdq2hwdz4kxzhzoz5
Les cartes graphiques/Le Video Display Controler
0
67397
745334
742492
2025-06-25T02:41:45Z
Mewtow
31375
745334
wikitext
text/x-wiki
Dans les années 70-80, un système vidéo pouvait être fabriqué de deux grandes manières différentes. La première concevait la carte d'affichage à partir de composants très simples, comme des portes logiques ou des transistors, à partir de zéro, sans réutiliser de matériel existant. De telles cartes vidéos avaient des performances et des fonctionnalités très variables, mais étaient très complexes à concevoir et coutaient cher.
La seconde catégorie utilisait des '''''Video Display Controler ''''' (VDC), des circuits déjà tout près, placés dans un boitier, produits en masse, qu'il suffisait de compléter avec une mémoire vidéo et quelques autres circuits pour obtenir un système vidéo. De tels circuits permettaient d'obtenir des performances décentes, voire très bonnes, pour un prix nettement inférieur. Les deux fonctionnent de la même manière, peu importe qu'il s'agisse d'un VDC ou d'un circuit fait main. Les deux contiennent globalement les mêmes circuits, ils fonctionnent de la même manière.
Dans le chapitre sur les cartes d'affichage, nous avons vu qu'une carte d'affichage contient trois à quatre circuits distincts : un ''framebuffer'', un circuit de contrôle, le circuit d’interfaçage électrique avec l'écran (le RAMDAC) et éventuellement une connexion avec le bus. Le VDC correspond au circuit de contrôle. Les fonctionnalités d'un VDC sont très variables. Ils s'occupent des choses de base, comme gérer la résolution, l'envoi de l'image à afficher à l'écran, ce genre de choses. Il ne s'occupe pas de la transmission avec le bus, il ne gère pas vraiment l’interfaçage électrique.
[[File:Architecture d'une carte d'affichage avec VDC.png|centre|vignette|upright=1.5|Architecture d'une carte d'affichage avec VDC]]
Si la plupart des VDC communiquent avec la mémoire vidéo, il existe quelques exceptions qui se débrouillent sans mémoire vidéo, comme les ''Video shifters'' dont nous parlerons dans quelques chapitres. La meilleure manière d'aborder les VDC est de d'abord les voir comme des espèces de boite noire, dont on ne se préoccupe pas du contenu en premier lieu. Un VDC communique avec l'écran, le processeur et avec la mémoire vidéo. Dans ce chapitre, nous allons voir comment il communique avec l'écran et le processeur. Nous laissons de côté l'interface avec la mémoire vidéo, car elle dépend du VDC et n'est pas la même selon que la carte d'affichage utilise ou non un ''framebuffer''.
Le tout est illustré ci-dessous. L'interface VDC-écran correspond aux flèches en rouge et sera vue dans la première section. L'interface VDC-processeur correspond aux flèches en bleu et est le sujet de la seconde section. Enfin, l'interface entre mémoire vidéo et processeur correspond aux flèches en vert. En soi, elle n'est pas liée directement au VDC, mais nous allons quand même la voir dans ce chapitre.
[[File:Interface VDC.png|centre|vignette|upright=2|Interface d'un VDC.]]
==L'interface du VDC avec l'écran==
[[File:Array2.svg|vignette|upright=0.5|Coordonnées d'un pixel à l'écran.]]
Un écran est considéré par la carte graphique comme un tableau de pixels, organisé en lignes et en colonnes. Les écrans LCD sont bel et bien conçus comme cela, c'est plus compliqué sur les écrans CRT, mais cela ne change rien du point de vue de la carte graphique. Chaque pixel est localisé sur l'écran par deux coordonnées : sa position en largeur et en hauteur. Par convention, on suppose que le pixel de coordonnées (0,0) est celui situé tout haut et tout à gauche de l'écran. Le pixel de coordonnées (X,Y) est situé sur la X-ème colonne et la Y-ème ligne. Le tout est illustré ci-contre.
===Le balayage progressif et l'entrelacement===
L'écran peut afficher une image en utilisant deux modes principaux : le balayage progressif, et le balayage entrelacé.
Avec le '''balayage progressif''', la carte graphique doit envoyer les pixels ligne par ligne, colonne par colonne : de haut en bas et de gauche à droite. Le balayage progressif est utilisé sur tous les écrans LCD moderne, mais il était plus adapté aux écrans CRT. Sur les écrans plats, l'image est transmise à l'écran, mais est affichée une fois qu'elle est intégralement reçue, d'un seul coup. Mais sur les anciens écrans de télévision, les choses étaient différentes.
Les vieux écrans CRT fonctionnaient sur ce principe : un canon à électrons balayait l'écran en commençant en haut à gauche, et balayait l'écran ligne par ligne. Ce ''scan progressif'' de l'image faisait apparaître l'image progressivement et profitait de la persistance rétinienne pour former une image fixe. L'image était donc affichée en même temps qu'elle était envoyée et le scan progressif correspondait à l'ordre d'allumage des pixels à l'écran.
[[File:CRT color.png|centre|vignette|upright=2|Intérieur d'un écran CRT. En 1, on voit le canon à électron. En 2, on voit le faisceau d'électron associé à chaque couleur. En 3, les faisceaux d'électrons sont déviés par des électroaimants, pour atterrir sur le pixel à éclairer. En 4, le faisceau d'électrons frappe la surface de l'écran, composée de phosphore, qui s'illumine alors. En 5, on voit que les trois faisceaux ne frappent exactement au même endroit : l'un frappe sur une zone colorée en bleu, l'autre sur du vert, l'autre sur du rouge. Les trois zones combinées affichent une couleur par mélange du rouge, du vert et du bleu. Ne vous trompez pas : le faisceau d'électron n'a pas de couleur, comme indiqué sur le schéma, la couleur a été ajoutée pour faire comprendre qu'un faisceau est dirigé sur les pixels rouges, un autre sur les pixels bleus, et l'autre sur les pixels verts.]]
[[File:CRT image creation animation.gif|vignette|upright=0.5|Illustration de l'entrelacement.]]
La technique du balayage progressif n'avait pas de défauts particuliers, ce qui fait que tous les écrans d’ordinateurs CRT l'utilisait. Mais les télévisions de l'époque utilisaient une méthode différente, appelée l''''entrelacement'''. Avec elle, l'écran faisait un scan pour les lignes paires, suivi par un scan pour les lignes impaires. Le tout est illustré dans l'animation ci-contre.
[[File:Interlace zoom.gif|vignette|Illustration de l'entrelacement et de ses effets sur la perception.]]
L'entrelacement donne l'illusion de doubler la fréquence d'affichage, ce qui est très utile sur les écrans à faible fréquence de rafraîchissement. Pour comprendre pourquoi, il faut comparer ce qui se passe entre un écran à scan progressif non-entrelacé et un écran entrelacé. Avec l'écran non-entrelacé, l'image met un certain temps à s'afficher, qui correspond au temps que met le canon à électron à balayer la totalité de l'écran, ligne par ligne. Avec l'entrelacement, le temps mis pour balayer l'écran est le même, car le nombre de lignes à balayer reste le même, seul l'ordre change.
Sur l'écran entrelacé, l'image s'affiche à moitié une première fois (sur les lignes paires) avant que l'image complète s'affiche. La moitié d'image affichée par l'écran entrelacé a une résolution suffisante pour que le cerveau humain soit trompé et perçoive une image presque complète. En clair, le cerveau verra deux images par balayage complet : une image partielle lors du balayage des lignes paires et une image complète lors du balayage des lignes impaires. Sans entrelacement, le cerveau ne verra qu'une seule image lors de chaque balayage complet.
L'effet est d'autant plus important que la résolution verticale (le nombre de lignes) est important. De plus, l'effet est encore plus important si l'ordinateur calcule un grand nombre d'images par secondes. Par exemple, pour un écran avec une fréquence de rafraîchissement de 60 Hz et un jeu vidéo qui tourne deux fois plus vite (à 120 images par secondes, donc), l'image sur les lignes impaires sera plus récente que celle sur les lignes paires. Le cerveau humain sera sensible à cela et verra une image plus fluide (bien qu'imparfaitement fluide).
Le nombre de lignes est toujours impair (normes analogiques : 625 en Europe, 525 en Amérique), ce qui fait un nombre non entier de lignes pour chacune des 2 trames (impaires et paires). Par exemple, pour 625 lignes cela fait {{formatnum:312.5}} lignes par trame. Le balayage vertical étant progressif durant le balayage horizontal, les lignes sont imperceptiblement penchées. À la fin du balayage d'une trame, le rayon se retrouve au milieu de la ligne horizontale, soit un décalage vertical d'une demie-ligne (voir image ci-dessous).
[[Fichier:Balayage entrelace affichage trames.svg|center|500px|class=transparent|Entrelacement sur tube cathodique.]]
===La fréquence de rafraichissement===
Même si cela commence à changer de nos jours, l'écran affiche un certain nombre d'images par secondes, le nombre en question étant désigné sous le terme de '''fréquence de rafraîchissement'''. Pour un écran avec une fréquence de rafraîchissement de 60 Hz (60 images par secondes), la carte graphique doit envoyer une nouvelle image tous les (1 seconde / 60) = 16,666... millisecondes.
Sur les écrans LCD, la fréquence de rafraîchissement ne dépend pas de la résolution utilisée, en raison de différences de technologie. Sur les anciens écrans CRT, la fréquence de rafraîchissement dépendait de la résolution utilisée, et la carte d'affichage devait alors gérer le couple résolution-fréquence elle-même et la gestion de la fréquence de rafraîchissement était donc plus compliquée.
Depuis environ 2016, quelques écrans supportent une '''fréquence de rafraichissement variable'''. Variable dans le sens : peut varier entre une fréquence minimale et une fréquence maximale selon les besoins. L'écran reçoit des images de part de la carte graphique, et les affiche immédiatement, sans attendre un signal de synchronisation vertical de fréquence fixe. Tant que la carte d'affichage ne va pas trop vite, l'écran suit, il affiche les images dès qu'il les reçoit. Par contre, au-delà d'un certain flux d'image, il bloque à une fréquence de rafraichissement maximale.
Les bénéfices d'une fréquence de rafraichissement variable sont nombreux. Déjà, le temps de latence est réduit, l'''input lag'' si cher aux joueurs compétitifs est réduit de quelques millisecondes. De plus, la qualité d'image est améliorée du fait de l'absence de ''screen tearing'' sur lequel on reviendra plus tard.
===La gestion des timings pour la communication avec l'écran===
Le câble qui relie la carte graphique à l'écran transmet au mieux un seul pixel à la fois, voire un seul bit à la fois. On ne peut pas envoyer l'image d'un seul coup à l'écran, et on doit l'envoyer pixel par pixel. L'écran traite alors ce flux de pixels de deux manières différentes. Dans le cas des écrans LCD, le plus intuitif, l'écran accumule les pixels reçus dans une mémoire tampon et affiche l'image une fois qu'elle est totalement reçue. Pour les écrans CRT, l'écran affiche les pixels reçus immédiatement dès leur réception sur l'entrée. Dans les deux cas, il faut envoyer les pixels dans un certain ordre bien précis.
Un point important est que la carte graphique ne peut pas envoyer un flux de pixels n'importe quand et doit respecter des timings bien précis. Le flux de pixel envoyé à l'écran est souvent structuré d'une certaine manière, avec des temps de pause, un temps de maintien minimum pour chaque pixel, etc.
Déjà, il faut tenir compte des timings liés à la transmission de l'image elle-même. La carte graphique doit envoyer les pixels avec des timings tout aussi stricts, qui dépendent du standard vidéo utilisé. Chaque pixel doit être maintenu durant un certain temps bien précis, il y a un certain temps entre la transmission de deux pixels, etc. Et le circuit d’interfaçage doit gérer le '''temps de transmission d'un pixel'''. Pour cela, le VDC envoie un signal d'horloge dont la période correspond au temps de transmission/affichage d'un pixel. En, clair, le VDC envoie un pixel à chaque cycle d'horloge.
Ensuite, il faut prévenir l'écran qu'on a fini de transmettre une image avec un '''signal de synchronisation verticale''', qui indiquait à l'écran qu'une image entière vient d'être transmise. Le VDC transmet l'image pixel par pixel, et lève ce signal de synchronisation verticale une fois l'image intégralement transmise. Ce signal était transmis sur un fil spécialisé, qu'on trouve sur la plupart des connecteurs VGA. De nos jours, sur les standards HDMI, DisplayPort, et autres, les choses sont plus compliquées, mais ce signal est quand même transmis, bien que pas forcément sur un fil spécialisé.
Enfin, il faut aussi tenir compte d'autres timings pour gérer la résolution. Les pixels sont envoyés ligne par ligne, mais une ligne de pixel n'a pas la même taille suivant la résolution : 640 pixels pour du 640 × 480, 1280 pour du 1280 × 1024, etc. La carte graphique doit donc indiquer quand commencent et se terminent chaque ligne dans le flux de pixels. Sans cela, on ne pourrait pas gérer des résolutions différentes. Pour cela, le VDC envoie un '''signal de synchronisation horizontale''' une fois qu'il a fini d'envoyer une ligne.
En tout, cela fait au minimum trois signaux : une horloge pour la transmission des pixels, un signal de synchronisation verticale, et un signal de synchronisation horizontale. Sans cela, impossible d'envoyer des pixels à l'écran ou de gérer la résolution convenablement. Et il y a d'autres contraintes de timings dont nous parlerons plus bas, qui ne sont pas évidentes pour le moment. Par exemple, sur les écrans CRT, il y a un temps de latence à la fin d'une ligne pour que le canon à électron se déplace sur le début de la ligne suivante. Et cela impose de ne pas démarrer l'envoie de la ligne suivante avant un certain temps. Cela il n'existe plus sur les écrans LCD, mais il fallait le prendre en compte à l'époque.
===L'exemple du standard VGA===
Un bon exemple est le standard VGA, qui était le seul utilisé pour connecter les écrans CRT, mais qui est encore utilisé de nos jours sur les écrans LCD. Avec ce standard, le connecteur contenait trois fils R, G, et B pour envoyer la couleur, codée en analogique. Il existait un fil H-SYNC pour indiquer qu'on transmettait une nouvelle ligne et un fil V-SYNC pour indiquer qu'on envoie une nouvelle image. Une nouvelle ligne ou image est indiquée en mettant un 0 sur le fil adéquat. Jusque là, rien de surprenant, c'est une redite de ce qu'on a dit plus haut. On trouve aussi plusieurs fils pour la masse, à savoir le 0 Volt, ainsi qu'une tension d'alimentation. Il y a une masse générale, ainsi que plusieurs masses, une par signal RGB.
Et enfin, il faut citer la connexion DDE/DDC qui permet de communiquer des informations de configuration à l'écran. Quand vous branchez l'écran à une carte graphique, celle-ci communique avec l'écran pour savoir quelles sont les résolutions supportées, quelle fréquence de rafraichissement est supporté, si l'écran supporte des couleurs 32 bits, etc. Sans cela, impossible de configurer la résolution. Pour cela, l'écran contient une petite mémoire ROM, dont le contenu est standardisé, qui contient toutes les informations nécessaires pour configurer l'écran.LA carte graphique lit cette ROM en passant par un bus appelé le bus '''''Display Data Channel''''', qui permet à la carte graphique de lire cette ROM, d'interroger l'écran sur les résolutions et fonctionnalités supportées. Le bus est un dérivé du bus I²c, et a trois fils dédiés : un pour l'horloge, l'autre pour la transmission des données, et une masse dédiée.
[[File:EMacVGA.png|centre|vignette|upright=2|Connecteurs VGA]]
Les premières subtilités du standard VGA viennent des timings des signaux HSYCN et VSYNC. Le signal HSYNC n'est pas envoyé dès la fin de la ligne : il y a un temps d'attente de quelques microsecondes entre la fin de la ligne et l'envoie du signal HSYNC. Le signal HSYNC est maintenu durant quelques microsecondes, la durée d'envoi est fixe. Puis, on a encore un nouveau temps d'attente avant l'envoi de la prochaine ligne, durant lequel le signal HSYNC n'est pas envoyé. Durant ces trois périodes (deux temps d'attentes, envoi de HSYNC), aucun pixel n'est envoyé à l'écran.
[[File:VGA 640×480 horizontal timings.svg|centre|vignette|upright=2|VGA 640×480 horizontal timings. Les durées vertes et jaunes sont des temps d'attentes où rien n'est envoyé sur le connecteur, rouge correspond à l'envoi du signal HSYCN, bleu est l'envoi de la ligne.]]
Et il y a la même chose avec les signaux VSYNC, même si les timings sont différents. On devait attendre un certain temps entre la transmission de deux lignes, ce qui introduisait des vides dans le flux de pixels. Même chose entre deux images, sauf que le temps d'attente était plus long que le temps d'attente entre deux lignes. Le tout est détaillé dans le schéma ci-dessous, qui détaille le cas pour une résolution de 640 par 480.
[[File:VGASpecification.png|centre|vignette|upright=2|Standard VGA : spécification des temps d'attentes entre deux lignes et deux images.]]
==Les registres d'interface processeur du VDC==
Pour le processeur, le VDC a une interface similaire à celle de n'importe quel périphérique : un paquet de registres, et éventuellement des mémoires SRAM intégrées. La mémoire vidéo peut être intégrée dans le VDP ou être séparée, les deux sont possibles. Mais nous allons partir du principe que la mémoire vidéo est séparée. Dans cette section, nous allons voir ce qui a trait aux échanges entre CPU et VDC proprement dit, la communication VDC-VRAM sera le sujet d'une section ultérieure.
===Les registres de configuration du VDC===
La programmation d'un VDC se fait par en configurant des registres de configuration interne, qui permettent de configurer la résolution, la fréquence d’affichage, la position du curseur de souris, etc. Le processeur a juste à écrire dans ces registres, pour configurer la carte d'affichage comme souhaité.
En général, les registres de configuration sont accessibles directement par le processeur. Quelques adresses mémoire sont détournées pour pointer, non pas vers la mémoire RAM, mais vers les registres de configuration. Ce n'est ni plus ni moins que la technique des '''entrée-sorties mappées en mémoire''', que vous connaissez sans doute si vous avez déjà lu un cours d'architecture des ordinateurs. Il y a typiquement une adresse mémoire par registre, le processeur a juste à écrire dans cette adresse pour configurer le registre.
Plus rarement, l'écriture des registres de configuration se fait via une adresse unique, partagée entre tous les registres de configuration. La configuration d'un registre se fait en deux temps : il écrit le numéro du registre à configurer, puis la donnée à écrire. La carte graphique reçoit ces deux informations l'une après l'autre, et les utilise pour configurer le registre elle-même.
===Le registre d'état du VDC===
Le VDC incorpore presque toujours un '''registre d'état''', ou un '''registre de statut''' qui permet au processeur de connaitre l'état du VDC. Il permet de savoir si le VDC est libre, s'il est en train d'afficher une ligne, si une erreur a eu lieu et laquelle. Le processeur a juste à lire le registre en question, pour vérifier l'état de la carte graphique. Chaque bit du registre de statut a une interprétation fixée à l'avance et fournit une information précise.
Plusieurs bits du registre de statut sont réservés au traitement des erreurs. Si le VDC rencontre une erreur, il met une valeur bien précise dans ces bits, appelée le '''code d'erreur'''. Typiquement, la valeur 0 indique qu'il n'y a pas d'erreur, les autres valeurs précisent une erreur. Le code d'erreur dépend de l'erreur en question et du VDC, il n'y a pas de standard pour ça.
==La synchronisation entre CPU et VDC pour l'accès à la RAM vidéo==
Un point qui va nous intéresser dans ce qui suit est la gestion des accès mémoire. Aussi bien le processeur que le VDC accèdent à la mémoire vidéo. Et ils ne faut pas qu'ils se marchent sur les pieds. Les deux ne peuvent pas accéder en même temps à la mémoire vidéo, ils doivent y accéder à tour de rôle. Et pour cela, divers mécanismes sont implémentés. Le mécanisme le plus simple est le suivant : quand le VDC lit des pixels à afficher en mémoire vidéo, ils prévient le processeur que la RAM vidéo est occupée. Le processeur attend alors que le VDC libère la RAM vidéo.
L'idée part du principe que l'affichage d'une image se fait à fréquence régulière. La carte d'affichage accède à la mémoire vidéo durant un certain temps pour envoyer l'image à l'écran, mais la laisse libre le reste du temps. Par exemple, sur un écran à 60 Hz, avec une image accédée toute les 16.66666 millisecondes, la carte d'affichage accède à la RAM vidéo pendant 5 à 10 millisecondes, le reste du temps est laissé au processeur.
De même, il y a un certain temps de libre entre l'affichage de deux lignes, le temps que le canon à électron du CRT se repositionne au début de la ligne suivante. Cela laissait un petit peu de temps au processeur pour changer la configuration de la carte graphique, par exemple pour changer la palette de couleur, changer des ''sprites'', écrire dans la mémoire vidéo, ou tout autre chose. Le tout est très utile pour rendre certains effets graphiques.
Si le processeur sait quand la carte d'affichage affiche une image/ligne à l'écran, il sait quand la mémoire est libre et peut alors accéder à la mémoire vidéo. Reste à indiquer au processeur que la carte d'affichage n'utilise pas la mémoire vidéo. Pour prévenir le processeur, deux méthodes sont utilisées : le ''pooling'' et les interruptions.
===La synchronisation CPU-VDC par ''pooling''===
La solution du ''pooling'' utilise le registre d'état de la carte d'affichage. Avant d’accéder à la mémoire vidéo, le processeur vérifiait ce registre pour savoir si le VDC accède à al mémoire vidéo. Si c'est le cas, le processeur attend que la mémoire vidéo soit libre. Sinon, le processeur accédait à la mémoire vidéo.
Pour cela, le registre de statut du VDC contient un bit qui précise que l'écran est en train d'afficher une ligne. Il est appelé le '''bit de ''blanking'' horizontal'''. En général, ce bit est à 0 quand le VDC est en train de transmettre une ligne à l'écran, à 1 quand la mémoire vidéo est libre. Notons que ce signal n'est pas équivalent au signal HSYNC. Pour reprendre l'exemple du standard VGA, il y a deux temps d'attente avant et après l'envoi du signal HSYNC, où l'écran n'envoie pas de données. Le signal HSYNC est alors à 0, alors que le bit de ''blanking'' est bien à 1.
===La synchronisation CPU-VDC via ''raster interrupts''===
L'usage d'un bit de ''blanking'' permet au VDC de prévenir le processeur qu'il ne peut pas écrire en RAM vidéo. Mais les VDC de ce type sont assez rudimentaires. Une autre méthode pour ce faire utilise une technique appelée les interruptions matérielles. Pour rappel, les '''interruptions''' sont des fonctionnalités du processeur, qui interrompent temporairement l’exécution d'un programme pour réagir à un événement extérieur (matériel, erreur fatale d’exécution d'un programme…). Lors d'une interruption, le processeur suit la procédure suivante :
* arrête l'exécution du programme en cours et sauvegarde l'état du processeur (registres et ''program counter'') ;
* exécute un petit programme nommé '''routine d'interruption''' ;
* restaure l'état du programme sauvegardé afin de reprendre l'exécution de son programme là ou il en était.
[[File:Interruption processeur.png|centre|vignette|upright=2|Interruption processeur]]
Les interruptions matérielles, aussi appelées '''IRQ''', sont des interruptions déclenchées par un périphérique et ce sont celles qui vont nous intéresser dans ce qui suit. Les IRQ qui nous intéressent sont générées par la carte graphique quand c'est nécessaire. Pour que la carte graphique puisse déclencher une interruption sur le processeur, on a juste besoin de la connecter à une entrée sur le processeur, appelée l''''entrée d'interruption''', souvent notée INTR ou INT. Lorsque la carte graphique envoie un 1 dessus, le processeur passe en mode interruption.
: Si vous avez déjà lu un cours d'architecture des ordinateurs, vous savez sans doute que les choses sont assez compliquées, qu'un ordinateur moderne contient un contrôleur d'interruption pour gérer les interruptions de plusieurs périphériques, mais nous n'avons pas besoin de parler de tout cela ici. Nous avons juste besoin de voir le cas simple où la carte graphique est connectée directement sur le processeur.
Les cartes graphiques d'antan géraient plusieurs types d'interruptions, qui sont regroupées sous le terme de '''''Raster Interrupt'''''. Grâce à ces interruptions, le processeur sait quand la mémoire vidéo est libre.
La plus importante s'appelle la '''''Vertical blank interrupt''''' (VBI). Elle indique que la carte graphique a fini d'afficher une image et servait à implémenter la synchronisation verticale. La ''Vertical blank interrupt'' elle était parfois utilisée pour d'autres choses qui n'ont rien à voir avec l'écran ou le rôle d'une carte graphique. Par exemple, sur les anciens ordinateurs qui ne disposaient pas de ''timers'' sur la carte mère, la VBI était utilisée pour timer les échanges avec le clavier et la souris. A chaque VBI, la routine d'interruption vérifiait si le clavier ou la souris avaient envoyé quelque chose à l'ordinateur.
Le VDC contient donc une sortie dédiée aux interruptions, connectée à l'entrée d'interruption du CPU (directement ou par l'intermédiaire d'un contrôleur d'interruption). Les signaux de ''raster interrupt'' ne sont pas identiques aux signaux de synchronisation verticale et horizontale, ni aux signaux de ''blanking'', même s'ils se ressemblent. La différence est que les signaux de synchronisation verticale/horizontale ont des contraintes de timing différents. Par exemple, le standard VGA impose que ces deux signaux soient maintenus durant un certain temps à l'écran, alors que les ''raster interrupts'' sont remises à zéro dès que le processeur est a pris en compte.
==L'interface processeur - mémoire vidéo==
L'usage de ''raster interrupts'' est très efficace, mais a pour défaut de beaucoup utiliser le processeur. Diverses optimisations permettent de se passer de ''raster interrupts'', ou du moins d'en réduire le cout en performance. Mais ces techniques demandent de modifier la mémoire vidéo, précisément la manière dont le processeur communique avec la mémoire vidéo. Nous allons voir ces techniques dans cette section.
===Les mémoires vidéo double port===
Sur les premières consoles de jeu et les premières cartes graphiques, le ''framebuffer'' était mémorisé dans une mémoire vidéo spécialisée appelée une '''mémoire vidéo double port'''. Par double port, on veut dire qu'elles avaient deux entrée-sorties sur lesquelles on pouvait lire ou écrire leur contenu simultanément.
Le premier port était connecté au processeur ou à la carte graphique, alors que le second port était connecté à un écran CRT. Aussi, nous appellerons ces deux port le ''port CPU/GPU'' et l'autre sera appelé le ''port CRT''. Le premier port était utilisé pour enregistrer l'image à calculer et faire les calculs, alors que le second port était utilisé pour envoyer à l'écran l'image à afficher. Le port CPU/GPU est tout ce qu'il y a de plus normal : on peut lire ou écrire des données, en précisant l'adresse mémoire de la donnée, rien de compliqué. Le port CRT est assez original : il permet d'envoyer un paquet de données bit par bit.
De telles mémoires étaient des mémoires dont le support de stockage était organisé en ligne et colonnes. Une ligne à l'intérieur de la mémoire correspond à une ligne de pixel à l'écran, ce qui se marie bien avec le fait que les anciens écrans CRT affichaient les images ligne par ligne. L'envoi d'une ligne à l'écran se fait bit par bit, sur un câble assez simple comme un câble VGA ou autre. Le second port permettait de faire cela automatiquement, en permettant de lire une ligne bit par bit, les bits étant envoyés l'un après l'autre automatiquement.
Pour cela, les mémoires vidéo double port incorporaient un registre capable de stocker une ligne entière. Le registre en question était un registre à décalage, à savoir un registre dont le contenu est décalé d'un rang à chaque cycle d'horloge. Le bit sortant est récupéré sur une sortie du registre, sortie qui était directement connectée au port CRT. Lors de l'accès au second port, la carte graphique fournissait un numéro de ligne et la ligne était chargée dans le tampon de ligne associé à l'écran. La carte graphique envoyait un signal d'horloge de même fréquence que l'écran, qui commandait le tampon de ligne à décalage : un bit sortait à chaque cycle d'écran et les bits étaient envoyé dans le bon ordre.
===Le multiplexage temporel des accès mémoire===
Les mémoires double port n'étaient pas si rares, mais elles n'étaient pas la solution la plus utilisée. La majorité des micro-ordinateurs et consoles utilisaient une mémoire vidéo normale, simple port, bien plus courante et bien moins chère. Mais il ajoutaient de circuits annexes ou utilisaient des ruses pour éviter que le processeur et la carte d'affichage se marchent sur les pieds. L'idée est de garantir que le processeur et la carte d'affichage n'accèdent pas à la mémoire en même temps. On parle de '''multiplexage temporel'''.
Un première mise en œuvre fait en sorte que la moitié des cycles d'horloge de la mémoire soit réservé au processeur, l'autre à la carte d'affichage. En clair, on change d’utilisateur à chaque cycle : si un cycle est attribué au processeur, le suivant l'est à la carte d'affichage. L'implémentation la plus simple utilise une mémoire qui va à une fréquence double de celle du processeur et de la carte d'affichage, les deux étant cadencés à la même fréquence. Un exemple est celui du micro-ordinateur BBC Micro, qui avait une fréquence de 4 MHz avec un processeur à 2 MHz et une carte d'affichage de 2 MHz lui aussi. Les fréquences du CPU et de la carte d'affichage étaient décalées d'une moitié de cycle, ce qui fait que leurs cycles correspondaient à des cycles mémoire différents. Le défaut est que cette technique demande une RAM très rapide, ce qui est un un gros problème.
Une autre solution laissait le processeur accéder en permanence à la mémoire vidéo. La carte d'affichage ne peut pas accéder à la mémoire vidéo quand le CPU écrit dedans, car des circuits annexes désactivent la carte d'affichage quand le processeur écrit dedans. Le micro-ordinateur TRS-80 faisait ainsi. Un défaut de cette méthode est qu'elle cause des artefacts graphiques à l'écran. Des pixels ne sont pas affichés et des écritures processeur trop longues peuvent causer des lignes noires à l'écran.
Enfin, une autre solution utilisait les mécanismes d'arbitrage du bus, qui gèrent les accès concurrents sur un bus. Le processeur et la mémoire sont reliés à la mémoire par le même ensemble de fils, et non par des ports séparés. La carte d'affichage et la mémoire envoient des demandes d'accès mémoire sur le bus, et elles sont ou non acceptées selon l'état de la mémoire. La carte d'affichage a la priorité, ce qui fait que si le processeur lance une demande d'accès à la mémoire pendant que la carte d'affichage y accède, le bus lui envoie un signal indiquant que le bus est occupé. Le processeur se met en attente tant que ce signal est à 1.
===L'usage de tampons de synchronisation FIFO===
Une dernière solution est l'usage de mémoires tampon entre le processeur et la mémoire vidéo. Le processeur n'écrivait pas directement dans la mémoire vidéo, mais dans une mémoire intermédiaire. La mémoire intermédiaire est une '''mémoire FIFO''', à savoir qu'elle mémorise les données à écrire et leur adresse dans leur ordre d'arrivée. Elle sert à mettre en attente les accès mémoire du processeur tant que la mémoire vidéo est occupée.
Ainsi, si la mémoire vidéo est libre, le processeur peut écrire directement dans la mémoire vidéo, sans intermédiaire. Mais si la carte d'affichage accède à la mémoire vidéo, les écritures du processeur sont mises en attente dans la mémoire FIFO. Elles s'accumulent tant que la mémoire vidéo est occupée, elles sont conservées dans l'ordre d'envoi par le processeur. Dès que la mémoire vidéo se libère, les données présentes dans la FIFO sont écrites dans la mémoire vidéo, au rythme d'une écriture par cycle d'horloge de la VRAM : la mémoire FIFO se vide progressivement.
Si la mémoire FIFO est pleine, elle prévient le processeur en lui envoyant un bit/signal, et le processeur agit en conséquence en cessant les écritures et en se mettant en pause.
[[File:FIFO d'écriture en mémoire vidéo.png|centre|vignette|upright=2|FIFO d'écriture en mémoire vidéo]]
Sur les cartes d'affichage, le processeur n'adresse pas la mémoire vidéo directement. A la place, le processeur envoie des données sur le bus, sur le connecteur de la carte d'affichage. La carte d'affichage récupère les données transmises sur le bus et les mets en attente dans une mémoire FIFO assez similaire. Elle les écrit en mémoire vidéo si besoin quand elle est libre. En conséquence, les cartes graphiques modernes n'ont pas besoin de ''raster interrupts'', qui étaient utilisées sur les premiers PC ou les premières consoles. A la place, c'est la carte graphique qui s'occupe de tout, et notamment son circuit de contrôle qui gère la mémoire vidéo. D'ailleurs, c'est ce circuit de contrôle qui gère la synchronisation verticale, pas le processeur, pas besoin de ''vertical blanking interrupt''.
==La génération des signaux de commande pour l'écran==
Les VDC contiennent tous de quoi générer les signaux de commande à destination de l'écran, ainsi que des signaux d'interruption à destination du processeur. Le premier signal à générer est le signal d'horloge transmission des pixels, à savoir le signal d'horloge dont la période est égale au temps mis pour envoyer un pixel à l'écran. Ce signal est souvent transmis à l'écran, via un fil dédié. Les VDC contiennent de quoi générer cette fréquence, grâce à un circuit oscillateur dédié.
Il faut aussi générer les signaux de synchronisation verticale/horizontale, ainsi que les ''raster interrupts''. Et ils se trouve que les deux sont générés par les mêmes circuits, à peu de choses près. Dans ce qui va suivre, nous allons voir comment sont générés ces signaux, quels sont les circuits qui s'en chargent. Ils sont assez simples : ce sont de simples compteurs reliés à des comparateurs !
===La génération des signaux de synchronisation verticale/horizontale===
Le VDC gère les signaux de synchronisation verticale ou horizontale. Pour cela, ils intègrent deux compteurs (des circuits qui comptent de 0 à N). Le premier compteur compte les lignes transmises, l'autre les pixels dans une ligne, ce qui leur vaut les noms de compteur de colonne et de compteur de ligne. Les deux compteurs sont initialisés à 0 avant la transmission et sont incrémentés automatiquement quand on passe d'un pixel à l'autre, ou bien d'une ligne à l'autre. Quand le compteur atteint la valeur adéquate, il émet un signal de synchronisation verticale/horizontale. Au passage à la ligne suivante, le compteur de colonne est réinitialisé à 0, idem pour le compteur de ligne quand une image a été affichée totalement.
Ils sont configurés de manière à prendre en compte la résolution de l'écran, mais pas de la manière dont vous le pensez. Par exemple, pour une résolution de 640 par 480 : vous imaginez sans doute que le compteur de colonne est configuré pour compter de 0 à 639, alors que l'autre compte de 0 à 479. Par exemple, pour une résolution de 640 par 480, les deux compteurs sont initialisés à 0. Le compteur de colonne est incrémenté à chaque envoi de pixel, et il déclenche le signal de synchronisation horizontale une fois que le compteur atteint 640. Le compteur de colonne est alors réinitialisé après un certain temps, alors que le compteur de ligne est incrémenté. Le compteur de ligne est donc incrémenté à chaque nouvelle ligne. De plus, il émet un signal de synchronisation verticale quand il atteint 480, et est réinitialisé après cela.
Il est possible de faire ainsi, mais ce n'est pas la solution idéale. En réalité, il faut tenir compte du fait que les signaux de HSYNC et VSYNC, qui sont eux aussi générés par les deux compteurs. Imaginons que le signal HSYNC prenne 20 cycles d'horloge, et le signal VSYNC 150 cycles. Pour une résolution de 640 par 480, on utilise un compteur de colonne qui compte de 0 à 640 + 20, et un compteur de ligne qui compte de 0 à 480 + 150.
L'idée est d'utiliser des comparateurs pour générer les signaux HSYNC et VSYNC, un pour le signal HSYNC et un autre pour le signal VSYNC. En reprenant les valeurs mentionnées précédemment, on utilise un comparateur qui vérifie si le compteur de colonne est supérieur ou égal à 640, et un autre comparateur qui vérifie si le compteur de ligne est égal ou dépasse 480. La sortie des deux comparateurs fournit directement les signaux HSYNC et VSYNC.
Une autre solution remplace les comparateurs par une mémoire ROM. L'idée est d'envoyer les compteurs sur l'entrée d'adresse, la ROM fournit en sortie les signaux de commande destinés à l'écran. En remplissant la ROM avec les valeurs adéquates, la technique fonctionne à merveille et on peut se passer des circuits comparateurs. Pour les haute résolutions, il est possible d'utiliser deux ROMs : une pour le compteur de ligne, une pour le compteur de colonne.
Le VDC peut gérer plusieurs résolutions différentes, et les ''timings'' sont différents suivant les résolutions. Idéalement, il faut envoyer quelques bits de commande pour choisir la résolutions en entrée de la mémoire ROM pour choisir les bons timings. Avec des comparateurs, la technique demande d'utiliser les mêmes comparateurs, mais d'ajouter des circuits pour gérer les différentes résolutions.
===L'exemple des timings du standard VGA===
Reprenons l'exemple du standard VGA. Avec ce standard, il existait un fil H-SYNC pour indiquer qu'on transmettait une nouvelle ligne et un fil V-SYNC pour indiquer qu'on envoie une nouvelle image. Une nouvelle ligne ou image est indiquée en mettant un 0 sur le fil adéquat. De plus, on devait attendre un certain temps entre la transmission de deux lignes, ce qui introduisait des vides dans le flux de pixels. Même chose entre deux images, sauf que le temps d'attente était plus long que le temps d'attente entre deux lignes. Le tout est détaillé dans le schéma ci-dessous, qui détaille le cas pour une résolution de 640 par 480.
[[File:VGASpecification.png|centre|vignette|upright=2|Standard VGA : spécification des temps d'attentes entre deux lignes et deux images.]]
Le compteur de colonne est cadencé à une fréquence bien précise, qui détermine le temps mis pour passer d'un pixel à l'autre. Le temps de transmission d'un pixel est de 25,6 µs / 640 = 0,04 µs, ce qui correspond à une fréquence de 25 MégaHertz. Et cela permet d'implémenter facilement les deux temps d'attente avant et après l'affichage d'une ligne. Les temps d'attente de 1,54 et 0,64 µs correspondent respectivement à 38 et 16 cycles du compteur, la durée de 3,8 µs du signal H-sync correspond à 95 cycles. En tout, cela fait 640 + 95 + 16 + 38 = 789. Il faut donc un compteur qui compte de 0 à 788.
La transmission des pixels commence quand le compteur commence à compter. Puis, le compteur continue de compter pendant 0,64 µs alors qu'aucun pixel n'est envoyé, afin de gérer le temps d'attente après le signal H-sync. Puis, au 640 + 16ème cycle, le signal H-sync est généré pendant 95 cycles. Enfin, le compteur continue de compter pendant 38 cycles pour le second temps d'attente, avant le prochain envoi de ligne. Le signal H-sync est donc généré quand le compteur a une valeur comprise entre 656 et 751 : il suffit d'ajouter un comparateur qui vérifie si le compteur est dans cet intervalle, et donc la sortie est à zéro si c'est le cas. L'adresse n'est pas calculée si le compteur n'a pas une valeur comprise entre 0 et la largeur indiquée par la résolution.
La même logique s'applique avec le signal V-sync, mais avec des timings différents, illustrés plus haut.
Pour implémenter tout cela, il suffit de combiner les deux compteurs avec des circuits comparateurs, qui vérifient si la valeur du compteur est dans tel ou tel intervalle. Il faut au minimum deux circuits comparateurs, un pour le signal HSYNC, un autre pour le signal VSYNC. D'autres compteurs peuvent être utilisés pour générer les bits de ''blanking'' ou pour réinitialiser le compteur à la valeur adéquate. Les comparateurs peuvent être remplacés par une mémoire ROM, comme dit plus haut.
[[File:VGACmptFig2.png|centre|vignette|upright=2|Circuit de gestion des timings H-sync et V-sync d'un écran VGA.]]
===La génération des ''raster interrupts'' et des bits de ''blanking''===
Les mêmes compteurs ou la ROM sont souvent utilisés pour générer les ''raster interrupts'' et le bit de ''blanking'', qui permettent de prévenir le processeur quand la carte d'affichage a terminé d'envoyer une ligne et/ou une image entière à l'écran.
Notons qu'il est possible d'implémenter les interruptions à partir du bit de ''blanking'', cela demande juste aux compteurs de générer ce bit de ''blanking'' et de l'utiliser pour générer les ''raster interrupt''. Au passage, les compteurs de ligne et colonne ne servent pas qu'à générer des signaux : on verra dans la section sur le CRTC que quand on dispose de ces deux compteurs, ajouter de quoi parcourir le ''framebuffer'' est trivial !
{{NavChapitre | book=Les cartes graphiques
| prev=Les cartes d'affichage
| prevText=Les cartes d'affichage
| next=Les systèmes à framebuffer
| nextText=Les systèmes à framebuffer
}}
{{autocat}}
lqkbj3px3vao6qsnxlu0xr5t2vk08m8
Fonctionnement d'un ordinateur/Le chemin de données
0
69025
745342
743243
2025-06-25T05:46:15Z
Regimminius
7153
/* Une architecture LOAD-STORE basique, avec adressage absolu */ Faute de frappe
745342
wikitext
text/x-wiki
Comme vu précédemment, le '''chemin de donnée''' est l'ensemble des composants dans lesquels circulent les données dans le processeur. Il comprend l'unité de calcul, les registres, l'unité de communication avec la mémoire, et le ou les interconnexions qui permettent à tout ce petit monde de communiquer. Dans ce chapitre, nous allons voir ces composants en détail.
==Les unités de calcul==
Le processeur contient des circuits capables de faire des calculs arithmétiques, des opérations logiques, et des comparaisons, qui sont regroupés dans une unité de calcul appelée '''unité arithmétique et logique'''. Certains préfèrent l’appellation anglaise ''arithmetic and logic unit'', ou ALU. Par défaut, ce terme est réservé aux unités de calcul qui manipulent des nombres entiers. Les unités de calcul spécialisées pour les calculs flottants sont désignées par le terme "unité de calcul flottant", ou encore FPU (''Floating Point Unit'').
L'interface d'une unité de calcul est assez simple : on a des entrées pour les opérandes et une sortie pour le résultat du calcul. De plus, les instructions de comparaisons ou de calcul peuvent mettre à jour le registre d'état, qui est relié à une autre sortie de l’unité de calcul. Une autre entrée, l''''entrée de sélection de l'instruction''', spécifie l'opération à effectuer. Elle sert à configurer l'unité de calcul pour faire une addition et pas une multiplication, par exemple. Sur cette entrée, on envoie un numéro qui précise l'opération à effectuer. La correspondance entre ce numéro et l'opération à exécuter dépend de l'unité de calcul. Sur les processeurs où l'encodage des instructions est "simple", une partie de l'opcode de l'instruction est envoyé sur cette entrée.
[[File:Unité de calcul usuelle.png|centre|vignette|upright=2|Unité de calcul usuelle.]]
Il faut signaler que les processeurs modernes possèdent plusieurs unités de calcul, toutes reliées aux registres. Cela permet d’exécuter plusieurs calculs en même temps dans des unités de calcul différentes, afin d'augmenter les performances du processeur. Diverses technologies, abordées dans la suite du cours permettent de profiter au mieux de ces unités de calcul : pipeline, exécution dans le désordre, exécution superscalaire, jeux d'instructions VLIW, etc. Mais laissons cela de côté pour le moment.
===L'ALU entière : additions, soustractions, opérations bit à bit===
Un processeur contient plusieurs ALUs spécialisées. La principale, présente sur tous les processeurs, est l''''ALU entière'''. Elle s'occupe uniquement des opérations sur des nombres entiers, les nombres flottants sont gérés par une ALU à part. Elle gère des opérations simples : additions, soustractions, opérations bit à bit, parfois des décalages/rotations. Par contre, elle ne gère pas la multiplication et la division, qui sont prises en charge par un circuit multiplieur/diviseur à part.
L'ALU entière a déjà été vue dans un chapitre antérieur, nommé "Les unités arithmétiques et logiques entières (simples)", qui expliquait comment en concevoir une. Nous avions vu qu'une ALU entière est une sorte de circuit additionneur-soustracteur amélioré, ce qui explique qu'elle gère des opérations entières simples, mais pas la multiplication ni la division. Nous ne reviendrons pas dessus. Cependant, il y a des choses à dire sur leur intégration au processeur.
Une ALU entière gère souvent une opération particulière, qui ne fait rien et recopie simplement une de ses opérandes sur sa sortie. L'opération en question est appelée l''''opération ''Pass through''''', encore appelée opération NOP. Elle est implémentée en utilisant un simple multiplexeur, placé en sortie de l'ALU. Le fait qu'une ALU puisse effectuer une opération ''Pass through'' permet de fortement simplifier le chemin de donnée, d'économiser des multiplexeurs. Mais nous verrons cela sous peu.
[[File:ALU avec opération NOP.png|centre|vignette|upright=2|ALU avec opération NOP.]]
Avant l'invention du microprocesseur, le processeur n'était pas un circuit intégré unique. L'ALU, le séquenceur et les registres étaient dans des puces séparées. Les ALU étaient vendues séparément et manipulaient des opérandes de 4/8 bits, les ALU 4 bits étaient très fréquentes. Si on voulait créer une ALU pour des opérandes plus grandes, il fallait construire l'ALU en combinant plusieurs ALU 4/8 bits. Par exemple, l'ALU des processeurs AMD Am2900 est une ALU de 16 bits composée de plusieurs sous-ALU de 4 bits. Cette technique qui consiste à créer des unités de calcul à partir d'unités de calcul plus élémentaires s'appelle en jargon technique du '''bit slicing'''. Nous en avions parlé dans le chapitre sur les unités de calcul, aussi nous n'en reparlerons pas plus ici.
L'ALU manipule des opérandes codées sur un certain nombre de bits. Par exemple, une ALU peut manipuler des entiers codés sur 8 bits, sur 16 bits, etc. En général, la taille des opérandes de l'ALU est la même que la taille des registres. Un processeur 32 bits, avec des registres de 32 bit, a une ALU de 32 bits. C'est intuitif, et cela rend l'implémentation du processeur bien plus facile. Mais il y a quelques exceptions, où l'ALU manipule des opérandes plus petits que la taille des registres. Par exemple, de nombreux processeurs 16 bits, avec des registres de 16 bits, utilisent une ALU de 8 bits. Un autre exemple assez connu est celui du Motorola 68000, qui était un processeur 32 bits, mais dont l'ALU faisait juste 16 bits. Son successeur, le 68020, avait lui une ALU de 32 bits.
Sur de tels processeurs, les calculs sont fait en plusieurs passes. Par exemple, avec une ALU 8 bit, les opérations sur des opérandes 8 bits se font en un cycle d'horloge, celles sur 16 bits se font en deux cycles, celles en 32 en quatre, etc. Si un programme manipule assez peu d'opérandes 16/32/64 bits, la perte de performance est assez faible. Diverses techniques visent à améliorer les performances, mais elles ne font pas de miracles. Par exemple, vu que l'ALU est plus courte, il est possible de la faire fonctionner à plus haute fréquence, pour réduire la perte de performance.
Pour comprendre comme est implémenté ce système de passes, prenons l'exemple du processeur 8 bit Z80. Ses registres entiers étaient des registres de 8 bits, alors que l'ALU était de 4 bits. Les calculs étaient faits en deux phases : une qui traite les 4 bits de poids faible, une autre qui traite les 4 bits de poids fort. Pour cela, les opérandes étaient placées dans des registres de 4 bits en entrée de l'ALU, plusieurs multiplexeurs sélectionnaient les 4 bits adéquats, le résultat était mémorisé dans un registre de résultat de 8 bits, un démultiplexeur plaçait les 4 bits du résultat au bon endroit dans ce registre. L'unité de contrôle s'occupait de la commande des multiplexeurs/démultiplexeurs. Les autres processeurs 8 ou 16 bits utilisent des circuits similaires pour faire leurs calculs en plusieurs fois.
[[File:ALU du Z80.png|centre|vignette|upright=2|ALU du Z80]]
Un exemple extrême est celui des des '''processeurs sériels''' (sous-entendu ''bit-sériels''), qui utilisent une '''ALU sérielle''', qui fait leurs calculs bit par bit, un bit à la fois. S'il a existé des processeurs de 1 bit, comme le Motorola MC14500B, la majeure partie des processeurs sériels étaient des processeurs 4, 8 ou 16 bits. L'avantage de ces ALU est qu'elles utilisent peu de transistors, au détriment des performances par rapport aux processeurs non-sériels. Mais un autre avantage est qu'elles peuvent gérer des opérandes de grande taille, avec plus d'une trentaine de bits, sans trop de problèmes.
===Les circuits multiplieurs et diviseurs===
Les processeurs modernes ont une ALU pour les opérations simples (additions, décalages, opérations logiques), couplée à une ALU pour les multiplications, un circuit multiplieur séparé. Précisons qu'il ne sert pas à grand chose de fusionner le circuit multiplieur avec l'ALU, mieux vaut les garder séparés par simplicité. Les processeurs haute performance disposent systématiquement d'un circuit multiplieur et gèrent la multiplication dans leur jeu d'instruction.
Le cas de la division est plus compliqué. La présence d'un circuit multiplieur est commune, mais les circuits diviseurs sont eux très rares. Leur cout en circuit est globalement le même que pour un circuit multiplieur, mais le gain en performance est plus faible. Le gain en performance pour la multiplication est modéré car il s'agit d'une opération très fréquente, alors qu'il est très faible pour la division car celle-ci est beaucoup moins fréquente.
Pour réduire le cout en circuits, il arrive que l'ALU pour les multiplications gère à la fois la multiplication et la division. Les circuits multiplieurs et diviseurs sont en effet très similaires et partagent beaucoup de points communs. Généralement, la fusion se fait pour les multiplieurs/diviseurs itératifs.
Il existe cependant des circuits qui se passent de multiplieurs, tout en supportant la multiplication dans leur jeu d'instruction. Certains utilisent pour cela du microcode, technique qu'on verra dans deux chapitres, mais l'Intel Atom utilise une technique franchement peu ordinaire. L'Intel Atom utilise l'unité de calcul flottante pour faire les multiplications entières. Les opérandes entières sont traduites en nombres flottants, multipliés par l'unité de calcul flottante, puis le résultat est converti en un entier avec quelques corrections à la clé. Ainsi, on fait des économies de circuits, en mutualisant le multiplieur entre l'unité de calcul flottante et l'ALU entière, surtout que ce multiplieur manipule des opérandes plus courtes. Les performances sont cependant réduites comparé à l'usage d'un vrai multiplieur entier.
===Le ''barrel shifter''===
On vient d'expliquer que la présence de plusieurs ALU spécialisée est très utile pour implémenter des opérations compliquées à insérer dans une unité de calcul normale, comme la multiplication et la division. Mais les décalages sont aussi dans ce cas, de même que les rotations. Nous avions vu il y a quelques chapitres qu'ils sont réalisés par un circuit spécialisé, appelé un ''barrel shifter'', qu'il est difficile de fusionner avec une ALU normale. Aussi, beaucoup de processeurs incorporent un ''barrel shifter'' séparé de l'ALU.
Les processeurs ARM utilise un ''barrel shifter'', mais d'une manière un peu spéciale. On a vu il y a quelques chapitres que si on fait une opération logique, une addition, une soustraction ou une comparaison, la seconde opérande peut être décalée automatiquement. L'instruction incorpore le type de de décalage à faire et par combien de rangs il faut décaler directement à côté de l'opcode. Cela simplifie grandement les calculs d'adresse, qui se font en une seule instruction, contre deux ou trois sur d'autres architectures. Et pour cela, l'ALU proprement dite est précédée par un ''barrel shifter'',une seconde ALU spécialisée dans les décalages. Notons que les instructions MOV font aussi partie des instructions où la seconde opérande (le registre source) peut être décalé : cela signifie que les MOV passent par l'ALU, qui effectue alors un NOP, une opération logique OUI.
===Les unités de calcul spécialisées===
Un processeur peut disposer d’unités de calcul séparées de l'unité de calcul principale, spécialisées dans les décalages, les divisions, etc. Et certaines d'entre elles sont spécialisées dans des opérations spécifiques, qui ne sont techniquement pas des opérations entières, sur des nombres entiers.
Depuis les années 90-2000, presque tous les processeurs utilisent une unité de calcul spécialisée pour les nombres flottants : la '''Floating-Point Unit''', aussi appelée FPU. En général, elle regroupe un additionneur flottant, un soustracteur, un multiplieur et un diviseur flottant. Parfois, le diviseur flottant est omis, ou séparé du reste de la FPU, tout dépend du processeur. Précisons que sur certains processeurs, la FPU et l'ALU entière ne vont pas à la même fréquence, pour des raisons de performance et de consommation d'énergie !
Il existe des unités de calcul spécialisées pour les calculs d'adresse. Elles ne supportent guère plus que des incrémentations/décrémentations, des additions/soustractions, et des décalages simples. Les autres opérations n'ont pas de sens avec des adresses. L'usage d'ALU spécialisées pour les adresses est un avantage sur les processeurs où les adresses ont une taille différente des données, ce qui est fréquent sur les anciens processeurs 8 bits.
De nombreux processeurs modernes disposent d'une unité de calcul spécialisée dans le calcul des conditions, tests et branchements. C’est notamment le cas sur les processeurs sans registre d'état, qui disposent de registres à prédicats. En général, les registres à prédicats sont placés à part des autres registres, dans un banc de registre séparé. L'unité de calcul normale n'est pas reliée aux registres à prédicats, alors que l'unité de calcul pour les branchements/test/conditions l'est. les registres à prédicats sont situés juste en sortie de cette unité de calcul.
==Les registres du processeur==
Après avoir vu l'unité de calcul, il est temps de passer aux registres d'un processeur. L'organisation des registres est généralement assez compliquée, avec quelques registres séparés des autres comme le registre d'état ou le ''program counter''. Les registres d'un processeur peuvent se classer en deux camps : soit ce sont des registres isolés, soit ils sont regroupés en paquets appelés banc de registres.
Un '''banc de registres''' (''register file'') est une RAM, dont chaque byte est un registre. Il regroupe un paquet de registres différents dans un seul composant, dans une seule mémoire. Dans processeur moderne, on trouve un ou plusieurs bancs de registres. La répartition des registres, à savoir quels registres sont dans le banc de registre et quels sont ceux isolés, est très variable suivant les processeurs.
[[File:Register File Simple.svg|centre|vignette|upright=1|Banc de registres simplifié.]]
===L'adressage du banc de registres===
Le banc de registre est une mémoire comme une autre, avec une entrée d'adresse qui permet de sélectionner le registre voulu. Plutot que d'adresse, nous allons parler d''''identifiant de registre'''. Le séquenceur forge l'identifiant de registre en fonction des registres sélectionnés. Dans les chapitres précédents, nous avions vu qu'il existe plusieurs méthodes pour sélectionner un registre, qui portent les noms de modes d'adressage. Et bien les modes d'adressage jouent un grand rôle dans la forge de l'identifiant de registre.
Pour rappel, sur la quasi-totalité des processeurs actuels, les registres généraux sont identifiés par un nom de registre, terme trompeur vu que ce nom est en réalité un numéro. En clair, les processeurs numérotent les registres, le numéro/nom du registre permettant de l'identifier. Par exemple, si je veux faire une addition, je dois préciser les deux registres pour les opérandes, et éventuellement le registre pour le résultat : et bien ces registres seront identifiés par un numéro. Mais tous les registres ne sont pas numérotés et ceux qui ne le sont pas sont adressés implicitement. Par exemple, le pointeur de pile sera modifié par les instructions qui manipulent la pile, sans que cela aie besoin d'être précisé par un nom de registre dans l'instruction.
Dans le cas le plus simple, les registres nommés vont dans le banc de registres, les registres adressés implicitement sont en-dehors, dans des registres isolés. L'idéntifiant de registre est alors simplement le nom de registre, le numéro. Le séquenceur extrait ce nom de registre de l'insutrction, avant de l'envoyer sur l'entrée d'adresse du banc de registre.
[[File:Adressage du banc de registres généruax.png|centre|vignette|upright=2|Adressage du banc de registres généraux]]
Dans un cas plus complexe, des registres non-nommés sont placés dans le banc de registres. Par exemple, les pointeurs de pile sont souvent placés dans le banc de registre, même s'ils sont adressés implicitement. Même des registres aussi importants que le ''program counter'' peuvent se mettre dans le banc de registre ! Nous verrons le cas du ''program counter'' dans le chapitre suivant, qui porte sur l'unité de chargement. Dans ce cas, le séquenceur forge l'identifiant de registre de lui-même. Dans le cas des registres nommés, il ajoute quelques bits aux noms de registres. Pour les registres adressés implicitement, il forge l'identifiant à partir de rien.
[[File:Adressage du banc de registre - cas général.png|centre|vignette|upright=2|Adressage du banc de registre - cas général]]
Nous verrons plus bas que dans certains cas, le nom de registre ne suffit pas à adresser un registre dans un banc de registre. Dans ce cas, le séquenceur rajoute des bits, comme dans l'exemple précédent. Tout ce qu'il faut retenir est que l'identifiant de registre est forgé par le séquenceur, qui se base entre autres sur le nom de registre s'il est présent, sur l'instruction exécutée dans le cas d'un registre adressé implicitement.
===Les registres généraux===
Pour rappel, les registres généraux peuvent mémoriser des entiers, des adresses, ou toute autre donnée codée en binaire. Ils sont souvent séparés des registres flottants sur les architectures modernes. Les registres généraux sont rassemblés dans un banc de registre dédié, appelé le '''banc de registres généraux'''. Le banc de registres généraux est une mémoire multiport, avec au moins un port d'écriture et deux ports de lecture. La raison est que les instructions lisent deux opérandes dans les registres et enregistrent leur résultat dans des registres. Le tout se marie bien avec un banc de registre à deux de lecture (pour les opérandes) et un d'écriture (pour le résultat).
[[File:Banc de registre multiports.png|centre|vignette|upright=2|Banc de registre multiports.]]
L'interface exacte dépend de si l'architecture est une architecture 2 ou 3 adresses. Pour rappel, la différence entre les deux tient dans la manière dont on précise le registre où enregistrer le résultat d'une opération. Avec les architectures 2-adresses, on précise deux registres : le premier sert à la fois comme opérande et pour mémoriser le résultat, l'autre sert uniquement d'opérande. Un des registres est donc écrasé pour enregistrer le résultat. Sur les architecture 3-adresses, on précise trois registres : deux pour les opérandes, un pour le résultat.
Les architectures 2-adresses ont un banc de registre où on doit préciser deux "adresses", deux noms de registre. L'interface du banc de registre est donc la suivante :
[[File:Register File Medium.svg|centre|vignette|upright=1.5|Register File d'une architecture à 2-adresses]]
Les architectures 3-adresses doivent rajouter une troisième entrée pour préciser un troisième nom de registre. L'interface du banc de registre est donc la suivante :
[[File:Register File Large.svg|centre|vignette|upright=1.5|Register File d'une architecture à 3-adresses]]
Rien n'empêche d'utiliser plusieurs bancs de registres sur un processeur qui utilise des registres généraux. La raison est une question d'optimisation. Au-delà d'un certain nombre de registres, il devient difficile d'utiliser un seul gros banc de registres. Il faut alors scinder le banc de registres en plusieurs bancs de registres séparés. Le problème est qu'il faut prévoir de quoi échanger des données entre les bancs de registres. Dans la plupart des cas, cette séparation est invisible du point de vue du langage machine. Sur d'autres processeurs, les transferts de données entre bancs de registres se font via une instruction spéciale, souvent appelée COPY.
===Les registres flottants : banc de registre séparé ou unifié===
Passons maintenant aux registres flottants. Intuitivement, on a des registres séparés pour les entiers et les flottants. Il est alors plus simple d'utiliser un banc de registres séparé pour les nombres flottants, à côté d'un banc de registre entiers. L'avantage est que les nombres flottants et entiers n'ont pas forcément la même taille, ce qui se marie bien avec deux bancs de registres, où la taille des registres est différente dans les deux bancs.
Mais d'autres processeurs utilisent un seul '''banc de registres unifié''', qui regroupe tous les registres de données, qu'ils soient entier ou flottants. Par exemple, c'est le cas des Pentium Pro, Pentium II, Pentium III, ou des Pentium M : ces processeurs ont des registres séparés pour les flottants et les entiers, mais ils sont regroupés dans un seul banc de registres. Avec cette organisation, un registre flottant et un registre entier peuvent avoir le même nom de registre en langage machine, mais l'adresse envoyée au banc de registres ne doit pas être la même : le séquenceur ajoute des bits au nom de registre pour former l'adresse finale.
[[File:Désambiguïsation de registres sur un banc de registres unifié.png|centre|vignette|upright=2|Désambiguïsation de registres sur un banc de registres unifié.]]
===Le registre d'état===
Le registre d'état fait souvent bande à part et n'est pas placé dans un banc de registres. En effet, le registre d'état est très lié à l'unité de calcul. Il reçoit des indicateurs/''flags'' provenant de la sortie de l'unité de calcul, et met ceux-ci à disposition du reste du processeur. Son entrée est connectée à l'unité de calcul, sa sortie est reliée au séquenceur et/ou au bus interne au processeur.
Le registre d'état est relié au séquenceur afin que celui-ci puisse gérer les instructions de branchement, qui ont parfois besoin de connaitre certains bits du registre d'état pour savoir si une condition a été remplie ou non. D'autres processeurs relient aussi le registre d'état au bus interne, ce qui permet de lire son contenu et de le copier dans un registre de données. Cela permet d'implémenter certaines instructions, notamment celles qui permettent de mémoriser le registre d'état dans un registre général.
[[File:Place du registre d'état dans le chemin de données.png|centre|vignette|upright=2|Place du registre d'état dans le chemin de données]]
L'ALU fournit une sortie différente pour chaque bit du registre d'état, la connexion du registre d'état est directe, comme indiqué dans le schéma suivant. Vous remarquerez que le bit de retenue est à la fois connecté à la sortie de l'ALU, mais aussi sur son entrée. Ainsi, le bit de retenue calculé par une opération peut être utilisé pour la suivante. Sans cela, diverses instructions comme les opérations ''add with carry'' ne seraient pas possibles.
[[File:AluStatusRegister.svg|centre|vignette|upright=2|Registre d'état et unit de calcul.]]
Il est techniquement possible de mettre le registre d'état dans le banc de registre, pour économiser un registre. La principale difficulté est que les instructions doivent faire deux écritures dans le banc de registre : une pour le registre de destination, une pour le registre d'état. Soit on utilise deux ports d'écriture, soit on fait les deux écritures l'une après l'autre. Dans les deux cas, le cout en performances et en transistors n'en vaut pas le cout. D'ailleurs, je ne connais aucun processeur qui utilise cette technique.
Il faut noter que le registre d'état n'existe pas forcément en tant que tel dans le processeur. Quelques processeurs, dont le 8086 d'Intel, utilisent des bascules dispersées dans le processeur au lieu d'un vrai registre d'état. Les bascules dispersées mémorisent chacune un bit du registre d'état et sont placées là où elles sont le plus utile. Les bascules utilisées pour les branchements sont proches du séquenceur, le bascules pour les bits de retenue sont placées proche de l'ALU, etc.
===Les registres à prédicats===
Les registres à prédicats remplacent le registre d'état sur certains processeurs. Pour rappel, les registres à prédicat sont des registres de 1 bit qui mémorisent les résultats des comparaisons et instructions de test. Ils sont nommés/numérotés, mais les numéros en question sont distincts de ceux utilisés pour les registres généraux.
Ils sont placés à part, dans un banc de registres séparé. Le banc de registres à prédicats a une entrée de 1 bit connectée à l'ALU et une sortie de un bit connectée au séquenceur. Le banc de registres à prédicats est parfois relié à une unité de calcul spécialisée dans les conditions/instructions de test. Pour rappel, certaines instructions permettent de faire un ET, un OU, un XOR entre deux registres à prédicats. Pour cela, l'unité de calcul dédiée aux conditions peut lire les registres à prédicats, pour combiner le contenu de plusieurs d'entre eux.
[[File:Banc de registre pour les registres à prédicats.png|centre|vignette|upright=2|Banc de registre pour les registres à prédicats]]
===Les registres dédiés aux interruptions===
Dans le chapitre sur les registres, nous avions vu que certains processeurs dupliquaient leurs registres architecturaux, pour accélérer les interruptions ou les appels de fonction. Dans le cas qui va nous intéresser, les interruptions avaient accès à leurs propres registres, séparés des registres architecturaux. Les processeurs de ce type ont deux ensembles de registres identiques : un dédié aux interruptions, un autre pour les programmes normaux. Les registres dans les deux ensembles ont les mêmes noms, mais le processeur choisit le bon ensemble suivant s'il est dans une interruption ou non. Si on peut utiliser deux bancs de registres séparés, il est aussi possible d'utiliser un banc de registre unifié pour les deux.
Sur certains processeurs, le banc de registre est dupliqué en plusieurs exemplaires. La technique est utilisée pour les interruptions. Certains processeurs ont deux ensembles de registres identiques : un dédié aux interruptions, un autre pour les programmes normaux. Les registres dans les deux ensembles ont les mêmes noms, mais le processeur choisit le bon ensemble suivant s'il est dans une interruption ou non. On peut utiliser deux bancs de registres séparés, un pour les interruptions, et un pour les programmes.
Sur d'autres processeurs, on utilise un banc de registre unifié pour les deux ensembles de registres. Les registres pour les interruptions sont dans les adresses hautes, les registres pour les programmes dans les adresses basses. Le choix entre les deux est réalisé par un bit qui indique si on est dans une interruption ou non, disponible dans une bascule du processeur. Appelons là la bascule I.
===Le fenêtrage de registres===
[[File:Fenetre de registres.png|vignette|upright=1|Fenêtre de registres.]]
Le '''fenêtrage de registres''' fait que chaque fonction a accès à son propre ensemble de registres, sa propre fenêtre de registres. Là encore, cette technique duplique chaque registre architectural en plusieurs exemplaires qui portent le même nom. Chaque ensemble de registres architecturaux forme une fenêtre de registre, qui contient autant de registres qu'il y a de registres architecturaux. Lorsqu'une fonction s’exécute, elle se réserve une fenêtre inutilisée, et peut utiliser les registres de la fenêtre comme bon lui semble : une fonction manipule le registre architectural de la fenêtre réservée, mais pas les registres avec le même nom dans les autres fenêtres.
Il peut s'implémenter soit avec un banc de registres unifié, soit avec un banc de registre par fenêtre de registres.
Il est possible d'utiliser des bancs de registres dupliqués pour le fenêtrage de registres. Chaque fenêtre de registre a son propre banc de registres. Le choix entre le banc de registre à utiliser est fait par un registre qui mémorise le numéro de la fenêtre en cours. Ce registre commande un multiplexeur qui permet de choisir le banc de registre adéquat.
[[File:Fenêtrage de registres au niveau du banc de registres.png|vignette|Fenêtrage de registres au niveau du banc de registres.]]
L'utilisation d'un banc de registres unifié permet d'implémenter facilement le fenêtrage de registres. Il suffit pour cela de regrouper tous les registres des différentes fenêtres dans un seul banc de registres. Il suffit de faire comme vu au-dessus : rajouter des bits au nom de registre pour faire la différence entre les fenêtres. Cela implique de se souvenir dans quelle fenêtre de registre on est actuellement, cette information étant mémorisée dans un registre qui stocke le numéro de la fenêtre courante. Pour changer de fenêtre, il suffit de modifier le contenu de ce registre lors d'un appel ou retour de fonction avec un petit circuit combinatoire. Bien sûr, il faut aussi prendre en compte le cas où ce registre déborde, ce qui demande d'ajouter des circuits pour gérer la situation.
[[File:Désambiguïsation des fenêtres de registres.png|centre|vignette|upright=2|Désambiguïsation des fenêtres de registres.]]
==L'interface de communication avec la mémoire==
L''''interface avec la mémoire''' est, comme son nom l'indique, des circuits qui servent d'intermédiaire entre le bus mémoire et le processeur. Elle est parfois appelée l'unité mémoire, l'unité d'accès mémoire, la ''load-store unit'', et j'en oublie.
[[File:Unité de communication avec la mémoire, de type simple port.png|centre|vignette|upright=2|Unité de communication avec la mémoire, de type simple port.]]
Sur certains processeurs, elle gère les mémoires multiport.
[[File:Unité de communication avec la mémoire, de type multiport.png|centre|vignette|upright=2|Unité de communication avec la mémoire, de type multiport.]]
===Les registres d'interfaçage mémoire===
L'interface mémoire se résume le plus souvent à des '''registres d’interfaçage mémoire''', intercalés entre le bus mémoire et le chemin de données. Généralement, il y a au moins deux registres d’interfaçage mémoire : un registre relié au bus d'adresse, et autre relié au bus de données.
[[File:Registres d’interfaçage mémoire.png|centre|vignette|upright=2|Registres d’interfaçage mémoire.]]
Au lieu de lire ou écrire directement sur le bus, le processeur lit ou écrit dans ces registres, alors que l'unité d'accès mémoire s'occupe des échanges entre registres et bus mémoire. Lors d'une écriture, le processeur place l'adresse dans le registre d'interfaçage d'adresse, met la donnée à écrire dans le registre d'interfaçage de donnée, puis laisse l'unité d'accès mémoire faire son travail. Lors d'une lecture, il place l'adresse à lire sur le registre d'interfaçage d'adresse, il attend que la donnée soit lue, puis récupère la donnée dans le registre d'interfaçage de données.
L'avantage est que le processeur n'a pas à maintenir une donnée/adresse sur le bus durant tout un accès mémoire. Par exemple, prenons le cas où la mémoire met 15 cycles processeurs pour faire une lecture ou une écriture. Sans registres d'interfaçage mémoire, le processeur doit maintenir l'adresse durant 15 cycles, et aussi la donnée dans le cas d'une écriture. Avec ces registres, le processeur écrit dans les registres d'interfaçage mémoire au premier cycle, et passe les 14 cycles suivants à faire quelque chose d'autre. Par exemple, il faut faire un calcul en parallèle, envoyer des signaux de commande au banc de registre pour qu'il soit prêt une fois la donnée lue arrivée, etc. Cet avantage simplifie l'implémentation de certains modes d'adressage, comme on le verra à la fin du chapitre.
===L'unité de calcul d'adresse===
Les registres d'interfaçage sont presque toujours présents, mais le circuit que nous allons voir est complétement facultatif. Il s'agit d'une unité de calcul spécialisée dans les calculs d'adresse, dont nous avons parlé rapidement dans la section sur les ALU. Elle s'appelle l''''''Address generation unit''''', ou AGU. Elle est parfois séparée de l'interface mémoire proprement dit, et est alors considérée comme une unité de calcul à part, mais elle est généralement intimement liée à l'interface mémoire.
Elle sert pour certains modes d'adressage, qui demandent de combiner une adresse avec soit un indice, soit un décalage, plus rarement les deux. Les calculs d'adresse demandent de simplement incrémenter/décrémenter une adresse, de lui ajouter un indice (et de décaler les indices dans certains cas), mais guère plus. Pas besoin d'effectuer de multiplications, de divisions, ou d'autre opération plus complexe. Des décalages et des additions/soustractions suffisent. L'AGU est donc beaucoup plus simple qu'une ALU normale et se résume souvent à un vulgaire additionneur-soustracteur, éventuellement couplée à un décaleur pour multiplier les indices.
[[File:Unité d'accès mémoire avec unité de calcul dédiée.png|centre|vignette|upright=1.5|Unité d'accès mémoire avec unité de calcul dédiée]]
Le fait d'avoir une unité de calcul séparée pour les adresses peut s'expliquer pour plusieurs raisons. Sur les processeurs normaux, la raison est que cela simplifie un peu l'implémentation des modes d'adressage indirects. Sur les rares processeurs qui ont des registres séparés pour les adresses, un banc de registre dédié est réservé aux registres d'adresses, ce qui rend l'usage d'une unité de calcul d'adresse bien plus pratique. Une autre raison se manifestait sur les processeurs 8 bits : ils géraient des données de 8 bits, mais des adresses de 16 bits. Dans ce cas, le processeur avait une ALU simple de 16 bits pour les adresses, et une ALU complexe de 8 bits pour les données.
[[File:Unité d'accès mémoire avec registres d'adresse ou d'indice.png|centre|vignette|upright=2|Unité d'accès mémoire avec registres d'adresse ou d'indice]]
===La gestion de l'alignement et du boutisme===
L'interface mémoire gère les accès mémoire non-alignés, à cheval sur deux mots mémoire (rappelez-vous le chapitre sur l'alignement mémoire). Elle détecte les accès mémoire non-alignés et réagit en conséquence. Dans le cas où les accès non-alignés sont interdits, elle lève une exception matérielle. Dans le cas où ils sont autorisés, elle les gère automatiquement, à savoir qu'elle charge deux mots mémoire et les combine entre eux pour donner le résultat final. Dans les deux cas, cela demande d'ajouter des circuits de détection des accès non-alignés, et éventuellement des circuits pour le double lecture/écriture.
Les circuits de détection des accès non-alignés sont très simples. Dans le cas où les adresses sont alignées sur une puissance de deux (cas le plus courant), il suffit de vérifier les bits de poids faible de l'adresse à lire. Prenons l'exemple d'un processeur avec des adresses codées sur 64 bits, avec des mots mémoire de 32 bits, alignés sur 32 bits (4 octets). Un mot mémoire contient 4 octets, les contraintes d'alignement font que les adresses autorisées sont des multiples de 4. En conséquence, les 2 bits de poids faible d'une adresse valide sont censés être à 0. En vérifiant la valeur de ces deux bits, on détecte facilement les accès non-alignés.
En clair, détecter les accès non-alignés demande de tester si les bits de poids faibles adéquats sont à 0. Il suffit donc d'un circuit de comparaison avec zéro; qui est une simple porte OU. Cette porte OU génère un bit qui indique si l'accès testé est aligné ou non : 1 si l'accès est non-aligné, 0 sinon. Le signal peut être transmis au séquenceur pour générer une exception matérielle, ou utilisé dans l'unité d'accès mémoire pour la double lecture/écriture.
La gestion automatique des accès non-alignés est plus complexe. Dans ce cas, l'unité mémoire charge deux mots mémoire et les combine entre eux pour donner le résultat final. Charger deux mots mémoires consécutifs est assez simple, si le registre d'interfaçage est un compteur. L'accès initial charge le premier mot mémoire, puis l'adresse stockée dans le registre d'interfaçage est incrémentée pour démarrer un second accès. Le circuit pour combiner deux mots mémoire contient des registres, des circuits de décalage, des multiplexeurs.
===Le rafraichissement mémoire optimisé et le contrôleur mémoire intégré===
Depuis les années 80, les processeurs sont souvent combinés avec une mémoire principale de type DRAM. De telles mémoires doivent être rafraichies régulièrement pour ne pas perdre de données. Le rafraichissement se fait généralement adresse par adresse, ou ligne par ligne (les lignes sont des super-bytes internes à la DRAM). Le rafraichissement est en théorie géré par le contrôleur mémoire installé sur la carte mère. Mais au tout début de l'informatique, du temps des processeurs 8 bits, le rafraichissement mémoire était géré directement par le processeur.
Si quelques processeurs géraient le rafraichissement mémoire avec des interruptions, d'autres processeurs disposaient d’optimisations pour optimiser le rafraichissement mémoire. Divers processeurs implémentaient de quoi faciliter le rafraichissement par adresse. Par exemple, le processeur Zilog Z80 contenait un compteur de ligne, un registre qui contenait le numéro de la prochaine ligne à rafraichir. Il était incrémenté à chaque rafraichissement mémoire, automatiquement, par le processeur lui-même. Un ''timer'' interne permettait de savoir quand rafraichir la mémoire : quand ce ''timer'' atteignait 0, une commande de rafraichissement était envoyée à la mémoire, et le ''timer'' était ''reset''. Et tout cela était intégré à l'unité d'accès mémoire.
Depuis les années 2000, les processeurs modernes ont un contrôleur mémoire DRAM intégré directement dans le processeur. Ce qui fait qu'ils gèrent non seulement le rafraichissement, mais aussi d'autres fonctions bien pus complexes.
==Le chemin de données et son réseau d'interconnexions==
Nous venons de voir que le chemin de données contient une unité de calcul (parfois plusieurs), des registres isolés, un banc de registre, une unité mémoire. Le tout est chapeauté par une unité de contrôle qui commande le chemin de données, qui fera l'objet des prochains chapitres. Mais il faut maintenant relier registres, ALU et unité mémoire pour que l'ensemble fonctionne. Pour cela, diverses interconnexions internes au processeur se chargent de relier le tout.
Sur les anciens processeurs, les interconnexions sont assez simples et se résument à un ou deux '''bus internes au processeur''', reliés au bus mémoire. C'était la norme sur des architectures assez ancienne, qu'on n'a pas encore vu à ce point du cours, appelées les architectures à accumulateur et à pile. Mais ce n'est plus la solution utilisée actuellement. De nos jours, le réseaux d'interconnexion intra-processeur est un ensemble de connexions point à point entre ALU/registres/unité mémoire. Et paradoxalement, cela rend plus facile de comprendre ce réseau d'interconnexion.
===Introduction propédeutique : l'implémentation des modes d'adressage principaux===
L'organisation interne du processeur dépend fortement des modes d'adressage supportés. Pour simplifier les explications, nous allons séparer les modes d'adressage qui gèrent les pointeurs et les autres. Suivant que le processeur supporte les pointeurs ou non, l'organisation des bus interne est légèrement différente. La différence se voit sur les connexions avec le bus d'adresse et de données.
Tout processeur gère au minimum le '''mode d'adressage absolu''', où l'adresse est intégrée à l'instruction. Le séquenceur extrait l'adresse mémoire de l'instruction, et l'envoie sur le bus d'adresse. Pour cela, le séquenceur est relié au bus d'adresse, le chemin de donnée est relié au bus de données. Le chemin de donnée n'est pas connecté au bus d'adresse, il n'y a pas d'autres connexions.
[[File:Chemin de données sans support des pointeurs.png|centre|vignette|upright=2|Chemin de données sans support des pointeurs]]
Le '''support des pointeurs''' demande d'intégrer des modes d'adressage dédiés : l'adressage indirect à registre, l'adresse base + indice, et les autres. Les pointeurs sont stockés dans le banc de registre et sont modifiés par l'unité de calcul. Pour supporter les pointeurs, le chemin de données est connecté sur le bus d'adresse avec le séquenceur. Suivant le mode d'adressage, le bus d'adresse est relié soit au chemin de données, soit au séquenceur.
[[File:Chemin de données avec support des pointeurs.png|centre|vignette|upright=2|Chemin de données avec support des pointeurs]]
Pour terminer, il faut parler des instructions de '''copie mémoire vers mémoire''', qui copient une donnée d'une adresse mémoire vers une autre. Elles ne se passent pas vraiment dans le chemin de données, mais se passent purement au niveau des registres d’interfaçage. L'usage d'un registre d’interfaçage unique permet d'implémenter ces instructions très facilement. Elle se fait en deux étapes : on copie la donnée dans le registre d’interfaçage, on l'écrit en mémoire RAM. L'adresse envoyée sur le bus d'adresse n'est pas la même lors des deux étapes.
===Le banc de registre est multi-port, pour gérer nativement les opérations dyadiques===
Les architectures RISC et CISC incorporent un banc de registre, qui est connecté aux unités de calcul et au bus mémoire. Et ce banc de registre peut être mono-port ou multiport. S'il a existé d'anciennes architectures utilisant un banc de registre mono-port, elles sont actuellement obsolètes. Nous les aborderons dans un chapitre dédié aux architectures dites canoniques, mais nous pouvons les laisser de côté pour le moment. De nos jours, tous les processeurs utilisent un banc de registre multi-port.
[[File:Chemin de données minimal d'une architecture LOAD-STORE (sans MOV inter-registres).png|centre|vignette|upright=2|Chemin de données minimal d'une architecture LOAD-STORE (sans MOV inter-registres)]]
Le banc de registre multiport est optimisé pour les opérations dyadiques. Il dispose précisément de deux ports de lecture et d'un port d'écriture pour l'écriture. Un port de lecture par opérande et le port d'écriture pour enregistrer le résultat. En clair, le processeur peut lire deux opérandes et écrire un résultat en un seul cycle d'horloge. L'avantage est que les opérations simples ne nécessitent qu'une micro-opération, pas plus.
[[File:ALU data paths.svg|centre|vignette|upright=1.5|Processeur LOAD-STORE avec un banc de registre multiport, avec les trois ports mis en évidence.]]
===Une architecture LOAD-STORE basique, avec adressage absolu===
Voyons maintenant comment l'implémentation d'une architecture RISC très simple, qui ne supporte pas les adressages pour les pointeurs, juste les adressages inhérent (à registres) et absolu (par adresse mémoire). Les instructions LOAD et STORE utilisent l'adressage absolu, géré par le séquenceur, reste à gérer l'échange entre banc de registres et bus de données. Une lecture LOAD relie le bus de données au port d'écriture du banc de registres, alors que l'écriture relie le bus au port de lecture du banc de registre. Pour cela, il faut ajouter des multiplexeurs sur les chemins existants, comme illustré par le schéma ci-dessous.
[[File:Bus interne au processeur sur archi LOAD STORE avec banc de registres multiport.png|centre|vignette|upright=2|Organisation interne d'une architecture LOAD STORE avec banc de registres multiport. Nous n'avons pas représenté les signaux de commandes envoyés par le séquenceur au chemin de données.]]
Ajoutons ensuite les instructions de copie entre registres, souvent appelées instruction COPY ou MOV. Elles existent sur la plupart des architectures LOAD-STORE. Une première solution boucle l'entrée du banc de registres sur son entrée, ce qui ne sert que pour les copies de registres.
[[File:Chemin de données d'une architecture LOAD-STORE.png|centre|vignette|upright=2|Chemin de données d'une architecture LOAD-STORE]]
Mais il existe une seconde solution, qui ne demande pas de modifier le chemin de données. Il est possible de faire passer les copies de données entre registres par l'ALU. Lors de ces copies, l'ALU une opération ''Pass through'', à savoir qu'elle recopie une des opérandes sur sa sortie. Le fait qu'une ALU puisse effectuer une opération ''Pass through'' permet de fortement simplifier le chemin de donnée, dans le sens où cela permet d'économiser des multiplexeurs. Mais nous verrons cela sous peu. D'ailleurs, dans la suite du chapitre, nous allons partir du principe que les copies entre registres passent par l'ALU, afin de simplifier les schémas.
===L'ajout des modes d'adressage indirects à registre pour les pointeurs===
Passons maintenant à l'implémentation des modes d'adressages pour les pointeurs. Avec eux, l'adresse mémoire à lire/écrire n'est pas intégrée dans une instruction, mais est soit dans un registre, soit calculée par l'ALU.
Le premier mode d'adressage de ce type est le mode d'adressage indirect à registre, où l'adresse à lire/écrire est dans un registre. L'implémenter demande donc de connecter la sortie du banc de registres au bus d'adresse. Il suffit d'ajouter un MUX en sortie d'un port de lecture.
[[File:Chemin de données à trois bus.png|centre|vignette|upright=2|Chemin de données à trois bus.]]
Le mode d'adressage base + indice est un mode d'adressage où l'adresse à lire/écrire est calculée à partir d'une adresse et d'un indice, tous deux présents dans un registre. Le calcul de l'adresse implique au minimum une addition et donc l'ALU. Dans ce cas, on doit connecter la sortie de l'unité de calcul au bus d'adresse.
[[File:Bus avec adressage base+index.png|centre|vignette|upright=2|Bus avec adressage base+index]]
Le chemin de données précédent gère aussi le mode d'adressage indirect avec pré-décrément. Pour rappel, ce mode d'adressage est une variante du mode d'adressage indirect, qui utilise une pointeur/adresse stocké dans un registre. La différence est que ce pointeur est décrémenté avant d'être envoyé sur le bus d'adresse. L'implémentation matérielle est la même que pour le mode Base + Indice : l'adresse est lue depuis les registres, décrémentée dans l'ALU, et envoyée sur le bus d'adresse.
Le schéma précédent montre que le bus d'adresse est connecté à un MUX avant l'ALU et un autre MUX après. Mais il est possible de se passer du premier MUX, utilisé pour le mode d'adressage indirect à registre. La condition est que l'ALU supporte l'opération ''pass through'', un NOP, qui recopie une opérande sur sa sortie. L'ALU fera une opération NOP pour le mode d'adressage indirect à registre, un calcul d'adresse pour le mode d'adressage base + indice. Par contre, faire ainsi rendra l'adressage indirect légèrement plus lent, vu que le temps de passage dans l'ALU sera compté.
[[File:Bus avec adressage indirect.png|centre|vignette|upright=2|Bus avec adressages pour les pointeurs, simplifié.]]
Dans ce qui va suivre, nous allons partir du principe que le processeur est implémenté en suivant le schéma précédent, afin d'avoir des schéma plus lisibles.
===L'adressage immédiat et les modes d'adressages exotiques===
Passons maintenant au mode d’adressage immédiat, qui permet de préciser une constante dans une instruction directement. La constante est extraite de l'instruction par le séquenceur, puis insérée au bon endroit dans le chemin de données. Pour les opérations arithmétiques/logiques/branchements, il faut insérer la constante extraite sur l'entrée de l'ALU. Sur certains processeurs, la constante peut être négative et doit alors subir une extension de signe dans un circuit spécialisé.
[[File:Chemin de données - Adressage immédiat avec extension de signe.png|centre|vignette|upright=2|Chemin de données - Adressage immédiat avec extension de signe.]]
L'implémentation précédente gère aussi les modes d'adressage base + décalage et absolu indexé. Pour rappel, le premier ajoute une constante à une adresse prise dans les registres, le second prend une adresse constante et lui ajoute un indice pris dans les registres. Dans les deux cas, on lit un registre, extrait une constante/adresse de l’instruction, additionne les deux dans l'ALU, avant d'envoyer le résultat sur le bus d'adresse. La seule difficulté est de désactiver l'extension de signe pour les adresses.
Le mode d'adressage absolu peut être traité de la même manière, si l'ALU est capable de faire des NOPs. L'adresse est insérée au même endroit que pour le mode d'adressage immédiat, parcours l'unité de calcul inchangée parce que NOP, et termine sur le bus d'adresse.
[[File:Chemin de données avec une ALU capable de faire des NOP.png|centre|vignette|upright=2|Chemin de données avec adressage immédiat étendu pour gérer des adresses.]]
Passons maintenant au cas particulier d'une instruction MOV qui copie une constante dans un registre. Il n'y a rien à faire si l'unité de calcul est capable d'effectuer une opération NOP/''pass through''. Pour charger une constante dans un registre, l'ALU est configurée pour faire un NOP, la constante traverse l'ALU et se retrouve dans les registres. Si l'ALU ne gère pas les NOP, la constante doit être envoyée sur l'entrée d'écriture du banc de registres, à travers un MUX dédié.
[[File:Implémentation de l'adressage immédiat dans le chemin de données.png|centre|vignette|upright=2|Implémentation de l'adressage immédiat dans le chemin de données]]
===Les architectures CISC : les opérations ''load-op''===
Tout ce qu'on a vu précédemment porte sur les processeurs de type LOAD-STORE, souvent confondus avec les processeurs de type RISC, où les accès mémoire sont séparés des instructions utilisant l'ALU. Il est maintenant temps de voir les processeurs CISC, qui gèrent des instructions ''load-op'', qui peuvent lire une opérande depuis la mémoire.
L'implémentation des opérations ''load-op'' relie le bus de donnée directement sur une entrée de l'unité de calcul, en utilisant encore une fois un multiplexeur. L'implémentation parait simple, mais c'est parce que toute la complexité est déportée dans le séquenceur. C'est lui qui se charge de détecter quand la lecture de l'opérande est terminée, quand l'opérande est disponible.
Les instructions ''load-op'' s'exécutent en plusieurs étapes, en plusieurs micro-opérations. Il y a typiquement une étape pour l'opérande à lire en mémoire et une étape de calcul. L'usage d'un registre d’interfaçage permet d'implémenter les instructions ''load-op'' très facilement. Une opération ''load-op'' charge l'opérande en mémoire dans un registre d’interfaçage, puis relier ce registre d’interfaçage sur une des entrées de l'ALU. Un simple multiplexeur suffit pour implémenter le tout, en plus des modifications adéquates du séquenceur.
[[File:Chemin de données d'un CPU CISC avec lecture des opérandes en mémoire.png|centre|vignette|upright=2|Chemin de données d'un CPU CISC avec lecture des opérandes en mémoire]]
Supporter les instructions multi-accès (qui font plusieurs accès mémoire) ne modifie pas fondamentalement le réseau d'interconnexion, ni le chemin de données La raison est que supporter les instructions multi-accès se fait au niveau du séquenceur. En réalité, les accès mémoire se font en série, l'un après l'autre, sous la commande du séquenceur qui émet plusieurs micro-opérations mémoire consécutives. Les données lues sont placées dans des registres d’interactivement mémoire, ce qui demande d'ajouter des registres d’interfaçage mémoire en plus.
==Annexe : le cas particulier du pointeur de pile==
Le pointeur de pile est un registre un peu particulier. Il peut être placé dans le chemin de données ou dans le séquenceur, voire dans l'unité de chargement, tout dépend du processeur. Tout dépend de si le pointeur de pile gère une pile d'adresses de retour ou une pile d'appel.
===Le pointeur de pile non-adressable explicitement===
Avec une pile d'adresse de retour, le pointeur de pile n'est pas adressable explicitement, il est juste adressé implicitement par des instructions d'appel de fonction CALL et des instructions de retour de fonction RET. Le pointeur de pile est alors juste incrémenté ou décrémenté par un pas constant, il ne subit pas d'autres opérations, son adressage est implicite. Il est juste incrémenté/décrémenté par pas constants, qui sont fournis par le séquenceur. Il n'y a pas besoin de le relier au chemin de données, vu qu'il n'échange pas de données avec les autres registres. Il y a alors plusieurs solutions, mais la plus simple est de placer le pointeur de pile dans le séquenceur et de l'incrémenter par un incrémenteur dédié.
Quelques processeurs simples disposent d'une pile d'appel très limitée, où le pointeur de pile n'est pas adressable explicitement. Il est adressé implicitement par les instruction CALL, RET, mais aussi PUSH et POP, mais aucune autre instruction ne permet cela. Là encore, le pointeur de pile ne communique pas avec les autres registres. Il est juste incrémenté/décrémenté par pas constants, qui sont fournis par le séquenceur. Là encore, le plus simple est de placer le pointeur de pile dans le séquenceur et de l'incrémenter par un incrémenteur dédié.
Dans les deux cas, le pointeur de pile est placé dans l'unité de contrôle, le séquenceur, et est associé à un incrémenteur dédié. Il se trouve que cet incrémenteur est souvent partagé avec le ''program counter''. En effet, les deux sont des adresses mémoire, qui sont incrémentées et décrémentées par pas constants, ne subissent pas d'autres opérations (si ce n'est des branchements, mais passons). Les ressemblances sont suffisantes pour fusionner les deux circuits. Ils peuvent donc avoir un '''incrémenteur partagé'''.
L'incrémenteur en question est donc partagé entre pointeur de pile, ''program counter'' et quelques autres registres similaires. Par exemple, le Z80 intégrait un registre pour le rafraichissement mémoire, qui était réalisé par le CPU à l'époque. Ce registre contenait la prochaine adresse mémoire à rafraichir, et était incrémenté à chaque rafraichissement d'une adresse. Et il était lui aussi intégré au séquenceur et incrémenté par l'incrémenteur partagé.
[[File:Organisation interne d'une architecture à pile.png|centre|vignette|upright=2|Organisation interne d'une architecture à pile]]
===Le pointeur de pile adressable explicitement===
Maintenant, étudions le cas d'une pile d'appel, précisément d'une pile d'appel avec des cadres de pile de taille variable. Sous ces conditions, le pointeur de pile est un registre adressable, avec un nom/numéro de registre dédié. Tel est par exemple le cas des processeurs x86 avec le registre ESP (''Extended Stack Pointer''). Il est manipulé par les instructions CALL, RET, PUSH et POP, mais aussi par les instructions d'addition/soustraction pour gérer des cadres de pile de taille variable. De plus, il peut servir d'opérande pour des calculs d'adresse, afin de lire/écrire des variables locales, les arguments d'une fonction, et autres.
Dans ce cas, la meilleure solution est de placer le pointeur de pile dans le banc de registre généraux, avec les autres registres entiers. En faisant cela, la manipulation du pointeur de pile est faite par l'unité de calcul entière, pas besoin d'utiliser un incrémenteur dédiée. Il a existé des processeurs qui mettaient le pointeur de pile dans le banc de registre, mais l'incrémentaient avec un incrémenteur dédié, mais nous les verrons dans le chapitre sur les architectures à accumulateur. La raison est que sur les processeurs concernés, les adresses ne faisaient pas la même taille que les données : c'était des processeurs 8 bits, qui géraient des adresses de 16 bits.
==Annexe : l'implémentation du système d'''aliasing'' des registres des CPU x86==
Il y a quelques chapitres, nous avions parlé du système d'''aliasing'' des registres des CPU x86. Pour rappel, il permet de donner plusieurs noms de registre pour un même registre. Plus précisément, pour un registre 64 bits, le registre complet aura un nom de registre, les 32 bits de poids faible auront leur nom de registre dédié, idem pour les 16 bits de poids faible, etc. Il est possible de faire des calculs sur ces moitiés/quarts/huitièmes de registres sans problème.
===L'''aliasing'' du 8086, pour les registres 16 bits===
[[File:Register 8086.PNG|vignette|Register 8086]]
L'implémentation de l'''aliasing'' est apparue sur les premiers CPU Intel 16 bits, notamment le 8086. En tout, ils avaient quatre registres généraux 16 bits : AX, BX, CX et DX. Ces quatre registres 16 bits étaient coupés en deux octets, chacun adressable. Par exemple, le registre AX était coupé en deux octets nommés AH et AL, chacun ayant son propre nom/numéro de registre. Les instructions d'addition/soustraction pouvaient manipuler le registre AL, ou le registre AH, ce qui modifiait les 8 bits de poids faible ou fort selon le registre choisit.
Le banc de registre ne gére que 4 registres de 16 bits, à savoir AX, BX, CX et DX. Lors d'une lecture d'un registre 8 bits, le registre 16 bit entier est lu depuis le banc de registre, mais les bits inutiles sont ignorés. Par contre, l'écriture peut se faire soit avec 16 bits d'un coup, soit pour seulement un octet. Le port d'écriture du banc de registre peut être configuré de manière à autoriser l'écriture soit sur les 16 bits du registre, soit seulement sur les 8 bits de poids faible, soit écrire dans les 8 bits de poids fort.
[[File:Port d'écriture du banc de registre du 8086.png|centre|vignette|upright=2.5|Port d'écriture du banc de registre du 8086]]
Une opération sur un registre 8 bits se passe comme suit. Premièrement, on lit le registre 16 bits complet depuis le banc de registre. Si l'on a sélectionné l'octet de poids faible, il ne se passe rien de particulier, l'opérande 16 bits est envoyée directement à l'ALU. Mais si on a sélectionné l'octet de poids fort, la valeur lue est décalée de 7 rangs pour atterrir dans les 8 octets de poids faible. Ensuite, l'unité de calcul fait un calcul avec cet opérande, un calcul 16 bits tout ce qu'il y a de plus classique. Troisièmement, le résultat est enregistré dans le banc de registre, en le configurant convenablement. La configuration précise s'il faut enregistrer le résultat dans un registre 16 bits, soit seulement dans l'octet de poids faible/fort.
Afin de simplifier le câblage, les 16 bits des registres AX/BX/CX/DX sont entrelacés d'une manière un peu particulière. Intuitivement, on s'attend à ce que les bits soient physiquement dans le même ordre que dans le registre : le bit 0 est placé à côté du bit 1, suivi par le bit 2, etc. Mais à la place, l'octet de poids fort et de poids faible sont mélangés. Deux bits consécutifs appartiennent à deux octets différents. Le tout est décrit dans le tableau ci-dessous.
{|class="wikitable"
|-
! Registre 16 bits normal
| class="f_bleu" | 15
| class="f_bleu" | 14
| class="f_bleu" | 13
| class="f_bleu" | 12
| class="f_bleu" | 11
| class="f_bleu" | 10
| class="f_bleu" | 9
| class="f_bleu" | 8
| class="f_rouge" | 7
| class="f_rouge" | 6
| class="f_rouge" | 5
| class="f_rouge" | 4
| class="f_rouge" | 3
| class="f_rouge" | 2
| class="f_rouge" | 1
| class="f_rouge" | 0
|-
! Registre 16 bits du 8086
| class="f_bleu" | 15
| class="f_rouge" | 7
| class="f_bleu" | 14
| class="f_rouge" | 6
| class="f_bleu" | 13
| class="f_rouge" | 5
| class="f_bleu" | 12
| class="f_rouge" | 4
| class="f_bleu" | 11
| class="f_rouge" | 3
| class="f_bleu" | 10
| class="f_rouge" | 2
| class="f_bleu" | 9
| class="f_rouge" | 1
| class="f_bleu" | 8
| class="f_rouge" | 0
|}
En faisant cela, le décaleur en entrée de l'ALU est bien plus simple. Il y a 8 multiplexeurs, mais le câblage est bien plus simple. Par contre, en sortie de l'ALU, il faut remettre les bits du résultat dans l'ordre adéquat, celui du registre 8086. Pour cela, les interconnexions sur le port d'écriture sont conçues pour. Il faut juste mettre les fils de sortie de l'ALU sur la bonne entrée, par besoin de multiplexeurs.
===L'''aliasing'' sur les processeurs x86 32/64 bits===
Les processeurs x86 32 et 64 bits ont un système d'''aliasing'' qui complète le système précédent. Les processeurs 32 bits étendent les registres 16 bits existants à 32 bits. Pour ce faire, le registre 32 bit a un nouveau nom de registre, distincts du nom de registre utilisé pour l'ancien registre 16 bits. Il est possible d'adresser les 16 bits de poids faible de ce registre, avec le même nom de registre que celui utilisé pour le registre 16 sur les processeurs d'avant. Même chose avec les processeurs 64, avec l'ajout d'un nouveau nom de registre pour adresser un registre de 64 bit complet.
En soit, implémenter ce système n'est pas compliqué. Prenons le cas du registre RAX (64 bits), et de ses subdivisions nommées EAX (32 bits), AX (16 bits). À l'intérieur du banc de registre, il n'y a que le registre RAX. Le banc de registre ne comprend qu'un seul nom de registre : RAX. Les subdivisions EAX et AX n'existent qu'au niveau de l'écriture dans le banc de registre. L'écriture dans le banc de registre est configurable, de manière à ne modifier que les bits adéquats. Le résultat d'un calcul de l'ALU fait 64 bits, il est envoyé sur le port d'écriture. À ce niveau, soit les 64 bits sont écrits dans le registre, soit seulement les 32/16 bits de poids faible. Le système du 8086 est préservé pour les écritures dans les 16 bits de poids faible.
<noinclude>
{{NavChapitre | book=Fonctionnement d'un ordinateur
| prev=Les composants d'un processeur
| prevText=Les composants d'un processeur
| next=L'unité de chargement et le program counter
| nextText=L'unité de chargement et le program counter
}}
</noinclude>
7eelf6u4hbadj7f5duaks4xjav1sfj7
Les cartes graphiques/Le processeur de commandes
0
69571
745291
744056
2025-06-24T13:51:30Z
Mewtow
31375
/* L'exemple du NV1 */
745291
wikitext
text/x-wiki
Dans ce chapitre, nous allons parler de tous les intermédiaires qui se placent entre une application de rendu 3D et les circuits de rendu 3D proprement dit. En premier lieu, on trouve le pilote de la carte graphique, aux fonctions multiples et variées. En second lieu, nous allons parler d'un circuit de la carte graphique qui fait l'interface entre le logiciel et les circuits de rendu 3D de la carte graphique : le processeur de commandes. Ce processeur de commandes est un circuit qui sert de chef d'orchestre, qui pilote le fonctionnement global de la carte graphique. API 3D, pilote de carte graphique et processeur de commande travaillent de concert pour que l'application communique avec la carte graphique. Nous allons d'abord voir le pilote de la carte graphique, avant de voir le fonctionnement du processeur de commande.
==Le pilote de carte graphique==
Le pilote de la carte graphique est un logiciel qui s'occupe de faire l'interface entre le logiciel et la carte graphique. De manière générale, les pilotes de périphérique sont des intermédiaires entre les applications/APIs et le matériel. En théorie, le système d'exploitation est censé jouer ce rôle, mais il n'est pas programmé pour être compatible avec tous les périphériques vendus sur le marché. À la place, le système d'exploitation ne gère pas directement certains périphériques, mais fournit de quoi ajouter ce qui manque. Le pilote d'un périphérique sert justement à ajouter ce qui manque : ils ajoutent au système d'exploitation de quoi reconnaître le périphérique, de quoi l'utiliser au mieux. Pour une carte graphique, il a diverses fonctions que nous allons voir ci-dessous.
Avant toute chose, précisons que les systèmes d'exploitation usuels (Windows, Linux, MacOsX et autres) sont fournis avec un pilote de carte graphique générique, compatible avec la plupart des cartes graphiques existantes. Rien de magique derrière cela : toutes les cartes graphiques vendues depuis plusieurs décennies respectent des standards, comme le VGA, le VESA, et d'autres. Et le pilote de base fournit avec le système d'exploitation est justement compatible avec ces standards minimaux. Mais le pilote ne peut pas profiter des fonctionnalités qui sont au-delà de ce standard. L'accélération 3D est presque inexistante avec le pilote de base, qui ne sert qu'à faire du rendu 2D (de quoi afficher l’interface de base du système d'exploitation, comme le bureau de Windows). Et même pour du rendu 2D, la plupart des fonctionnalités de la carte graphique ne sont pas disponibles. Par exemple, certaines résolutions ne sont pas disponibles, notamment les grandes résolutions. Ou encore, les performances sont loin d'être excellentes. Si vous avez déjà utilisé un PC sans pilote de carte graphique installé, vous avez certainement remarqué qu'il était particulièrement lent.
===La gestion des interruptions===
Une fonction particulièrement importante du pilote de carte graphique est la réponse aux interruptions lancées par la carte graphique. Pour rappel, les interruptions sont une fonctionnalité qui permet à un périphérique de communiquer avec le processeur, tout en économisant le temps de calcul de ce dernier. Typiquement, lorsqu'un périphérique lance une interruption, le processeur stoppe son travail et exécute automatiquement une routine d'interruption, un petit programme qui s'occupe de gérer le périphérique, de faire ce qu'il faut, ce que l'interruption a demandé.
Il y a plusieurs interruptions possibles, chacune ayant son propre numéro et sa fonction dédiée, chacune ayant sa propre routine d'interruption. Après tout, la routine qui gère un débordement de la mémoire vidéo n'est pas la même que celle qui gère un changement de fréquence du GPU ou la fin du calcul d'une image 3D. Le pilote fournit l'ensemble des routines d'interruptions nécessaires pour gérer la carte graphique.
===La compilation des ''shaders''===
Le pilote de carte graphique est aussi chargé de traduire les ''shaders'' en code machine. En soi, cette étape est assez complexe, et ressemble beaucoup à la compilation d'un programme informatique normal. Les ''shaders'' sont écrits dans un langage de programmation de haut niveau, comme le HLSL ou le GLSL, mais ce n'est pas ce code source qui est compilé par le pilote de carte graphique. À la place, les ''shaders'' sont pré-compilés vers un langage dit intermédiaire, avant d'être compilé par le pilote en code machine. Le langage intermédiaire, comme son nom l'indique, sert d'intermédiaire entre le code source écrit en HLSL/GLSL et le code machine exécuté par la carte graphique. Il ressemble à un langage assembleur, mais reste malgré tout assez générique pour ne pas être un véritable code machine. Par exemple, il y a peu de limitations quant au nombre de processeurs ou de registres. En clair, il y a deux passes de compilation : une passe de traduction du code source en langage intermédiaire, puis une passe de compilation du code intermédiaire vers le code machine. Notons que la première passe est réalisée par le programmeur des ''shaders'', non pas par l'utilisateur. Par exemple, les ''shaders'' d'un jeu vidéo sont fournis déjà pré-compilés : les fichiers du jeu ne contiennent pas le code source GLSL/HLSL, mais du code intermédiaire.
L'avantage de cette méthode est que le travail du pilote est fortement simplifié. Le pilote de périphérique pourrait compiler directement du code HLSL/GLSL, mais le temps de compilation serait très long et cela aurait un impact sur les performances. Avec l'usage du langage intermédiaire, le gros du travail à été fait lors de la première passe de compilation et le pilote graphique ne fait que finir le travail. Les optimisations importantes ont déjà été réalisées lors de la première passe. Il doit bien faire la traduction, ajouter quelques optimisations de bas niveau par-ci par-là, mais rien de bien gourmand en processeur. Autant dire que cela économise plus le processeur que si on devait compiler complètement les ''shaders'' à chaque exécution.
Fait amusant, il faut savoir que le pilote peut parfois remplacer les ''shaders'' d'un jeu vidéo à la volée. Les pilotes récents embarquent en effet des ''shaders'' alternatifs pour les jeux les plus vendus et/ou les plus populaires. Lorsque vous lancez un de ces jeux vidéo et que le ''shader'' originel s'exécute, le pilote le détecte automatiquement et le remplace par la version améliorée, fournie par le pilote. Évidemment, le ''shader'' alternatif du pilote est optimisé pour le matériel adéquat. Cela permet de gagner en performance, voire en qualité d'image, sans pour autant que les concepteurs du jeu n'aient quoique ce soit à faire.
Enfin, certains ''shaders'' sont fournis par le pilote pour d'autres raisons. Par exemple, les cartes graphiques récentes n'ont pas de circuits fixes pour traiter la géométrie. Autant les anciennes cartes graphiques avaient des circuits de T&L qui s'en occupaient, autant tout cela doit être émulé sur les machines récentes. Sans cette émulation, les vieux jeux vidéo conçus pour exploiter le T&L et d'autres technologies du genre ne fonctionneraient plus du tout. Émuler les circuits fixes disparus sur les cartes récentes est justement le fait de ''shaders'', présents dans le pilote de carte graphique.
===Les autres fonctions===
Le pilote a aussi bien d'autres fonctions. Par exemple, il s'occupe d'initialiser la carte graphique, de fixer la résolution, d'allouer la mémoire vidéo, de gérer le curseur de souris matériel, etc.
==Les commandes graphiques/vidéo/autres : des ordres envoyés au GPU==
Tous les traitements que la carte graphique doit effectuer, qu'il s'agisse de rendu 2D, de calculs 2D, du décodage matérielle d'un flux vidéo, ou de calculs généralistes, sont envoyés par le pilote de la carte graphique, sous la forme de '''commandes'''. Ces commandes demandent à la carte graphique d'effectuer une opération 2D, ou une opération 3D, ou encore le rendu d'une vidéo.
Les commandes graphiques en question varient beaucoup selon la carte graphique. Les commandes sont régulièrement revues et chaque nouvelle architecture a quelques changements par rapport aux modèles plus anciens. Des commandes peuvent apparaître, d'autres disparaître, d'autre voient leur fonctionnement légèrement altéré, etc.
Mais dans les grandes lignes, on peut classer ces commandes en quelques grands types. Les commandes les plus importantes s'occupent de l'affichage 3D : afficher une image à partir de paquets de sommets, préparer le passage d'une image à une autre, changer la résolution, etc. Plus rare, d'autres commandes servent à faire du rendu 2D : afficher un polygone, tracer une ligne, coloriser une surface, etc. Sur les cartes graphiques qui peuvent accélérer le rendu des vidéos, il existe des commandes spécialisées pour l’accélération des vidéos.
Pour donner quelques exemples, prenons les commandes 2D de la carte graphique AMD Radeon X1800.
{|class="wikitable"
|-
! Commandes 2D
! Fonction
|-
|PAINT
|Peindre un rectangle d'une certaine couleur
|-
|PAINT_MULTI
|Peindre des rectangles (pas les mêmes paramètres que PAINT)
|-
|BITBLT
|Copie d'un bloc de mémoire dans un autre
|-
|BITBLT_MULTI
|Plusieurs copies de blocs de mémoire dans d'autres
|-
|TRANS_BITBLT
|Copie de blocs de mémoire avec un masque
|-
|NEXTCHAR
|Afficher un caractère avec une certaine couleur
|-
|HOSTDATA_BLT
|Écrire une chaîne de caractères à l'écran ou copier une série d'image bitmap dans la mémoire vidéo
|-
|POLYLINE
|Afficher des lignes reliées entre elles
|-
|POLYSCANLINES
|Afficher des lignes
|-
|PLY_NEXTSCAN
|Afficher plusieurs lignes simples
|-
|SET_SCISSORS
|Utiliser des coupes (ciseaux)
|-
|LOAD_PALETTE
|Charger la palette pour affichage 2D
|}
===Le tampon de commandes===
L'envoi des commandes à la carte graphique ne se fait pas directement. La carte graphique n'est pas toujours libre pour accepter une nouvelle commande, soit parce qu'elle est occupée par une commande précédente, soit parce qu'elle fait autre chose. Il faut alors faire patienter les données tant que la carte graphique est occupée. Les pilotes de la carte graphique vont les mettre en attente dans le '''tampon de commandes'''.
Le tampon de commandes est ce qu'on appelle une file, une zone de mémoire dans laquelle on stocke des données dans l'ordre d'ajout. Si le tampon de commandes est plein, le driver n'accepte plus de demandes en provenance des applications. Un tampon de commandes plein est généralement mauvais signe : cela signifie que la carte graphique est trop lente pour traiter les demandes qui lui sont faites. Par contre, il arrive que le tampon de commandes soit vide : dans ce cas, c'est simplement que la carte graphique est trop rapide comparé au processeur, qui n'arrive alors pas à donner assez de commandes à la carte graphique pour l'occuper suffisamment.
Le tampon de commande est soit une mémoire FIFO séparée, soit une portion de la mémoire vidéo. Le NV1, la toute première carte graphique NVIDIA, utilisait une mémoire FIFO intégrée à la carte graphique, séparée des autres circuits. Le processeur envoyait les commandes par rafale au GPU, à savoir qu'il envoyait plusieurs dizaines ou centaines de commandes à la suite, avant de passer à autre chose. Le GPU traitait les commandes une par une, à un rythme bien plus lent. Le processeur pouvait consulter la FIFO pour savoir s'il restait des entrées libres et combien. Le processeur savait ainsi combien d'écritures il pouvait envoyer en une fois au GPU.
: Le fonctionnement de la FIFO du NV1 est décrit dans le brevet ''US5805930A : System for FIFO informing the availability of stages to store commands which include data and virtual address sent directly from application programs''.
===Le processeur de commandes===
Le '''processeur de commande''' est un circuit qui gère les commandes envoyées par le processeur. Il lit la commande la plus ancienne dans le tampon de commande, l’interprète, l’exécute, puis passe à la suivante. Le processeur de commande est un circuit assez compliqué. Sur les cartes graphiques anciennes, c'était un circuit séquentiel complexe, fabriqué à la main et était la partie la plus complexe du processeur graphique. Sur les cartes graphiques modernes, c'est un véritable microcontrôleur, avec un processeur, de la mémoire RAM, etc.
Lors de l'exécution d'une commande, le processeur de commande pilote les circuits de la carte graphique. Précisément, il répartit le travail entre les différents circuits de la carte graphique et assure que l'ordre du pipeline est respecté. Pour donner un exemple, prenons la première carte graphique de NVIDIA, le NV1. Il s'agissait d'une carte multimédia qui incorporait non seulement une carte 2D/3D, mais aussi une carte son un contrôleur de disque et de quoi communiquer avec des manettes. De plus, il incorporait un contrôleur DMA pour échanger des données entre RAM système et les autres circuits. Et à tout cela, il fallait ajouter la FIFO de commandes et le processeur de commande. L'ensemble était relié par un bus interne à la carte graphique.
Le processeur de commande servait de gestionnaire principal. Il envoyait des ordres internes aux circuits de rendu 3D, à sa carte son, au contrôleur DMA. Par exemple, lorsque le processeur voulait copier des données en mémoire vidéo, il envoyait une commande de copie, qui était stockée dans le tampon de commande, puis était exécutée par le processeur de commande. Le processeur de commande envoyait alors les ordres adéquats au contrôleur DMA, qui faisait la copie des données. Lors d'un rendu 3D, une commande de rendu 3D est envoyée au NV1, le processeur de commande la traite et l'envoi dans les circuits de rendu 3D.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
Les GPU modernes sont plus complexes, ce qui fait que le processeur de commande a plus de travail. Parmis les nombreuses tâches qui lui sont confiées, il répartit les ''shaders'' sur les processeurs de ''shaders'', en faisant en sorte qu'ils soient utilisés le plus possible. Dans le cas le plus simple, les unités sont alimentées en vertex/pixels les unes après les autres (principe du round-robin), mais des stratégies plus optimisées sont la règle de nos jours. Cela est très important sur les cartes graphiques : répartir plusieurs commandes sur plusieurs processeurs est une tâche difficile. Un chapitre entier sera d'ailleurs dédié à ce sujet.
Il envoie aussi certaines commandes aux circuits fixes, comme l’''input assembler''. Là encore, il faut en sorte que les circuits fixes soient utilisés le mieux possible. Sa tâche est compliquée par le fait que les GPU récents ont plusieurs unités de traitement des sommets et des pixels, plusieurs ROP, plusieurs unités de textures, etc. Et c'est en partie le travail du processeur de commande que de répartir les calculs sur ces différentes unités. C'est là un problème assez compliqué pour le processeur de commande et ce pour tout un tas de raisons que nous ne ferons que survoler. Les contraintes d'ordonnancement de l'API et les règles de celle-ci sont une partie de la réponse. Mais d'autres raisons sont intrinsèques au rendu 3D ou à la conception des circuits.
[[File:Architecture de base d'une carte 3D - 1.png|centre|vignette|upright=2.5|Architecture de base d'une carte 3D - 1]]
Le processeur de commande n'a pas qu'un rôle de répartiteur. Il connait à tout instant l'état de la carte graphique, l'état de chaque sous-circuit. L'implémentation est variable suivant le GPU. La plus simple ajoute un registre d'état à chaque circuit, qui, est consultable en temps réel par le processeur de commande. Mais il est possible d'utiliser un système d'interruptions interne à la puce, ou circuits préviennent le processeur de commande quand ils ont terminé leur travail. Grâce à cela, le processeur de commandes garde en mémoire l'état de traitement de chaque commande : est-ce qu'elle est en train de s’exécuter sur le processeur graphique, est-ce qu'elle est mise en pause, est-ce qu'elle attend une donnée en provenance de la mémoire, est-ce qu'elle est terminée, etc.
Pour résumer, le processeur de commande lit une commande, répartit le travail sur les différents circuits du GPU, et vérifie en permanence leur état pour déterminer si la commande est en pause, terminée, en cours d'exécution, etc.
==Le fonctionnement du processeur de commandes==
Intuitivement, on se dit que le processeur de commande procède une commande à la fois. Mais les GPU modernes sont plus intelligents et peuvent exécuter plusieurs commandes en même temps, dans des circuits séparés. De nombreuses optimisations de ce type sont utilisées pour gagner en performance. Voyons comment un processeur de commande moderne fonctionne.
===L'ordonnancement et la gestion des ressources===
Le processeur de commande doit souvent mettre en pause certains circuits du pipeline, ainsi que les circuits précédents. Par exemple, si tous les processeurs de ''vertex shader'' sont occupés, l’''input assembler'' ne peut pas charger le paquet suivant car il n'y a aucun processeur de libre pour l’accueillir. Dans ce cas, l'étape d’''input assembly'' est mise en pause en attendant qu'un processeur de ''shader'' soit libre.
La même chose a lieu pour l'étape de rastérisation : si aucun processeur de ''shader'' n'est libre, elle est mise en pause. Sauf que pour la rastérisation, cela a des conséquences sur les circuits précédents la rastérisation. Si un processeur de ''shader'' veut envoyer quelque chose au rastériseur alors qu'il est en pause, il devra lui aussi attendre que le rastériseur soit libre. On a la même chose quand les ROP sont occupés : les ''pixel shader'' ou l'unité de texture doivent parfois être mis en pause, ce qui peut entrainer la mise en pause des étapes précédentes, etc.
En clair : plus un étape est situé vers la fin du pipeline graphique, plus sa mise en pause a de chances de se répercuter sur les étapes précédentes et de les mettre en pause aussi. Le processeur de commande doit gérer ces situations.
===Le pipelining des commandes===
Dans les cartes graphiques les plus rudimentaires, le processeur de commande exécute les commandes dans l'ordre. Il exécute une commande çà la fois et attend qu'une commande soit terminé pour en lancer une autre. Plusieurs commandes sont accumulées dans le tampon de commande, le processeur de commande les traite de la plus ancienne vers la plus récente. Du moins, les anciennes cartes graphiques faisaient comme cela, les cartes graphiques modernes sont un peu plus complexes. Elles n'attendent pas qu'une commande soit totalement terminée avant d'en lancer une nouvelle. Les processeurs de commande actuels sont capables d’exécuter plusieurs commandes en même temps.
L'intérêt est un gain en performance assez important. Pour ceux qui ont déjà eu un cours d'architecture des ordinateurs avancé, lancer plusieurs commandes en même temps permet une forme de pipeline, dont les gains sont substantiels. Par exemple, imaginez qu'une commande de rendu 3D soit bien avancée et n'utilise que les processeurs de ''shaders'' et les ROP, mais laisse les unités géométriques libres. Il est alors possible de démarrer la commande suivante en avance, afin qu'elle remplisse les unités géométriques inutilisées. On a alors deux commandes en cours de traitement : une commande qui utilise la fin du pipeline uniquement, une autre qui utilise seulement le début du pipeline graphique.
Le processeur de commande gère donc plusieurs commandes simultanées, à savoir plusieurs commandes qui en sont à des étapes différentes du pipeline. On peut avoir une commande qui est en cours dans l'étage de gestion de la géométrie, une autre dans le rastériseur, et une autre dans les unités de traitement des pixels. Le fait que les étapes du pipeline graphique sont à effectuer dans un ordre bien précis permet ce genre de chose assez facilement.
Une autre possibilité est de faire des rendus en parallèle. Par exemple, imaginez qu'on ait des circuits séparés pour le rendu 2D et 3D. Prenons l'exemple où le calcul d'une image finale demande de faire un rendu 3D suivi d'un rendu 2D, par exemple un jeu vidéo en 3D qui a un HUD dessiné en 2D. Dans ce cas, on peut démarrer le calcul de l'image suivante dans le pipeline 3D pendant que la précédente est dans les circuits 2D, au lieu de laisser inutilisé le pipeline 3D pendant le rendu 2D.
Mais c'est là un défi compliqué à relever pour le processeur de commande, qui donne lieu à son lot de problèmes. Le problème principal est le partage de ressources entre commandes. Par exemple, on peut prendre le cas où une commande n'utilise pas beaucoup les processeurs de ''shaders''. On pourrait imaginer lancer une seconde commande en parallèle, afin que la seconde commande utiliser les processeurs de ''shaders'' inutilisés. Mais cela n'est possible que si les circuits fixes sont capables d'accepter une seconde commande. Si la première commande sature les circuits fixe, le lancement de la seconde commande sera empêché. Mais si ce n'est pas le cas, les deux commandes pourront être lancées en même temps. Pareil pour le débit mémoire, qui doit alors être partagé entre deux commandes simultanées, ce qui est à prendre en compte.
===La synchronisation de l’exécution des commandes avec le processeur===
Il arrive que le processeur doive savoir où en est le traitement des commandes, ce qui est très utile pour la gestion de la mémoire vidéo.
Un premier exemple : comment éviter de saturer une carte graphique de commande, alors qu'on ne sait pas si elles traite les commandes rapidement ou non ? L'idéal est de regarder où en est la carte graphique dans l'exécution des commandes. Si elle n'en est qu'au début du tampon de commande et que celui-ci est bien remplit, alors il vaut mieux lever le pied et ne pas envoyer de nouvelles commandes. A l'inverse, si le tampon de commande est presque vide, envoyer des commandes est la meilleure idée possible.
Un autre exemple un peu moins parlant est celui de la gestion de la mémoire vidéo. Rappelons qu'elle est réalisée par le pilote de la carte graphique, sur le processeur principal, qui décide d'allouer et de libérer de la mémoire vidéo. Pour donner un exemple d'utilisation, imaginez que le pilote de la carte graphique doive libérer de la mémoire pour pouvoir ajouter une nouvelle commande. Comment éviter d'enlever une texture tant que les commandes qui l'utilisent ne sont pas terminées ? Ce problème ne se limite pas aux textures, mais vaut pour tout ce qui est placé en mémoire vidéo.
Il faut donc que la carte graphique trouve un moyen de prévenir le processeur que le traitement de telle commande est terminée, que telle commande en est rendue à tel ou tel point, etc. Pour cela, on a globalement deux grandes solutions. La première est celle des interruptions, abordées dans les chapitres précédents. Mais elles sont assez couteuses et ne sont utilisées que pour des évènements vraiment importants, critiques, qui demandent une réaction rapide du processeur. L'autre solution est celle du ''pooling'', où le processeur monitore la carte graphique à intervalles réguliers. Deux solutions bien connues de ceux qui ont déjà lu un cours d'architecture des ordinateurs digne de ce nom, rien de bien neuf : la carte graphique communique avec le processeur comme tout périphérique.
Pour faciliter l'implémentation du ''pooling'', la carte graphique contient des registres de statut, dans le processeur de commande, qui mémorisent tout ou partie de l'état de la carte graphique. Si un problème survient, certains bits du registre de statut seront mis à 1 pour indiquer que telle erreur bien précise a eu lieu. Il existe aussi un registre de statut qui mémorise le numéro de la commande en cours, ou de la dernière commande terminée, ce qui permet de savoir où en est la carte graphique dans l'exécution des commandes. Et certaines registres de statut sont dédiés à la gestion des commandes. Ils sont totalement programmable, la carte graphique peut écrire dedans sans problème.
Les cartes graphiques récentes incorporent des commandes pour modifier ces registres de statut au besoin. Elles sont appelées des '''commandes de synchronisation''' : les barrières (''fences''). Elles permettent d'écrire une valeur dans un registre de statut quand telle ou telle condition est remplie. Par exemple, si jamais une commande est terminée, on peut écrire la valeur 1 dans tel ou tel registre. La condition en question peut être assez complexe et se résumer à quelque chose du genre : "si jamais toutes les ''shaders'' ont fini de traiter tel ensemble de textures, alors écrit la valeur 1024 dans le registre de statut numéro 9".
===La synchronisation de l’exécution des commandes intra-GPU===
Lancer plusieurs commandes en parallèle dans le pipeline permet de gagner en performance.Mais cela a un gros défaut : il n'est pas garantit que les commandes se terminent dans l'ordre. Si on démarre une seconde commande après la première, il arrive que la seconde finisse avant. Pire : il n'est pas garantit qu'elles s'exécutent dans l'ordre. Dans la plupart des cas, cela ne pose pas de problèmes. Mais dans d'autres, cela donne des résultats erronés.
Pour donner un exemple, utilisons les commandes de rendu 2D vues plus haut, histoire de simplifier le tout. Imaginez que vous codez un jeu vidéo et que le rendu se fait en 2D, partons sur un jeu de type FPS. Vous avez une série de commandes pour calculer l'image à rendre à l'écran, suivie par une série de commandes finales pour dessiner le HUB. Pour dessiner le HUD, vous utilisez des commandes HOSTDATA_BLT, dont le but est d'écrire une chaîne de caractères à l'écran ou copier une série d'image bitmap dans la mémoire vidéo. Et bien ces commandes HOSTDATA_BLT doivent démarrer une fois que le rendu de l'image est complétement terminé. Imaginez que vous dessiniez le HUD sur une portion de l'image pas encore terminée et que celui-ci est effacé par des écritures ultérieures !
Pour éviter tout problème, les GPU incorporent des commandes de synchronisation intra-GPU, destinées au processeur de commande, aussis appelées des '''commandes de sémaphore'''. Elles permettent de mettre en pause le lancement d'une nouvelle commande tant que les précédentes ne sont pas totalement ou partiellement terminées. Les plus simples empêchent le démarrage d'une commande tant que les précédentes ne sont pas terminées. D'autres permettent de démarrer une commande si et seulement si certaines commandes sont terminées. D'autres sont encore plus précises : elles empêchent le démarrage d'une nouvelle commande tant qu'une commande précédente n'a pas atteint un certain stade de son exécution. Là encore, de telles commandes sont des commandes du style "attend que le registre de statut numéro N contienne la valeur adéquate avant de poursuivre l'exécution des commandes".
Les commandes en question peuvent être sur le même modèle que précédemment, à savoir des commandes qui lisent les registres de statut. Mais elles peuvent aussi se baser sur le fait que telle ou telle ressource de rendu est libre ou non. Des commandes successives se partagent souvent des données, et que de nombreuses commandes différentes peuvent s'exécuter en même temps. Or, si une commande veut modifier les données utilisées par une autre commande, il faut que l'ordre des commandes soit maintenu : la commande la plus récente ne doit pas modifier les données utilisées par une commande plus ancienne. Avec ce modèle, les sémaphores bloquent une commande tant qu'une ressource (une texture) est utilisée par une autre commande.
{|class="wikitable"
|-
! Commandes de synchronisation
! Fonction
|-
|NOP
|Ne rien faire
|-
|WAIT_SEMAPHORE
|Attendre la synchronisation avec un sémaphore
|-
|WAIT_MEM
|Attendre que la mémoire vidéo soit disponible et inoccupée par le CPU
|}
==L'arbitrage de l'accès à la carte graphique==
Il n'est pas rare que plusieurs applications souhaitent accéder en même temps à la carte graphique. Imaginons que vous regardez une vidéo en ''streaming'' sur votre navigateur web, avec un programme de ''cloud computing'' de type ''Folding@Home'' qui tourne en arrière-plan, sur Windows. Le décodage de la vidéo est réalisé par la carte graphique, Windows s'occupe de l'affichage du bureau et des fenêtres, le navigateur web doit afficher tout ce qui est autour de la vidéo (la page web), le programme de ''cloud computing'' va lancer ses calculs sur la carte graphique, etc. Des situations de ce genre sont assez courantes et c'est soit le pilote qui s'en charge, soit la carte graphique elle-même.
===L'arbitrage logiciel du GPU===
L'arbitrage logiciel est la méthode la plus simple. Concrètement, c'est le système d'exploitation et/ou le pilote de la carte graphique qui gèrent l'arbitrage du GPU. Dans les deux cas, tout est fait en logiciel. Les commandes sont accumulées dans un tampon de commande, voire plusieurs, et l'OS/pilote décide quelle commande envoyer en priorité.
Sur Windows, avant l'arrivée du modèle de driver dit ''Windows Display Driver Model'', de telles situations étaient gérées simplement. Les applications ajoutaient des commandes dans une file de commande unique. Les commandes étaient exécutées dans l'ordre, en mode premier entrée dans la file premier sorti/exécuté. Il n'y avait pour ainsi dire pas d'arbitrage entre les applications. Et ce n'était pas un problème car, à l'époque, les seules applications qui utilisaient le GPU étaient des jeux vidéos fonctionnant en mode plein écran.
Avec le modèle de driver ''Windows Display Driver Model'' (WDDM), le GPU est maintenant arbitré, à savoir que les applications ont accès à tour de rôle au GPU, certaines ayant droit à plus de temps GPU que d'autres. Chaque programme a accès à la carte graphique durant quelques dizaines ou centaines de millisecondes, à tour de rôle. Si le programme finissait son travail en moins de temps que la durée impartie, il laissait la main au programme suivant. S’il atteignait la durée maximale allouée, il était interrompu pour laisser la place au programme suivant. Et chaque programme avait droit d'accès à la carte graphique chacun à son tour. Un tel algorithme en tourniquet est très simple, mais avait cependant quelques défauts. De nos jours, les algorithmes d'ordonnancement d'accès sont plus élaborés, bien qu'il soit difficile de trouver de la littérature ou des brevets sur le sujet.
Une autre fonctionnalité de WDDM est que les applications utilisant le GPU peuvent se partager la mémoire vidéo sans se marcher dessus. Avant, toutes les applications avaient accès à toute la mémoire vidéo, bien que ce soit par l’intermédiaire de commandes spécifiques. Mais avec WDDM, chaque application dispose de sa propre portion de mémoire vidéo, à laquelle est la seule à avoir accès. Une application ne peut plus lire le contenu réel de la mémoire vidéo, et encore moins lire le contenu des autres applications.
: Il s'agit d'un équivalent à l'isolation des processus pour les programmes Windows, mais appliqué à la mémoire vidéo et non la mémoire système. Et cette isolation est rendue d'autant plus simple que les GPU modernes implémentent un système de mémoire virtuelle à pagination très poussé, avec du swap en mémoire RAM système, une protection mémoire, et bien d'autres fonctionnalités. Nous en reparlerons dans les chapitres sur la mémoire vidéo.
===L'arbitrage accéléré par le GPU===
Depuis 2020, avec l'arrivée de la ''Windows 10 May 2020 update'', Windows est capable de déléguer l'arbitrage du GPU au GPU lui-même. Le GPU gère lui-même l'arbitrage, du moins en partie. En partie, car l'OS gère les priorité, à savoir quelle application a droit à plus de temps que les autres. Par contre, la gestion du ''quantum'' de temps ou les commutations de contexte (on stoppe une application pour laisser la place à une autre), est du fait du GPU lui-même.
L'avantage est que cela décharge le processeur d'une tâche assez lourde en calcul, pour la déporter sur le GPU. Un autre avantage est que le GPU peut gérer plus finement les ''quantum'' de temps et la commutation de contexte. Le CPU principal gère des durées assez importantes, de l'ordre de la milliseconde. De plus, il doit communiquer avec le GPU par des mécanismes temporellement imprécis, comme des interruptions. Avec l'arbitrage accéléré par le GPU, le GPU n'a plus besoin de communiquer avec le CPU pour l'arbitrage et peut le faire plus précisément, plus immédiatement. Un gain en performance théorique est donc possible.
L'arbitrage accéléré par le GPU est le fait soit du processeur de commandes, soit d'un processeur spécialisé, séparé.
===L'exemple du NV1===
La carte NVIDIA NV1 avait diverses optimisations pour supporter plusieurs applications à la fois. L'une d'entre elle est le support de plusieurs tampons de commande. La carte graphique gérait en tout 128 tampons de commandes, chacun contenant 32 commandes consécutives. Le tout permettait à 128 logiciels différents d'avoir chacun son propre tampon de commande. L'implémentation du NV1 utilisait en réalité une FIFO unique, dans une mémoire RAM unique, qui était segmentée en 128 sous-FIFOs. Mais il est techniquement possible d'utiliser plusieurs FIFOs séparées connectées à un multiplexeur en sortie et un démultiplexeur en entrée.
Le NV1 utilisait des adresses de 23 bits, ce qui fait 8 méga-octets de RAM. Les 8 méga-octets étaient découpées en 128 blocs de mémoire consécutifs, chacun étant associé à une application et faisant 64 kilo-octets. Les adresses de 23 bits étaient donc découpées en une portion de 7 bit pour identifier le logiciel qui envoie la commande, et une portion de 16 bits pour identifier la position des données dans le bloc de RAM. Une entrée dans la FIFO du NV1 faisait 48 bits, contenant une donnée de 32 bits et les 16 bits de l'adresse.
{{NavChapitre | book=Les cartes graphiques
| prev=La mémoire unifiée et la mémoire vidéo dédiée
| prevText=La mémoire unifiée et la mémoire vidéo dédiée
| next=Le pipeline géométrique d'avant DirectX 10
| netxText=Le pipeline géométrique d'avant DirectX 10
}}{{autocat}}
ngaxe7fdey5crfc96v9q7ajdlwq15fa
Les cartes graphiques/Sommaire
0
70681
745287
745229
2025-06-24T13:50:18Z
Mewtow
31375
/* La mémoire d'une carte graphique */
745287
wikitext
text/x-wiki
* [[Les cartes graphiques/Les cartes d'affichage|Introduction : les cartes d’affichage]]
===Les cartes d'affichage et d'accélération 2D===
* [[Les cartes graphiques/Le Video Display Controler|Le Video Display Controler]]
* [[Les cartes graphiques/Les systèmes à framebuffer|Les systèmes à framebuffer]]
* [[Les cartes graphiques/Les cartes accélératrices 2D|Les cartes accélératrices 2D]]
* [[Les cartes graphiques/Le mode texte et le rendu en tiles|Le mode texte et le rendu en tiles]]
* [[Les cartes graphiques/Les accélérateurs de scanline|Les accélérateurs de scanline]]
* [[Les cartes graphiques/Les Video Display Controler atypiques|Les Video Display Controler atypiques]]
===Les cartes accélératrices 3D===
* [[Les cartes graphiques/Le rendu d'une scène 3D : shaders et T&L|Le rendu d'une scène 3D : shaders et T&L]]
* [[Les cartes graphiques/Les cartes graphiques : architecture de base|Les cartes graphiques : architecture de base]]
* [[Les cartes graphiques/Les cartes accélératrices 3D|Les cartes accélératrices 3D]]
===Les processeurs de ''shader''===
* [[Les cartes graphiques/Les processeurs de shaders|Les processeurs de shaders]]
* [[Les cartes graphiques/La répartition du travail sur les unités de shaders|La répartition du travail sur les unités de shaders]]
* [[Les cartes graphiques/La microarchitecture des processeurs de shaders|La microarchitecture des processeurs de shaders]]
===La mémoire d'une carte graphique===
* [[Les cartes graphiques/La hiérarchie mémoire d'un GPU|La hiérarchie mémoire d'un GPU]]
* [[Les cartes graphiques/La mémoire unifiée et la mémoire vidéo dédiée|La mémoire unifiée et la mémoire vidéo dédiée]]
===Le pipeline fixe, non-programmable===
* [[Les cartes graphiques/Le processeur de commandes|Le processeur de commandes]]
* [[Les cartes graphiques/Le pipeline géométrique d'avant DirectX 10|Le pipeline géométrique d'avant DirectX 10]]
* [[Les cartes graphiques/Le pipeline géométrique après DirectX 10|Le pipeline géométrique après DirectX 10]]
* [[Les cartes graphiques/Le rasterizeur|Le rasterizeur]]
* [[Les cartes graphiques/Les unités de texture|Les unités de texture]]
* [[Les cartes graphiques/Les Render Output Target|Les Render Output Target]]
===Annexe===
* [[Les cartes graphiques/Le support matériel du lancer de rayons|Le support matériel du lancer de rayons]]
* [[Les cartes graphiques/L'antialiasing|L'antialiasing]]
* [[Les cartes graphiques/Le multi-GPU|Le multi-GPU]]
==En savoir plus==
* [https://fgiesen.wordpress.com/2011/07/09/a-trip-through-the-graphics-pipeline-2011-index/ A trip through the Graphics Pipeline 2011: Index]
{{autocat}}
gyaori5qzu08z2s09tclf6qbu8qogl4
Les cartes graphiques/La hiérarchie mémoire d'un GPU
0
74269
745282
745244
2025-06-24T13:31:09Z
Mewtow
31375
/* La cohérence des caches sur un GPU */
745282
wikitext
text/x-wiki
Dans ce chapitre, nous allons voir comment est organisée la mémoire d'un GPU, ou plutôt devrait-on dire les mémoires d'un GPU. Eh oui : un GPU contient beaucoup de mémoires différentes. La hiérarchie mémoire des GPUs est assez particulière, que ce soit au niveau des caches ou de la mémoire, parfois des registres. Un GPU contient évidemment une mémoire vidéo, de grande taille, capable de stocker textures, vertices, images et bien d'autres choses nécessaires pour un rendu 3D. On y trouve souvent des mémoires caches dédiées aux textures ou aux vertices.
Les GPUs récents contiennent aussi des caches tout court qui ne sont spécialisés dans les textures ou vertices. De plus, les caches sont complétés par des ''Local Store'', des mémoires normales de petite taille. Elles sont gérés par le logiciel, le programmeur, alors que les caches sont gérés par des circuits de contrôle qui s'occupent du chargement ou de l'éviction des données du cache. Elles servent donc de cache géré par le logiciel, si on peut dire. Un GPU moderne dispose de plusieurs ''local store'' : au moins par cœur.
==La mémoire vidéo==
La mémoire vidéo d'une carte graphique dédiée est nécessaire pour stocker l'image à afficher à l'écran, mais aussi pour mémoriser temporairement des informations importantes. Dans le cas le plus simple, elle sert simplement de ''Framebuffer'' : elle stocke l'image à afficher à l'écran. Au fil du temps, elle s'est vu ajouter d'autres fonctions, comme stocker les textures et les sommets de l'image à calculer, ainsi que divers résultats temporaires.
===Les spécificités des RAM des cartes vidéo dédiées===
Sur les cartes graphiques dédiées, la mémoire vidéo est très proche des mémoires RAM qu'on trouve sous forme de barrettes dans nos PC, à quelques différences près. Le point le plus important est que la mémoire vidéo d'une carte dédiée n'est pas présente sous la forme de barrettes de mémoire. À la place, les puces de mémoire sont soudées sur la puce. La conséquence est que l'on ne peut pas upgrader la RAM d'une carte vidéo. Ce serait sympathique, mais ne serait pas d'une grande utilité, car les jeux vidéos gourmands en mémoire vidéo sont aussi gourmands en puissance de calcul. Upgrader la RAM d'une carte graphique ne sert à rien si celle-ci n'a pas assez de puissance pour jouer à des jeux récents avec un framerate convenable.
Le fait que la mémoire est soudée simplifie la conception de la carte graphique, mais cela a des avantages au niveau électrique, qui permettent d'améliorer les performances. Niveau performances, la mémoire vidéo a des performances radicalement différentes de la RAM des PC. Elle a un temps d'accès très long, de plusieurs centaines de cycles d'horloge. Cela a des conséquences sur l'architecture de la carte graphique, notamment au niveau des processeurs de ''shaders'', qui sont conçus pour gérer ces temps d'accès long, comme on l'a vu dans le précédent chapitre. Par contre, elle a un très grand débit, autrement dit une bande passante élevée, proche de la centaine de gigaoctets par secondes sur les cartes graphiques modernes. Pour rappel, la bande passante d'une mémoire dépend de deux paramètres : sa fréquence et la largueur de son bus mémoire. Détaillons le dernier, qui explique en grande partie pourquoi la mémoire vidéo a un débit supérieur à la mémoire système.
===Le bus mémoire et sa largeur===
Le bus mémoire est ce qui connecte la mémoire au reste de la carte graphique. La largueur de ce bus n'est autre que la quantité de données que celui-ci peut transmettre à chaque cycle d'horloge, le nombre de bits que l'on peut lire/écrire en un cycle d'horloge. Sur la RAM système, le bus est de 64 bits sur les mémoires DDR modernes, mais peut monter à 128 bits en utilisant des techniques comme le ''dual channel'', voire en 192/256 bits avec des techniques de ''triple/quad channel'' qui sont rarement utilisées. Globalement, la configuration classique sur un PC moderne est 128 bits, avec quelques machines bas de gamme en 64 bits. Sur les cartes graphiques modernes, les bus de 128 bits ou moins sont utilisés sur les cartes graphiques de faible performance, le reste ayant un bus mémoire de 192, 256, 384, voire 512 bits. En clair, elles permettent de lire/écrire plus de données par cycle d'horloge qu'une RAM système, de 2 à 8 fois plus.
Le fait que le bus est plus large est lié au fait que les puces mémoires sont soudées. La mémoire vidéo des cartes dédiées est composée de pleins de puces mémoires accessibles en parallèle, ce qui permet de charger des blocs de plusieurs centaines d'octets en une seule fois. Les barrettes de mémoire ont des limites au nombres de broches que leur connecteur peut accepter, qui est proche de 300 pour les DDR actuelles (beaucoup de ces broches ne transfèrent pas des données, ce qui fait qu'on a bien 64 broches dédiées aux données seulement). Sans connecteurs, on est limité à ce que la puce du GPU peut accepter, et on est alors entre 4000 à 6000 broches sur les sockets de CPU ou de GPU actuels.
[[File:Puces mémories d'un GPU et d'une barrette de mémoire.png|centre|vignette|upright=2|Puces mémoires d'un GPU et d'une barrette de mémoire.]]
Pour résumer, sur les cartes graphiques dédiées, la RAM vidéo a un débit proche de la centaine de gigaoctets par secondes. Avec une mémoire RAM unifiée, vous pouvez facilement diviser cette estimation par 10.
===La mémoire vidéo est très lente===
La mémoire vidéo a donc un débit très élevé. Mais par contre, elle a un temps d'accès très lent. Concrètement, cela veut dire qu'un accès mémoire va prendre beaucoup de temps. Par exemple, si je veux lire une texture, entre le moment où j'envoie une demande de lecture à la mémoire vidéo, et le moment celle-ci me renvoie les premiers texels, il va se passer entre 200 à 1000 cycles d'horloge processeur. Par contre, une fois les premiers texels reçus, les texels suivants sont disponibles au cycle suivant, et ainsi de suite. En clair, les données lues mettent du temps avant d'arriver, mais elles arrivent par gros paquets une fois ce temps d'attente passé.
La différence entre débit et temps d'accès est primordiale sur les GPU modernes comme anciens. Toute l'architecture de la carte graphique est conçue de manière à prendre en compte ce temps d'attente. Les techniques employées sont multiples, et ne sont pas inconnues à ceux qui ont déjà lu un cours d'architecture des ordinateurs : mémoire caches, hiérarchie de caches, ''multithreading'' matériel au niveau du processeur, optimisations des accès mémoire comme des ''Load-Store Queues'' larges, des ''coalesing write buffers'', etc. Mais toutes ces techniques sont techniquement incorporées dans les processeurs de ''shaders'' et dans les circuits fixes. Aussi nous ne pouvons pas en parler dans ce chapitre. A une exception près : l'usage de caches et de ''local stores''.
==Les caches d'un GPU==
Les cartes graphiques sont censées avoir peu de caches. Les anciennes cartes graphiques se débrouillaient avec des caches spécialisés pour les textures ou pour les sommets, ce qui leur vaut les noms de caches de texture et de cache de sommets. Ce n'est que par la suite, quand les GPU commencèrent à être utilisés pour du calcul généraliste (scientifique, notamment), que la situation changea. Les GPU utilisèrent alors de plus en plus de caches généralistes, capables de stocker n'importe quelle forme de données.
Les caches en question sont intégrés dans les processeurs de ''shaders'' sur les GPU modernes. Même les caches de texture ou de sommets. Les deux sont d'ailleurs fusionnés sur les GPU modernes, vu que leur jeu d'instruction est unifié et qu'ils peuvent exécuter aussi bien des ''vertex shaders'' que des ''pixel shaders''. Sur les GPU plus anciens, avec des circuits fixes, ces caches étaient intégrés aux circuits non-programmables de gestion des textures et de la géométrie. Les caches de sommet et de texture étaient alors séparés.
===Le cache de textures===
Le '''cache de textures''', comme son nom l'indique, est un cache spécialisé dans les textures. Toutes les cartes graphiques modernes disposent de plusieurs unités de texture, qui disposent chacune de son ou ses propres caches de textures. Pas de cache partagé, ce serait peu utile et trop compliqué à implémenter.
De plus, les cartes graphiques modernes ont plusieurs caches de texture par unité de texture. Généralement, elles ont deux caches de textures : un petit cache rapide, et un gros cache lent. Les deux caches sont fortement différents. L'un est un gros cache, qui fait dans les 4 kibioctets, et l'autre est un petit cache, faisant souvent moins d'1 kibioctet. Mais le premier est plus lent que le second. Sur d'autres cartes graphiques récentes, on trouve plus de 2 caches de textures, organisés en une hiérarchie de caches de textures similaire à la hiérarchie de cache L1, L2, L3 des processeurs modernes.
Notons que ce cache interagit avec les techniques de compression de texture. Les textures sont en effet des images, qui sont donc compressées. Et elles restent compressées en mémoire vidéo, car les textures décompressées prennent beaucoup plus de place, entre 5 à 8 fois plus. Les textures sont décompressées lors des lectures : le processeur de shaders charge quelques octets, les décompresse, et utilise les données décompressées ensuite. Le cache s'introduit quelque part avant ou après la décompression.
On peut décompresser les textures avant de les placer dans le cache, ou laisser les textures compressées dans le cache. Tout est une question de compromis. Décompresser les textures dans le cache fait que la lecture dans le cache est plus rapide, car elle n'implique pas de décompression, mais le cache contient moins de données. A l'inverse, compresser les textures permet de charger plus de données dans le cache, mais rend les lectures légèrement plus lentes. C'est souvent la seconde solution qui est utilisée et ce pour deux raisons. Premièrement, la compression de texture est terriblement efficace, souvent capable de diviser par 6 la taille d'une texture, ce qui augmente drastiquement la taille effective du cache. Deuxièmement, les circuits de décompression sont généralement très rapides, très simples, et n'ajoutent que 1 à 3 cycles d'horloge lors d'une lecture.
Les anciens jeux vidéo ne faisaient que lire les textures, sans les modifier. Aussi, le cache de texture des cartes graphiques anciennes est seulement accessible en lecture, pas en écriture. Cela simplifiait fortement les circuits du cache, réduisant le nombre de transistors utilisés par le cache, réduisant sa consommation énergétique, augmentait sa rapidité, etc. Mais les jeux vidéos 3D récents utilisent des techniques dites de ''render-to-texture'', qui permettent de calculer certaines données et à les écrire en mémoire vidéo pour une utilisation ultérieure. Les textures peuvent donc être modifiées et cela se marie mal avec un cache en lecture seule.
Rendre le cache de texture accessible en écriture est une solution, mais qui demande d'ajouter beaucoup de circuits pour une utilisation somme toute peu fréquente. Une autre solution, plus adaptée, réinitialise le cache de textures quand on modifie une texture, que ce soit totalement ou partiellement. Une fois le cache vidé, les accès mémoire ultérieurs n'ont pas d'autre choix que d'aller lire la texture en mémoire et de remplir le cache avec les données chargées depuis la RAM. Les données de texture en RAM étant les bonnes, cela garantit l’absence d'erreur.
: Ces deux techniques peuvent être adaptées dans le cas où plusieurs caches de textures séparées existent sur une même carte graphique. Les écritures doivent invalider toutes les copies dans tous les caches de texture. Cela nécessite d'ajouter des circuits qui propagent l'invalidation dans tous les autres caches.
===Les caches généralistes===
La hiérarchie mémoire des GPU modernes ressemble de plus en plus à celle des CPU, avec toute une hiérarchie de caches, avec des caches L1, L2, L3, etc. Pour rappel, les processeurs multicœurs modernes ont pleins de mémoires cache, avec au minimum deux niveaux de cache, le plus souvent trois. Les trois niveaux de cache sont appelés les caches L1, L2 et L3. Pour le premier niveau, on trouve deux caches spécialisés par cœur/processeur : un cache pour les instructions et un cache pour les données. Pour le second niveau, on a un cache L2 par cœur/processeur, qui peut stocker indifféremment données et instructions. Le cache L3 est un cache partagé entre tous les cœurs/processeurs. Les GPU ont une organisation similaire, sauf que le nombre de cœurs est beaucoup plus grand que sur un processeur moderne.
[[File:Partage des caches sur un processeur multicoeurs.png|centre|vignette|upright=2|Partage des caches sur un processeur multicoeurs]]
Les caches d'instruction des GPU sont adaptés aux contraintes du rendu 3D. Le principe du rendu 3D est d'appliquer un shader assez simple sur un grand nombre de données, alors que les programmes généralistes effectuent un grand nombre d'instructions sur une quantité limitée de données. Les shaders sont donc des programmes assez légers, qui ont peu d'instructions. Les caches d'instructions L1 sont généralement assez petits, généralement quelques dizaines ou centaines de kilooctets. Et malgré cela, il n'est pas rare qu'un ''shader'' tienne tout entier dans le cache d'instruction, situation serait impensable sur un processeur généraliste. La seconde caractéristique est qu'un même programme s’exécute sur beaucoup de données. Il n'est pas rare que plusieurs processeurs de shaders exécutent le même ''shader''. Aussi, certains GPU partagent un même cache d’instruction entre plusieurs processeurs de ''shader'', comme c'est le cas sur les GPU AMD d'architecture GCN où un cache d'instruction de 32 kB est partagé entre 4 cœurs.
Pour les caches de données, il faut savoir qu'un shader a peu de chances de réutiliser une donnée qu'il a chargé précédemment. Les processeurs de shaders ont beaucoup de registres, ce qui fait que si accès ultérieur à une donnée il doit y avoir, elle passe généralement par les registres. Cette faible réutilisation fait que les caches de données ne sont pas censé être très utiles. Mais le fait est qu'un shader s’exécute en un grand nombre d'instances, chacune traitant un paquet de données différent. Il est très fréquent que différentes instances s’exécutent chacune sur un même cœur et ces différentes instances tendent à accéder à des données très proches, voire à des données identiques. Si un shader charge une donnée dans le cache, la donnée et ses voisines sont alors disponibles pour les autres instances. Le cas le plus classique est celui de l'accès aux textures : lors du placage de textures, des pixels contiguës accèderont à des texels contiguës. Et outre les textures, les pixels shaders ont tendance à traiter des pixels proches, donc à avoir besoin de données proches en mémoire. Ce qui fait que les caches de données sont quand même utiles.
Dans le même registre, un shader a besoin de certaines informations spécifiques, généralement constantes, pour faire son travail. Toutes les instances du shader manipulent ces données, elles ont besoin de les lire, pour les utiliser lors de l’exécution, les copier dans des registres, etc. Les GPU incorporent des '''caches de constantes''' pour accélérer l'accès à ces données. Ainsi, quand un shader lit une donnée, elle est chargée dans le cache de constante, ce qui fait que les futures instances auront accès à celle-ci dans le cache. Ces caches de constante sont séparés des autres caches de données pour une raison bien précise : les constantes en question sont d'accès peu fréquent. Généralement, on a un accès au début de chaque instance de shader, guère plus. Vu que ce sont des données peu fréquemment utilisées, elles sont censée être évincées en priorité du cache de données, qui privilégie les données fréquemment lues/écrites. Avec un cache séparé, on n'a pas ce problème. Au passage, ce cache de constante a des chances d'être partagé entre plusieurs cœurs, des cœurs différents ayant de fortes chances d’exécuter des instances différentes d'un même shader.
Il faut noter que sur la plupart des cartes graphiques modernes, les caches de données et le cache de texture sont un seul et même cache. Même chose pour le cache de sommets, utilisé par les unités géométrique, qui est fusionné avec les caches de données. La raison est que une économie de circuits qui ne coute pas grand chose en termes de performance. Rappelons que les processeurs de shaders sont unifiés à l'heure actuelle, c'est à dire qu'elles peuvent exécuter pixel shader et vertex shader. En théorie, chaque unité de shader devrait incorporer un cache de sommets et un cache de textures. Mais le processeur de shader exécute soit un pixel shader, soit un vertex shader, mais pas les deux en même temps. Donc, autant utiliser un seul cache, qui sert alternativement de cache de vertex et de cache de texture, afin d'économiser des circuits. Une fois les deux fusionnés, on obtient un cache de donnée généraliste, capable de traiter sommets et pixels, voire d'autres données. La seule difficulté tient au filtrage de textures et à sa décompression, mais cela demande juste de router les données lues vers l'unité de texture ou directement vers les registres/unités de calcul, ce qui est facile à faire.
==La mémoire partagée : un ''local store''==
En plus d'utiliser des caches, les GPU modernes utilisent des ''local stores'', aussi appelés ''scratchpad memories''. Ce sont des mémoires RAM intermédiaires entre la RAM principale et les registres. Ces local stores peuvent être vus comme des caches, mais que le programmeur doit gérer manuellement. Dans la réalité, ce sont des mémoires RAM très rapides mais de petite taille, qui sont adressées comme n'importe quelle mémoire RAM, en utilisant des adresses directement.
[[File:Scratch-Pad-Memory.jpg|centre|vignette|upright=2.0|Scratch-Pad-Memory (SPM).]]
Sur les GPU modernes, chaque processeur de ''shader'' possède un unique ''local store'', appelée la '''mémoire partagée'''. Il n'y a pas de hiérarchie des ''local store'', similaire à la hiérarchie des caches.
[[File:Cuda5.png|centre|vignette|upright=2.0|Local stores d'un GPU.]]
La faible capacité de ces mémoires, tout du moins comparé à la grande taille de la mémoire vidéo, les rend utile pour stocker temporairement des résultats de calcul "peu imposants". L'utilité principale est donc de réduire le trafic avec la mémoire centrale, les écritures de résultats temporaires étant redirigés vers les local stores. Ils sont surtout utilisés hors du rendu 3D, pour les applications de type GPGPU, où le GPU est utilisé comme architecture multicœurs pour du calcul scientifique.
===L'implémentation des ''local store''===
Vous vous attendez certainement à ce que je dise que les ''local store'' sont des mémoires séparées des mémoires caches et qu'il y a réellement des puces de mémoire RAM distinctes dans les processeurs de ''shaders''. Mais en réalité, ce n'est pas le cas pour tous les ''local store''. Le dernier niveau de ''local store'', la mémoire partagée, est bel et bien une mémoire SRAM à part des autres, avec ses propres circuits. Mais les cartes graphiques très récentes fusionnent la mémoire locale avec le cache L1.
L'avantage est une économie de transistors assez importante. De plus, cette technologie permet de partitionner le cache/''local store'' suivant les besoins. Par exemple, si la moitié du ''local store'' est utilisé, l'autre moitié peut servir de cache L1. Si le ''local store'' n'est pas utilisé, comme c'est le cas pour la majorité des rendu 3D, le cache/''local store'' est utilisé intégralement comme cache L1.
Et si vous vous demandez comment c'est possible de fusionner un cache et une mémoire RAM, voici comment le tout est implémenté. L'implémentation consiste à couper le cache en deux circuits, dont l'un est un ''local store'', et l'autre transforme le ''local store'' en cache. Ce genre de cache séparé en deux mémoires est appelé un ''phased cache'', pour ceux qui veulent en savoir plus, et ce genre de cache est parfois utilisés sur les processeurs modernes, dans des processeurs dédiés à l'embarqué ou pour certaines applications spécifiques.
Le premier circuit vérifie la présence des données à lire/écrire dans le cache. Lors d'un accès mémoire, il reçoit l'adresse mémoire à lire, et détermine si une copie de la donnée associée est dans le cache ou non. Pour cela, il utilise un système de tags qu'on ne détaillera pas ici, mais qui donne son nom à l'unité de vérification : l''''unité de tag'''. Son implémentation est très variable suivant le cache considéré, mais une simple mémoire RAM suffit généralement.
En plus de l'unité de tags, il y a une mémoire qui stocke les données, la mémoire cache proprement dite. Par simplicité, cette mémoire est une simple mémoire RAM adressable avec des adresses mémoires des plus normales, chaque ligne de cache correspondant à une adresse. La mémoire RAM de données en question n'est autre que le ''local store''. En clair, le cache s'obtient en combinant un ''local store'' avec un circuit qui s'occupe de vérifier de vérifier les succès ou défaut de cache, et qui éventuellement identifie la position de la donnée dans le cache.
[[File:Phased cache.png|centre|vignette|upright=1.5|Phased cache]]
Pour que le tout puisse servir alternativement de ''local store'' ou de cache, on doit contourner ou non l'unité de tags. Lors d'un accès au cache, on envoie l'adresse à lire/écrire à l'unité de tags. Lors d'un accès au ''local store'', on envoie l'adresse directement sur la mémoire RAM de données, sans intervention de l'unité de tags. Le contournement est d'autant plus simple que les adresses pour le ''local store'' sont distinctes des adresses de la mémoire vidéo, les espaces d'adressage ne sont pas les mêmes, les instructions utilisées pour lire/écrire dans ces deux mémoires sont aussi potentiellement différentes.
[[File:Hydride cache - local store.png|centre|vignette|upright=2.0|Hydride cache - local store]]
Il faut préciser que cette organisation en ''phased cache'' est assez naturelle. Les caches de texture utilisent cette organisation pour diverses raisons. Vu que cache L1 et cache de texture sont le même cache, il est naturel que les caches L1 et autres aient suivi le mouvement en conservant la même organisation. La transformation du cache L1 en hydride cache/''local store'' était donc assez simple à implémenter et s'est donc faite facilement.
{{NavChapitre | book=Les cartes graphiques
| prev=La mémoire unifiée et la mémoire vidéo dédiée
| prevText=La mémoire unifiée et la mémoire vidéo dédiée
| next=Le processeur de commandes
| netxText=Le processeur de commandes
}}{{autocat}}
69l68f4763qgxci7a53rtjphhep4h1h
745289
745282
2025-06-24T13:51:03Z
Mewtow
31375
/* L'implémentation des local store */
745289
wikitext
text/x-wiki
Dans ce chapitre, nous allons voir comment est organisée la mémoire d'un GPU, ou plutôt devrait-on dire les mémoires d'un GPU. Eh oui : un GPU contient beaucoup de mémoires différentes. La hiérarchie mémoire des GPUs est assez particulière, que ce soit au niveau des caches ou de la mémoire, parfois des registres. Un GPU contient évidemment une mémoire vidéo, de grande taille, capable de stocker textures, vertices, images et bien d'autres choses nécessaires pour un rendu 3D. On y trouve souvent des mémoires caches dédiées aux textures ou aux vertices.
Les GPUs récents contiennent aussi des caches tout court qui ne sont spécialisés dans les textures ou vertices. De plus, les caches sont complétés par des ''Local Store'', des mémoires normales de petite taille. Elles sont gérés par le logiciel, le programmeur, alors que les caches sont gérés par des circuits de contrôle qui s'occupent du chargement ou de l'éviction des données du cache. Elles servent donc de cache géré par le logiciel, si on peut dire. Un GPU moderne dispose de plusieurs ''local store'' : au moins par cœur.
==La mémoire vidéo==
La mémoire vidéo d'une carte graphique dédiée est nécessaire pour stocker l'image à afficher à l'écran, mais aussi pour mémoriser temporairement des informations importantes. Dans le cas le plus simple, elle sert simplement de ''Framebuffer'' : elle stocke l'image à afficher à l'écran. Au fil du temps, elle s'est vu ajouter d'autres fonctions, comme stocker les textures et les sommets de l'image à calculer, ainsi que divers résultats temporaires.
===Les spécificités des RAM des cartes vidéo dédiées===
Sur les cartes graphiques dédiées, la mémoire vidéo est très proche des mémoires RAM qu'on trouve sous forme de barrettes dans nos PC, à quelques différences près. Le point le plus important est que la mémoire vidéo d'une carte dédiée n'est pas présente sous la forme de barrettes de mémoire. À la place, les puces de mémoire sont soudées sur la puce. La conséquence est que l'on ne peut pas upgrader la RAM d'une carte vidéo. Ce serait sympathique, mais ne serait pas d'une grande utilité, car les jeux vidéos gourmands en mémoire vidéo sont aussi gourmands en puissance de calcul. Upgrader la RAM d'une carte graphique ne sert à rien si celle-ci n'a pas assez de puissance pour jouer à des jeux récents avec un framerate convenable.
Le fait que la mémoire est soudée simplifie la conception de la carte graphique, mais cela a des avantages au niveau électrique, qui permettent d'améliorer les performances. Niveau performances, la mémoire vidéo a des performances radicalement différentes de la RAM des PC. Elle a un temps d'accès très long, de plusieurs centaines de cycles d'horloge. Cela a des conséquences sur l'architecture de la carte graphique, notamment au niveau des processeurs de ''shaders'', qui sont conçus pour gérer ces temps d'accès long, comme on l'a vu dans le précédent chapitre. Par contre, elle a un très grand débit, autrement dit une bande passante élevée, proche de la centaine de gigaoctets par secondes sur les cartes graphiques modernes. Pour rappel, la bande passante d'une mémoire dépend de deux paramètres : sa fréquence et la largueur de son bus mémoire. Détaillons le dernier, qui explique en grande partie pourquoi la mémoire vidéo a un débit supérieur à la mémoire système.
===Le bus mémoire et sa largeur===
Le bus mémoire est ce qui connecte la mémoire au reste de la carte graphique. La largueur de ce bus n'est autre que la quantité de données que celui-ci peut transmettre à chaque cycle d'horloge, le nombre de bits que l'on peut lire/écrire en un cycle d'horloge. Sur la RAM système, le bus est de 64 bits sur les mémoires DDR modernes, mais peut monter à 128 bits en utilisant des techniques comme le ''dual channel'', voire en 192/256 bits avec des techniques de ''triple/quad channel'' qui sont rarement utilisées. Globalement, la configuration classique sur un PC moderne est 128 bits, avec quelques machines bas de gamme en 64 bits. Sur les cartes graphiques modernes, les bus de 128 bits ou moins sont utilisés sur les cartes graphiques de faible performance, le reste ayant un bus mémoire de 192, 256, 384, voire 512 bits. En clair, elles permettent de lire/écrire plus de données par cycle d'horloge qu'une RAM système, de 2 à 8 fois plus.
Le fait que le bus est plus large est lié au fait que les puces mémoires sont soudées. La mémoire vidéo des cartes dédiées est composée de pleins de puces mémoires accessibles en parallèle, ce qui permet de charger des blocs de plusieurs centaines d'octets en une seule fois. Les barrettes de mémoire ont des limites au nombres de broches que leur connecteur peut accepter, qui est proche de 300 pour les DDR actuelles (beaucoup de ces broches ne transfèrent pas des données, ce qui fait qu'on a bien 64 broches dédiées aux données seulement). Sans connecteurs, on est limité à ce que la puce du GPU peut accepter, et on est alors entre 4000 à 6000 broches sur les sockets de CPU ou de GPU actuels.
[[File:Puces mémories d'un GPU et d'une barrette de mémoire.png|centre|vignette|upright=2|Puces mémoires d'un GPU et d'une barrette de mémoire.]]
Pour résumer, sur les cartes graphiques dédiées, la RAM vidéo a un débit proche de la centaine de gigaoctets par secondes. Avec une mémoire RAM unifiée, vous pouvez facilement diviser cette estimation par 10.
===La mémoire vidéo est très lente===
La mémoire vidéo a donc un débit très élevé. Mais par contre, elle a un temps d'accès très lent. Concrètement, cela veut dire qu'un accès mémoire va prendre beaucoup de temps. Par exemple, si je veux lire une texture, entre le moment où j'envoie une demande de lecture à la mémoire vidéo, et le moment celle-ci me renvoie les premiers texels, il va se passer entre 200 à 1000 cycles d'horloge processeur. Par contre, une fois les premiers texels reçus, les texels suivants sont disponibles au cycle suivant, et ainsi de suite. En clair, les données lues mettent du temps avant d'arriver, mais elles arrivent par gros paquets une fois ce temps d'attente passé.
La différence entre débit et temps d'accès est primordiale sur les GPU modernes comme anciens. Toute l'architecture de la carte graphique est conçue de manière à prendre en compte ce temps d'attente. Les techniques employées sont multiples, et ne sont pas inconnues à ceux qui ont déjà lu un cours d'architecture des ordinateurs : mémoire caches, hiérarchie de caches, ''multithreading'' matériel au niveau du processeur, optimisations des accès mémoire comme des ''Load-Store Queues'' larges, des ''coalesing write buffers'', etc. Mais toutes ces techniques sont techniquement incorporées dans les processeurs de ''shaders'' et dans les circuits fixes. Aussi nous ne pouvons pas en parler dans ce chapitre. A une exception près : l'usage de caches et de ''local stores''.
==Les caches d'un GPU==
Les cartes graphiques sont censées avoir peu de caches. Les anciennes cartes graphiques se débrouillaient avec des caches spécialisés pour les textures ou pour les sommets, ce qui leur vaut les noms de caches de texture et de cache de sommets. Ce n'est que par la suite, quand les GPU commencèrent à être utilisés pour du calcul généraliste (scientifique, notamment), que la situation changea. Les GPU utilisèrent alors de plus en plus de caches généralistes, capables de stocker n'importe quelle forme de données.
Les caches en question sont intégrés dans les processeurs de ''shaders'' sur les GPU modernes. Même les caches de texture ou de sommets. Les deux sont d'ailleurs fusionnés sur les GPU modernes, vu que leur jeu d'instruction est unifié et qu'ils peuvent exécuter aussi bien des ''vertex shaders'' que des ''pixel shaders''. Sur les GPU plus anciens, avec des circuits fixes, ces caches étaient intégrés aux circuits non-programmables de gestion des textures et de la géométrie. Les caches de sommet et de texture étaient alors séparés.
===Le cache de textures===
Le '''cache de textures''', comme son nom l'indique, est un cache spécialisé dans les textures. Toutes les cartes graphiques modernes disposent de plusieurs unités de texture, qui disposent chacune de son ou ses propres caches de textures. Pas de cache partagé, ce serait peu utile et trop compliqué à implémenter.
De plus, les cartes graphiques modernes ont plusieurs caches de texture par unité de texture. Généralement, elles ont deux caches de textures : un petit cache rapide, et un gros cache lent. Les deux caches sont fortement différents. L'un est un gros cache, qui fait dans les 4 kibioctets, et l'autre est un petit cache, faisant souvent moins d'1 kibioctet. Mais le premier est plus lent que le second. Sur d'autres cartes graphiques récentes, on trouve plus de 2 caches de textures, organisés en une hiérarchie de caches de textures similaire à la hiérarchie de cache L1, L2, L3 des processeurs modernes.
Notons que ce cache interagit avec les techniques de compression de texture. Les textures sont en effet des images, qui sont donc compressées. Et elles restent compressées en mémoire vidéo, car les textures décompressées prennent beaucoup plus de place, entre 5 à 8 fois plus. Les textures sont décompressées lors des lectures : le processeur de shaders charge quelques octets, les décompresse, et utilise les données décompressées ensuite. Le cache s'introduit quelque part avant ou après la décompression.
On peut décompresser les textures avant de les placer dans le cache, ou laisser les textures compressées dans le cache. Tout est une question de compromis. Décompresser les textures dans le cache fait que la lecture dans le cache est plus rapide, car elle n'implique pas de décompression, mais le cache contient moins de données. A l'inverse, compresser les textures permet de charger plus de données dans le cache, mais rend les lectures légèrement plus lentes. C'est souvent la seconde solution qui est utilisée et ce pour deux raisons. Premièrement, la compression de texture est terriblement efficace, souvent capable de diviser par 6 la taille d'une texture, ce qui augmente drastiquement la taille effective du cache. Deuxièmement, les circuits de décompression sont généralement très rapides, très simples, et n'ajoutent que 1 à 3 cycles d'horloge lors d'une lecture.
Les anciens jeux vidéo ne faisaient que lire les textures, sans les modifier. Aussi, le cache de texture des cartes graphiques anciennes est seulement accessible en lecture, pas en écriture. Cela simplifiait fortement les circuits du cache, réduisant le nombre de transistors utilisés par le cache, réduisant sa consommation énergétique, augmentait sa rapidité, etc. Mais les jeux vidéos 3D récents utilisent des techniques dites de ''render-to-texture'', qui permettent de calculer certaines données et à les écrire en mémoire vidéo pour une utilisation ultérieure. Les textures peuvent donc être modifiées et cela se marie mal avec un cache en lecture seule.
Rendre le cache de texture accessible en écriture est une solution, mais qui demande d'ajouter beaucoup de circuits pour une utilisation somme toute peu fréquente. Une autre solution, plus adaptée, réinitialise le cache de textures quand on modifie une texture, que ce soit totalement ou partiellement. Une fois le cache vidé, les accès mémoire ultérieurs n'ont pas d'autre choix que d'aller lire la texture en mémoire et de remplir le cache avec les données chargées depuis la RAM. Les données de texture en RAM étant les bonnes, cela garantit l’absence d'erreur.
: Ces deux techniques peuvent être adaptées dans le cas où plusieurs caches de textures séparées existent sur une même carte graphique. Les écritures doivent invalider toutes les copies dans tous les caches de texture. Cela nécessite d'ajouter des circuits qui propagent l'invalidation dans tous les autres caches.
===Les caches généralistes===
La hiérarchie mémoire des GPU modernes ressemble de plus en plus à celle des CPU, avec toute une hiérarchie de caches, avec des caches L1, L2, L3, etc. Pour rappel, les processeurs multicœurs modernes ont pleins de mémoires cache, avec au minimum deux niveaux de cache, le plus souvent trois. Les trois niveaux de cache sont appelés les caches L1, L2 et L3. Pour le premier niveau, on trouve deux caches spécialisés par cœur/processeur : un cache pour les instructions et un cache pour les données. Pour le second niveau, on a un cache L2 par cœur/processeur, qui peut stocker indifféremment données et instructions. Le cache L3 est un cache partagé entre tous les cœurs/processeurs. Les GPU ont une organisation similaire, sauf que le nombre de cœurs est beaucoup plus grand que sur un processeur moderne.
[[File:Partage des caches sur un processeur multicoeurs.png|centre|vignette|upright=2|Partage des caches sur un processeur multicoeurs]]
Les caches d'instruction des GPU sont adaptés aux contraintes du rendu 3D. Le principe du rendu 3D est d'appliquer un shader assez simple sur un grand nombre de données, alors que les programmes généralistes effectuent un grand nombre d'instructions sur une quantité limitée de données. Les shaders sont donc des programmes assez légers, qui ont peu d'instructions. Les caches d'instructions L1 sont généralement assez petits, généralement quelques dizaines ou centaines de kilooctets. Et malgré cela, il n'est pas rare qu'un ''shader'' tienne tout entier dans le cache d'instruction, situation serait impensable sur un processeur généraliste. La seconde caractéristique est qu'un même programme s’exécute sur beaucoup de données. Il n'est pas rare que plusieurs processeurs de shaders exécutent le même ''shader''. Aussi, certains GPU partagent un même cache d’instruction entre plusieurs processeurs de ''shader'', comme c'est le cas sur les GPU AMD d'architecture GCN où un cache d'instruction de 32 kB est partagé entre 4 cœurs.
Pour les caches de données, il faut savoir qu'un shader a peu de chances de réutiliser une donnée qu'il a chargé précédemment. Les processeurs de shaders ont beaucoup de registres, ce qui fait que si accès ultérieur à une donnée il doit y avoir, elle passe généralement par les registres. Cette faible réutilisation fait que les caches de données ne sont pas censé être très utiles. Mais le fait est qu'un shader s’exécute en un grand nombre d'instances, chacune traitant un paquet de données différent. Il est très fréquent que différentes instances s’exécutent chacune sur un même cœur et ces différentes instances tendent à accéder à des données très proches, voire à des données identiques. Si un shader charge une donnée dans le cache, la donnée et ses voisines sont alors disponibles pour les autres instances. Le cas le plus classique est celui de l'accès aux textures : lors du placage de textures, des pixels contiguës accèderont à des texels contiguës. Et outre les textures, les pixels shaders ont tendance à traiter des pixels proches, donc à avoir besoin de données proches en mémoire. Ce qui fait que les caches de données sont quand même utiles.
Dans le même registre, un shader a besoin de certaines informations spécifiques, généralement constantes, pour faire son travail. Toutes les instances du shader manipulent ces données, elles ont besoin de les lire, pour les utiliser lors de l’exécution, les copier dans des registres, etc. Les GPU incorporent des '''caches de constantes''' pour accélérer l'accès à ces données. Ainsi, quand un shader lit une donnée, elle est chargée dans le cache de constante, ce qui fait que les futures instances auront accès à celle-ci dans le cache. Ces caches de constante sont séparés des autres caches de données pour une raison bien précise : les constantes en question sont d'accès peu fréquent. Généralement, on a un accès au début de chaque instance de shader, guère plus. Vu que ce sont des données peu fréquemment utilisées, elles sont censée être évincées en priorité du cache de données, qui privilégie les données fréquemment lues/écrites. Avec un cache séparé, on n'a pas ce problème. Au passage, ce cache de constante a des chances d'être partagé entre plusieurs cœurs, des cœurs différents ayant de fortes chances d’exécuter des instances différentes d'un même shader.
Il faut noter que sur la plupart des cartes graphiques modernes, les caches de données et le cache de texture sont un seul et même cache. Même chose pour le cache de sommets, utilisé par les unités géométrique, qui est fusionné avec les caches de données. La raison est que une économie de circuits qui ne coute pas grand chose en termes de performance. Rappelons que les processeurs de shaders sont unifiés à l'heure actuelle, c'est à dire qu'elles peuvent exécuter pixel shader et vertex shader. En théorie, chaque unité de shader devrait incorporer un cache de sommets et un cache de textures. Mais le processeur de shader exécute soit un pixel shader, soit un vertex shader, mais pas les deux en même temps. Donc, autant utiliser un seul cache, qui sert alternativement de cache de vertex et de cache de texture, afin d'économiser des circuits. Une fois les deux fusionnés, on obtient un cache de donnée généraliste, capable de traiter sommets et pixels, voire d'autres données. La seule difficulté tient au filtrage de textures et à sa décompression, mais cela demande juste de router les données lues vers l'unité de texture ou directement vers les registres/unités de calcul, ce qui est facile à faire.
==La mémoire partagée : un ''local store''==
En plus d'utiliser des caches, les GPU modernes utilisent des ''local stores'', aussi appelés ''scratchpad memories''. Ce sont des mémoires RAM intermédiaires entre la RAM principale et les registres. Ces local stores peuvent être vus comme des caches, mais que le programmeur doit gérer manuellement. Dans la réalité, ce sont des mémoires RAM très rapides mais de petite taille, qui sont adressées comme n'importe quelle mémoire RAM, en utilisant des adresses directement.
[[File:Scratch-Pad-Memory.jpg|centre|vignette|upright=2.0|Scratch-Pad-Memory (SPM).]]
Sur les GPU modernes, chaque processeur de ''shader'' possède un unique ''local store'', appelée la '''mémoire partagée'''. Il n'y a pas de hiérarchie des ''local store'', similaire à la hiérarchie des caches.
[[File:Cuda5.png|centre|vignette|upright=2.0|Local stores d'un GPU.]]
La faible capacité de ces mémoires, tout du moins comparé à la grande taille de la mémoire vidéo, les rend utile pour stocker temporairement des résultats de calcul "peu imposants". L'utilité principale est donc de réduire le trafic avec la mémoire centrale, les écritures de résultats temporaires étant redirigés vers les local stores. Ils sont surtout utilisés hors du rendu 3D, pour les applications de type GPGPU, où le GPU est utilisé comme architecture multicœurs pour du calcul scientifique.
===L'implémentation des ''local store''===
Vous vous attendez certainement à ce que je dise que les ''local store'' sont des mémoires séparées des mémoires caches et qu'il y a réellement des puces de mémoire RAM distinctes dans les processeurs de ''shaders''. Mais en réalité, ce n'est pas le cas pour tous les ''local store''. Le dernier niveau de ''local store'', la mémoire partagée, est bel et bien une mémoire SRAM à part des autres, avec ses propres circuits. Mais les cartes graphiques très récentes fusionnent la mémoire locale avec le cache L1.
L'avantage est une économie de transistors assez importante. De plus, cette technologie permet de partitionner le cache/''local store'' suivant les besoins. Par exemple, si la moitié du ''local store'' est utilisé, l'autre moitié peut servir de cache L1. Si le ''local store'' n'est pas utilisé, comme c'est le cas pour la majorité des rendu 3D, le cache/''local store'' est utilisé intégralement comme cache L1.
Et si vous vous demandez comment c'est possible de fusionner un cache et une mémoire RAM, voici comment le tout est implémenté. L'implémentation consiste à couper le cache en deux circuits, dont l'un est un ''local store'', et l'autre transforme le ''local store'' en cache. Ce genre de cache séparé en deux mémoires est appelé un ''phased cache'', pour ceux qui veulent en savoir plus, et ce genre de cache est parfois utilisés sur les processeurs modernes, dans des processeurs dédiés à l'embarqué ou pour certaines applications spécifiques.
Le premier circuit vérifie la présence des données à lire/écrire dans le cache. Lors d'un accès mémoire, il reçoit l'adresse mémoire à lire, et détermine si une copie de la donnée associée est dans le cache ou non. Pour cela, il utilise un système de tags qu'on ne détaillera pas ici, mais qui donne son nom à l'unité de vérification : l''''unité de tag'''. Son implémentation est très variable suivant le cache considéré, mais une simple mémoire RAM suffit généralement.
En plus de l'unité de tags, il y a une mémoire qui stocke les données, la mémoire cache proprement dite. Par simplicité, cette mémoire est une simple mémoire RAM adressable avec des adresses mémoires des plus normales, chaque ligne de cache correspondant à une adresse. La mémoire RAM de données en question n'est autre que le ''local store''. En clair, le cache s'obtient en combinant un ''local store'' avec un circuit qui s'occupe de vérifier de vérifier les succès ou défaut de cache, et qui éventuellement identifie la position de la donnée dans le cache.
[[File:Phased cache.png|centre|vignette|upright=1.5|Phased cache]]
Pour que le tout puisse servir alternativement de ''local store'' ou de cache, on doit contourner ou non l'unité de tags. Lors d'un accès au cache, on envoie l'adresse à lire/écrire à l'unité de tags. Lors d'un accès au ''local store'', on envoie l'adresse directement sur la mémoire RAM de données, sans intervention de l'unité de tags. Le contournement est d'autant plus simple que les adresses pour le ''local store'' sont distinctes des adresses de la mémoire vidéo, les espaces d'adressage ne sont pas les mêmes, les instructions utilisées pour lire/écrire dans ces deux mémoires sont aussi potentiellement différentes.
[[File:Hydride cache - local store.png|centre|vignette|upright=2.0|Hydride cache - local store]]
Il faut préciser que cette organisation en ''phased cache'' est assez naturelle. Les caches de texture utilisent cette organisation pour diverses raisons. Vu que cache L1 et cache de texture sont le même cache, il est naturel que les caches L1 et autres aient suivi le mouvement en conservant la même organisation. La transformation du cache L1 en hydride cache/''local store'' était donc assez simple à implémenter et s'est donc faite facilement.
{{NavChapitre | book=Les cartes graphiques
| prev=La microarchitecture des processeurs de shaders
| prevText=La microarchitecture des processeurs de shaders
| next=La mémoire unifiée et la mémoire vidéo dédiée
| netxText=La mémoire unifiée et la mémoire vidéo dédiée
}}{{autocat}}
r8gfkg2j6rrlroahs2icff518v9fu4f
745292
745289
2025-06-24T13:56:29Z
Mewtow
31375
/* Les caches généralistes */
745292
wikitext
text/x-wiki
Dans ce chapitre, nous allons voir comment est organisée la mémoire d'un GPU, ou plutôt devrait-on dire les mémoires d'un GPU. Eh oui : un GPU contient beaucoup de mémoires différentes. La hiérarchie mémoire des GPUs est assez particulière, que ce soit au niveau des caches ou de la mémoire, parfois des registres. Un GPU contient évidemment une mémoire vidéo, de grande taille, capable de stocker textures, vertices, images et bien d'autres choses nécessaires pour un rendu 3D. On y trouve souvent des mémoires caches dédiées aux textures ou aux vertices.
Les GPUs récents contiennent aussi des caches tout court qui ne sont spécialisés dans les textures ou vertices. De plus, les caches sont complétés par des ''Local Store'', des mémoires normales de petite taille. Elles sont gérés par le logiciel, le programmeur, alors que les caches sont gérés par des circuits de contrôle qui s'occupent du chargement ou de l'éviction des données du cache. Elles servent donc de cache géré par le logiciel, si on peut dire. Un GPU moderne dispose de plusieurs ''local store'' : au moins par cœur.
==La mémoire vidéo==
La mémoire vidéo d'une carte graphique dédiée est nécessaire pour stocker l'image à afficher à l'écran, mais aussi pour mémoriser temporairement des informations importantes. Dans le cas le plus simple, elle sert simplement de ''Framebuffer'' : elle stocke l'image à afficher à l'écran. Au fil du temps, elle s'est vu ajouter d'autres fonctions, comme stocker les textures et les sommets de l'image à calculer, ainsi que divers résultats temporaires.
===Les spécificités des RAM des cartes vidéo dédiées===
Sur les cartes graphiques dédiées, la mémoire vidéo est très proche des mémoires RAM qu'on trouve sous forme de barrettes dans nos PC, à quelques différences près. Le point le plus important est que la mémoire vidéo d'une carte dédiée n'est pas présente sous la forme de barrettes de mémoire. À la place, les puces de mémoire sont soudées sur la puce. La conséquence est que l'on ne peut pas upgrader la RAM d'une carte vidéo. Ce serait sympathique, mais ne serait pas d'une grande utilité, car les jeux vidéos gourmands en mémoire vidéo sont aussi gourmands en puissance de calcul. Upgrader la RAM d'une carte graphique ne sert à rien si celle-ci n'a pas assez de puissance pour jouer à des jeux récents avec un framerate convenable.
Le fait que la mémoire est soudée simplifie la conception de la carte graphique, mais cela a des avantages au niveau électrique, qui permettent d'améliorer les performances. Niveau performances, la mémoire vidéo a des performances radicalement différentes de la RAM des PC. Elle a un temps d'accès très long, de plusieurs centaines de cycles d'horloge. Cela a des conséquences sur l'architecture de la carte graphique, notamment au niveau des processeurs de ''shaders'', qui sont conçus pour gérer ces temps d'accès long, comme on l'a vu dans le précédent chapitre. Par contre, elle a un très grand débit, autrement dit une bande passante élevée, proche de la centaine de gigaoctets par secondes sur les cartes graphiques modernes. Pour rappel, la bande passante d'une mémoire dépend de deux paramètres : sa fréquence et la largueur de son bus mémoire. Détaillons le dernier, qui explique en grande partie pourquoi la mémoire vidéo a un débit supérieur à la mémoire système.
===Le bus mémoire et sa largeur===
Le bus mémoire est ce qui connecte la mémoire au reste de la carte graphique. La largueur de ce bus n'est autre que la quantité de données que celui-ci peut transmettre à chaque cycle d'horloge, le nombre de bits que l'on peut lire/écrire en un cycle d'horloge. Sur la RAM système, le bus est de 64 bits sur les mémoires DDR modernes, mais peut monter à 128 bits en utilisant des techniques comme le ''dual channel'', voire en 192/256 bits avec des techniques de ''triple/quad channel'' qui sont rarement utilisées. Globalement, la configuration classique sur un PC moderne est 128 bits, avec quelques machines bas de gamme en 64 bits. Sur les cartes graphiques modernes, les bus de 128 bits ou moins sont utilisés sur les cartes graphiques de faible performance, le reste ayant un bus mémoire de 192, 256, 384, voire 512 bits. En clair, elles permettent de lire/écrire plus de données par cycle d'horloge qu'une RAM système, de 2 à 8 fois plus.
Le fait que le bus est plus large est lié au fait que les puces mémoires sont soudées. La mémoire vidéo des cartes dédiées est composée de pleins de puces mémoires accessibles en parallèle, ce qui permet de charger des blocs de plusieurs centaines d'octets en une seule fois. Les barrettes de mémoire ont des limites au nombres de broches que leur connecteur peut accepter, qui est proche de 300 pour les DDR actuelles (beaucoup de ces broches ne transfèrent pas des données, ce qui fait qu'on a bien 64 broches dédiées aux données seulement). Sans connecteurs, on est limité à ce que la puce du GPU peut accepter, et on est alors entre 4000 à 6000 broches sur les sockets de CPU ou de GPU actuels.
[[File:Puces mémories d'un GPU et d'une barrette de mémoire.png|centre|vignette|upright=2|Puces mémoires d'un GPU et d'une barrette de mémoire.]]
Pour résumer, sur les cartes graphiques dédiées, la RAM vidéo a un débit proche de la centaine de gigaoctets par secondes. Avec une mémoire RAM unifiée, vous pouvez facilement diviser cette estimation par 10.
===La mémoire vidéo est très lente===
La mémoire vidéo a donc un débit très élevé. Mais par contre, elle a un temps d'accès très lent. Concrètement, cela veut dire qu'un accès mémoire va prendre beaucoup de temps. Par exemple, si je veux lire une texture, entre le moment où j'envoie une demande de lecture à la mémoire vidéo, et le moment celle-ci me renvoie les premiers texels, il va se passer entre 200 à 1000 cycles d'horloge processeur. Par contre, une fois les premiers texels reçus, les texels suivants sont disponibles au cycle suivant, et ainsi de suite. En clair, les données lues mettent du temps avant d'arriver, mais elles arrivent par gros paquets une fois ce temps d'attente passé.
La différence entre débit et temps d'accès est primordiale sur les GPU modernes comme anciens. Toute l'architecture de la carte graphique est conçue de manière à prendre en compte ce temps d'attente. Les techniques employées sont multiples, et ne sont pas inconnues à ceux qui ont déjà lu un cours d'architecture des ordinateurs : mémoire caches, hiérarchie de caches, ''multithreading'' matériel au niveau du processeur, optimisations des accès mémoire comme des ''Load-Store Queues'' larges, des ''coalesing write buffers'', etc. Mais toutes ces techniques sont techniquement incorporées dans les processeurs de ''shaders'' et dans les circuits fixes. Aussi nous ne pouvons pas en parler dans ce chapitre. A une exception près : l'usage de caches et de ''local stores''.
==Les caches d'un GPU==
Les cartes graphiques sont censées avoir peu de caches. Les anciennes cartes graphiques se débrouillaient avec des caches spécialisés pour les textures ou pour les sommets, ce qui leur vaut les noms de caches de texture et de cache de sommets. Ce n'est que par la suite, quand les GPU commencèrent à être utilisés pour du calcul généraliste (scientifique, notamment), que la situation changea. Les GPU utilisèrent alors de plus en plus de caches généralistes, capables de stocker n'importe quelle forme de données.
Les caches en question sont intégrés dans les processeurs de ''shaders'' sur les GPU modernes. Même les caches de texture ou de sommets. Les deux sont d'ailleurs fusionnés sur les GPU modernes, vu que leur jeu d'instruction est unifié et qu'ils peuvent exécuter aussi bien des ''vertex shaders'' que des ''pixel shaders''. Sur les GPU plus anciens, avec des circuits fixes, ces caches étaient intégrés aux circuits non-programmables de gestion des textures et de la géométrie. Les caches de sommet et de texture étaient alors séparés.
===Le cache de textures===
Le '''cache de textures''', comme son nom l'indique, est un cache spécialisé dans les textures. Toutes les cartes graphiques modernes disposent de plusieurs unités de texture, qui disposent chacune de son ou ses propres caches de textures. Pas de cache partagé, ce serait peu utile et trop compliqué à implémenter.
De plus, les cartes graphiques modernes ont plusieurs caches de texture par unité de texture. Généralement, elles ont deux caches de textures : un petit cache rapide, et un gros cache lent. Les deux caches sont fortement différents. L'un est un gros cache, qui fait dans les 4 kibioctets, et l'autre est un petit cache, faisant souvent moins d'1 kibioctet. Mais le premier est plus lent que le second. Sur d'autres cartes graphiques récentes, on trouve plus de 2 caches de textures, organisés en une hiérarchie de caches de textures similaire à la hiérarchie de cache L1, L2, L3 des processeurs modernes.
Notons que ce cache interagit avec les techniques de compression de texture. Les textures sont en effet des images, qui sont donc compressées. Et elles restent compressées en mémoire vidéo, car les textures décompressées prennent beaucoup plus de place, entre 5 à 8 fois plus. Les textures sont décompressées lors des lectures : le processeur de shaders charge quelques octets, les décompresse, et utilise les données décompressées ensuite. Le cache s'introduit quelque part avant ou après la décompression.
On peut décompresser les textures avant de les placer dans le cache, ou laisser les textures compressées dans le cache. Tout est une question de compromis. Décompresser les textures dans le cache fait que la lecture dans le cache est plus rapide, car elle n'implique pas de décompression, mais le cache contient moins de données. A l'inverse, compresser les textures permet de charger plus de données dans le cache, mais rend les lectures légèrement plus lentes. C'est souvent la seconde solution qui est utilisée et ce pour deux raisons. Premièrement, la compression de texture est terriblement efficace, souvent capable de diviser par 6 la taille d'une texture, ce qui augmente drastiquement la taille effective du cache. Deuxièmement, les circuits de décompression sont généralement très rapides, très simples, et n'ajoutent que 1 à 3 cycles d'horloge lors d'une lecture.
Les anciens jeux vidéo ne faisaient que lire les textures, sans les modifier. Aussi, le cache de texture des cartes graphiques anciennes est seulement accessible en lecture, pas en écriture. Cela simplifiait fortement les circuits du cache, réduisant le nombre de transistors utilisés par le cache, réduisant sa consommation énergétique, augmentait sa rapidité, etc. Mais les jeux vidéos 3D récents utilisent des techniques dites de ''render-to-texture'', qui permettent de calculer certaines données et à les écrire en mémoire vidéo pour une utilisation ultérieure. Les textures peuvent donc être modifiées et cela se marie mal avec un cache en lecture seule.
Rendre le cache de texture accessible en écriture est une solution, mais qui demande d'ajouter beaucoup de circuits pour une utilisation somme toute peu fréquente. Une autre solution, plus adaptée, réinitialise le cache de textures quand on modifie une texture, que ce soit totalement ou partiellement. Une fois le cache vidé, les accès mémoire ultérieurs n'ont pas d'autre choix que d'aller lire la texture en mémoire et de remplir le cache avec les données chargées depuis la RAM. Les données de texture en RAM étant les bonnes, cela garantit l’absence d'erreur.
: Ces deux techniques peuvent être adaptées dans le cas où plusieurs caches de textures séparées existent sur une même carte graphique. Les écritures doivent invalider toutes les copies dans tous les caches de texture. Cela nécessite d'ajouter des circuits qui propagent l'invalidation dans tous les autres caches.
===Les caches généralistes===
La hiérarchie mémoire des GPU modernes ressemble de plus en plus à celle des CPU, avec toute une hiérarchie de caches, avec des caches L1, L2, L3, etc. Pour rappel, les processeurs multicœurs modernes ont pleins de mémoires cache, avec au minimum deux niveaux de cache, le plus souvent trois. Les trois niveaux de cache sont appelés les caches L1, L2 et L3. Pour le premier niveau, on trouve deux caches spécialisés par cœur/processeur : un cache pour les instructions et un cache pour les données. Pour le second niveau, on a un cache L2 qui peut stocker indifféremment données et instruction et qui est partagé entre plusieurs cœurs/processeurs. Le cache L3 est un cache partagé entre tous les cœurs/processeurs. Les GPU ont une organisation similaire, sauf que le nombre de cœurs est beaucoup plus grand que sur un processeur moderne.
[[File:Partage des caches sur un processeur multicoeurs.png|centre|vignette|upright=2|Partage des caches sur un processeur multicoeurs]]
Les caches d'instruction des GPU sont adaptés aux contraintes du rendu 3D. Le principe du rendu 3D est d'appliquer un shader assez simple sur un grand nombre de données, alors que les programmes généralistes effectuent un grand nombre d'instructions sur une quantité limitée de données. Les shaders sont donc des programmes assez légers, qui ont peu d'instructions. Les caches d'instructions L1 sont généralement assez petits, généralement quelques dizaines ou centaines de kilooctets. Et malgré cela, il n'est pas rare qu'un ''shader'' tienne tout entier dans le cache d'instruction, situation serait impensable sur un processeur généraliste. La seconde caractéristique est qu'un même programme s’exécute sur beaucoup de données. Il n'est pas rare que plusieurs processeurs de shaders exécutent le même ''shader''. Aussi, certains GPU partagent un même cache d’instruction entre plusieurs processeurs de ''shader'', comme c'est le cas sur les GPU AMD d'architecture GCN où un cache d'instruction de 32 kB est partagé entre 4 cœurs.
Pour les caches de données, il faut savoir qu'un shader a peu de chances de réutiliser une donnée qu'il a chargé précédemment. Les processeurs de shaders ont beaucoup de registres, ce qui fait que si accès ultérieur à une donnée il doit y avoir, elle passe généralement par les registres. Cette faible réutilisation fait que les caches de données ne sont pas censé être très utiles. Mais le fait est qu'un shader s’exécute en un grand nombre d'instances, chacune traitant un paquet de données différent. Il est très fréquent que différentes instances s’exécutent chacune sur un même cœur et ces différentes instances tendent à accéder à des données très proches, voire à des données identiques. Si un shader charge une donnée dans le cache, la donnée et ses voisines sont alors disponibles pour les autres instances. Le cas le plus classique est celui de l'accès aux textures : lors du placage de textures, des pixels contiguës accèderont à des texels contiguës. Et outre les textures, les pixels shaders ont tendance à traiter des pixels proches, donc à avoir besoin de données proches en mémoire. Ce qui fait que les caches de données sont quand même utiles.
Dans le même registre, un shader a besoin de certaines informations spécifiques, généralement constantes, pour faire son travail. Toutes les instances du shader manipulent ces données, elles ont besoin de les lire, pour les utiliser lors de l’exécution, les copier dans des registres, etc. Les GPU incorporent des '''caches de constantes''' pour accélérer l'accès à ces données. Ainsi, quand un shader lit une donnée, elle est chargée dans le cache de constante, ce qui fait que les futures instances auront accès à celle-ci dans le cache. Ces caches de constante sont séparés des autres caches de données pour une raison bien précise : les constantes en question sont d'accès peu fréquent. Généralement, on a un accès au début de chaque instance de shader, guère plus. Vu que ce sont des données peu fréquemment utilisées, elles sont censée être évincées en priorité du cache de données, qui privilégie les données fréquemment lues/écrites. Avec un cache séparé, on n'a pas ce problème. Au passage, ce cache de constante a des chances d'être partagé entre plusieurs cœurs, des cœurs différents ayant de fortes chances d’exécuter des instances différentes d'un même shader.
Il faut noter que sur la plupart des cartes graphiques modernes, les caches de données et le cache de texture sont un seul et même cache. Même chose pour le cache de sommets, utilisé par les unités géométrique, qui est fusionné avec les caches de données. La raison est que une économie de circuits qui ne coute pas grand chose en termes de performance. Rappelons que les processeurs de shaders sont unifiés à l'heure actuelle, c'est à dire qu'elles peuvent exécuter pixel shader et vertex shader. En théorie, chaque unité de shader devrait incorporer un cache de sommets et un cache de textures. Mais le processeur de shader exécute soit un pixel shader, soit un vertex shader, mais pas les deux en même temps. Donc, autant utiliser un seul cache, qui sert alternativement de cache de vertex et de cache de texture, afin d'économiser des circuits. Une fois les deux fusionnés, on obtient un cache de donnée généraliste, capable de traiter sommets et pixels, voire d'autres données. La seule difficulté tient au filtrage de textures et à sa décompression, mais cela demande juste de router les données lues vers l'unité de texture ou directement vers les registres/unités de calcul, ce qui est facile à faire.
==La mémoire partagée : un ''local store''==
En plus d'utiliser des caches, les GPU modernes utilisent des ''local stores'', aussi appelés ''scratchpad memories''. Ce sont des mémoires RAM intermédiaires entre la RAM principale et les registres. Ces local stores peuvent être vus comme des caches, mais que le programmeur doit gérer manuellement. Dans la réalité, ce sont des mémoires RAM très rapides mais de petite taille, qui sont adressées comme n'importe quelle mémoire RAM, en utilisant des adresses directement.
[[File:Scratch-Pad-Memory.jpg|centre|vignette|upright=2.0|Scratch-Pad-Memory (SPM).]]
Sur les GPU modernes, chaque processeur de ''shader'' possède un unique ''local store'', appelée la '''mémoire partagée'''. Il n'y a pas de hiérarchie des ''local store'', similaire à la hiérarchie des caches.
[[File:Cuda5.png|centre|vignette|upright=2.0|Local stores d'un GPU.]]
La faible capacité de ces mémoires, tout du moins comparé à la grande taille de la mémoire vidéo, les rend utile pour stocker temporairement des résultats de calcul "peu imposants". L'utilité principale est donc de réduire le trafic avec la mémoire centrale, les écritures de résultats temporaires étant redirigés vers les local stores. Ils sont surtout utilisés hors du rendu 3D, pour les applications de type GPGPU, où le GPU est utilisé comme architecture multicœurs pour du calcul scientifique.
===L'implémentation des ''local store''===
Vous vous attendez certainement à ce que je dise que les ''local store'' sont des mémoires séparées des mémoires caches et qu'il y a réellement des puces de mémoire RAM distinctes dans les processeurs de ''shaders''. Mais en réalité, ce n'est pas le cas pour tous les ''local store''. Le dernier niveau de ''local store'', la mémoire partagée, est bel et bien une mémoire SRAM à part des autres, avec ses propres circuits. Mais les cartes graphiques très récentes fusionnent la mémoire locale avec le cache L1.
L'avantage est une économie de transistors assez importante. De plus, cette technologie permet de partitionner le cache/''local store'' suivant les besoins. Par exemple, si la moitié du ''local store'' est utilisé, l'autre moitié peut servir de cache L1. Si le ''local store'' n'est pas utilisé, comme c'est le cas pour la majorité des rendu 3D, le cache/''local store'' est utilisé intégralement comme cache L1.
Et si vous vous demandez comment c'est possible de fusionner un cache et une mémoire RAM, voici comment le tout est implémenté. L'implémentation consiste à couper le cache en deux circuits, dont l'un est un ''local store'', et l'autre transforme le ''local store'' en cache. Ce genre de cache séparé en deux mémoires est appelé un ''phased cache'', pour ceux qui veulent en savoir plus, et ce genre de cache est parfois utilisés sur les processeurs modernes, dans des processeurs dédiés à l'embarqué ou pour certaines applications spécifiques.
Le premier circuit vérifie la présence des données à lire/écrire dans le cache. Lors d'un accès mémoire, il reçoit l'adresse mémoire à lire, et détermine si une copie de la donnée associée est dans le cache ou non. Pour cela, il utilise un système de tags qu'on ne détaillera pas ici, mais qui donne son nom à l'unité de vérification : l''''unité de tag'''. Son implémentation est très variable suivant le cache considéré, mais une simple mémoire RAM suffit généralement.
En plus de l'unité de tags, il y a une mémoire qui stocke les données, la mémoire cache proprement dite. Par simplicité, cette mémoire est une simple mémoire RAM adressable avec des adresses mémoires des plus normales, chaque ligne de cache correspondant à une adresse. La mémoire RAM de données en question n'est autre que le ''local store''. En clair, le cache s'obtient en combinant un ''local store'' avec un circuit qui s'occupe de vérifier de vérifier les succès ou défaut de cache, et qui éventuellement identifie la position de la donnée dans le cache.
[[File:Phased cache.png|centre|vignette|upright=1.5|Phased cache]]
Pour que le tout puisse servir alternativement de ''local store'' ou de cache, on doit contourner ou non l'unité de tags. Lors d'un accès au cache, on envoie l'adresse à lire/écrire à l'unité de tags. Lors d'un accès au ''local store'', on envoie l'adresse directement sur la mémoire RAM de données, sans intervention de l'unité de tags. Le contournement est d'autant plus simple que les adresses pour le ''local store'' sont distinctes des adresses de la mémoire vidéo, les espaces d'adressage ne sont pas les mêmes, les instructions utilisées pour lire/écrire dans ces deux mémoires sont aussi potentiellement différentes.
[[File:Hydride cache - local store.png|centre|vignette|upright=2.0|Hydride cache - local store]]
Il faut préciser que cette organisation en ''phased cache'' est assez naturelle. Les caches de texture utilisent cette organisation pour diverses raisons. Vu que cache L1 et cache de texture sont le même cache, il est naturel que les caches L1 et autres aient suivi le mouvement en conservant la même organisation. La transformation du cache L1 en hydride cache/''local store'' était donc assez simple à implémenter et s'est donc faite facilement.
{{NavChapitre | book=Les cartes graphiques
| prev=La microarchitecture des processeurs de shaders
| prevText=La microarchitecture des processeurs de shaders
| next=La mémoire unifiée et la mémoire vidéo dédiée
| netxText=La mémoire unifiée et la mémoire vidéo dédiée
}}{{autocat}}
4v6q3744rs286sc55xd8pn6gssi55v0
Mathc initiation/a08
0
76336
745343
725985
2025-06-25T09:25:29Z
Xhungab
23827
745343
wikitext
text/x-wiki
__NOTOC__
[[Catégorie:Mathc initiation (livre)]]
:
.
:
[[Mathc initiation/Fichiers h : c44a4| Sommaire]]
:
.
:
'''L'étude de ce chapitre peut ce faire à l'aide de cette [[https://youtube.com/playlist?list=PLi6peGpf8EPOauB80fuxHOgDdtJjEEkGB&feature=shared Playlist]].
'''
:
.
:
'''J'ai sorti de la table des matières les sections qui m'ont permis de construire mon travail, mais qui ne sont plus utilisées une fois l'étude terminée. Ces sections se trouvent ici : '''[[Mathc initiation/a362| les travaux intermédiaires]]'''
:
.
:
{{Partie{{{type|}}}|'''Fonction :'''}}
:
{{Partie{{{type|}}}|[[Mathc initiation/000b|* Dessiner une fonction]]}}
{{Partie{{{type|}}}|[[Mathc initiation/000k|* Dessiner une fonction et son inverse]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/a77|* Trigonométrie]]}}
{{Partie{{{type|}}}|[[Mathc initiation/a78|* Trigonométrie hyperbolique]]}}
{{Partie{{{type|}}}|[[Mathc initiation/a78a|* Comparons les équations Trigonométrie et les Trigonométrie hyperbolique]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/a81|* Logarithmes Exponentielles]]}}
:
.
:
{{Partie{{{type|}}}|'''Limite :'''}}
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c30|* Forme indéterminée (Règle de L'Hôpital)]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c75|* Asymptotes et Branches Paraboliques]]}}
:
.
:
{{Partie{{{type|}}}|'''Dérivé :'''}}
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c22|* La méthode de Newton (en x)]]}}
{{Partie{{{type|}}}|[[Mathc initiation/a355|* Variations sur la tangente]]}}
:
.
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c23|* Dérivé première et seconde]]}}
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c76|* Dériver des fonctions élémentaires]]}}
{{Partie{{{type|}}}|[[Mathc initiation/001K|* Dérivation logarithmique]]}}
:
.
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c41fd|* Calculer la courbure pour une fonction f(x)]]}}
:
.
:
{{Partie{{{type|}}}|'''Intégrale :'''}}
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c14|* Intégrale simple]]}}
:
{{Partie{{{type|}}}|.}}
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c74|* Intégrer les fonctions élémentaires ]]}}
{{Partie{{{type|}}}|[[Mathc initiation/a569|* Fractions partielles]]}}
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c72|* Changement de variables ]]}}
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c73|* La méthode du tableur ]]}}
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c71|* L'intégration par partie ]]}}
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c79|* La méthode des conjugués ]]}}
{{Partie{{{type|}}}|[[Mathc initiation/a89|* Méthodes trigonométriques]]}}
:
{{Partie{{{type|}}}|.}}
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c81|* Intégrales impropres]]}}
{{Partie{{{type|}}}|.}}
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c37|* Calculer l'aire sous la courbe ]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c38|* Calculer l'aire entre deux courbes ]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c42|* Volume d'un solide entre une fonction et l'axe des x par la méthode des disques ]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c41|* Volume d'un solide entre deux fonctions par la méthode des disques ]]}}
:
{{Partie{{{type|}}}|[[Mathc initiation/a101|* Volume d'un solide entre une fonction et l'axe des x par la méthode des cylindres creux ]]}}
:
:
{{Partie{{{type|}}}|[[Mathc initiation/a22|* Volume d'un solide entre deux fonction par la méthode des cylindres creux ]]}}
:
:
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c39|* Calculer la longueur d'un arc ]]}}
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c40|* Calculer la surface de révolution autour de l'axe des x ]]}}
:
.
:
{{Partie{{{type|}}}|'''Personnaliser la fonction simpson();'''}}
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c43|* La fonction int_ds(); ]]}}
{{Partie{{{type|}}}|[[Mathc initiation/Fichiers h : c44|* La fonction surfacearea_ds(); ]]}}
{{AutoCat}}
77vfjyyx22l7gsr5mokotquwjbwdb2m
Mathc initiation/Fichiers h : c76a1
0
78282
745345
708788
2025-06-25T09:31:22Z
Xhungab
23827
745345
wikitext
text/x-wiki
[[Catégorie:Mathc initiation (livre)]]
'''Sommaire''' ◀ '''''Utilise la commande "Retour en Arrière" de ton navigateur.'''''
Installer ce fichier dans votre répertoire de travail.
{{Fichier|x_hfile.h|largeur=70%|info=utilitaire|icon=Crystal Clear mimetype source h.png}}
<syntaxhighlight lang="c">
/* --------------------------------- */
/* save as x_hfile.h */
/* --------------------------------- */
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <time.h>
#include <math.h>
#include <string.h>
/* --------------------------------- */
#include "x_def.h"
#include "x_fx.h"
/* --------------------------------- */
/* --------------------------------- */
</syntaxhighlight>
Dans ce fichier il y a la déclaration des fichiers h.
{{AutoCat}}
fdw8qjoxlaloa0a3syyp158919jsvwb
Les cartes graphiques/La mémoire unifiée et la mémoire vidéo dédiée
0
80571
745277
745266
2025-06-24T13:12:06Z
Mewtow
31375
/* Les GPU intégrés */
745277
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Rare || Très fréquent
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le deisgn des caches L3/L4 est alors différent : optimisé pour un temps d'accès faible pour le CPU, optimisé pour un fort débit mémoire pour le GPU. Mais utiliser des caches séparés entre CPU et GPU pose des problèmes de cohérence des caches, mais laissons cela de côté pour le moment.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
==La mémoire vidéo dédiée et l'accès à a RAM système==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
{{NavChapitre | book=Les cartes graphiques
| prev=La microarchitecture des processeurs de shaders
| prevText=La microarchitecture des processeurs de shaders
| next=La hiérarchie mémoire d'un GPU
| netxText=La hiérarchie mémoire d'un GPU
}}{{autocat}}
lwl9c0ni4rapczw7dpucvph6dpuzcvq
745278
745277
2025-06-24T13:12:18Z
Mewtow
31375
/* Les GPU intégrés */
745278
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Rare || Très fréquent
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le deisgn des caches L3/L4 est alors différent : optimisé pour un temps d'accès faible pour le CPU, optimisé pour un fort débit mémoire pour le GPU. Mais utiliser des caches séparés entre CPU et GPU pose des problèmes de cohérence des caches, mais laissons cela de côté pour le moment.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
==La mémoire vidéo dédiée et l'accès à a RAM système==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
{{NavChapitre | book=Les cartes graphiques
| prev=La microarchitecture des processeurs de shaders
| prevText=La microarchitecture des processeurs de shaders
| next=La hiérarchie mémoire d'un GPU
| netxText=La hiérarchie mémoire d'un GPU
}}{{autocat}}
4z0p2oqeh15esbg19g2lusf96wot6wk
745279
745278
2025-06-24T13:20:42Z
Mewtow
31375
745279
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le deisgn des caches L3/L4 est alors différent : optimisé pour un temps d'accès faible pour le CPU, optimisé pour un fort débit mémoire pour le GPU. Mais utiliser des caches séparés entre CPU et GPU pose des problèmes de cohérence des caches, mais laissons cela de côté pour le moment.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
==La mémoire vidéo dédiée et l'accès à a RAM système==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
{{NavChapitre | book=Les cartes graphiques
| prev=La microarchitecture des processeurs de shaders
| prevText=La microarchitecture des processeurs de shaders
| next=La hiérarchie mémoire d'un GPU
| netxText=La hiérarchie mémoire d'un GPU
}}{{autocat}}
mju5jl0dedo3fb6is1hal80yzsw26mg
745280
745279
2025-06-24T13:27:25Z
Mewtow
31375
/* Les GPU intégrés */
745280
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.
==La mémoire vidéo dédiée et l'accès à a RAM système==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
{{NavChapitre | book=Les cartes graphiques
| prev=La microarchitecture des processeurs de shaders
| prevText=La microarchitecture des processeurs de shaders
| next=La hiérarchie mémoire d'un GPU
| netxText=La hiérarchie mémoire d'un GPU
}}{{autocat}}
2qdffoqkneadwwdra4k1g1mlfg5rves
745281
745280
2025-06-24T13:31:08Z
Mewtow
31375
/* Les transferts Direct Memory Access */
745281
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.
==La mémoire vidéo dédiée et l'accès à a RAM système==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
==La cohérence des caches sur un GPU==
Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs. Dans les grandes lignes, si on omet les circuits spécialisés pour le rendu 3D comme les circuits de la rastérisation ou les unité de textures, c'est une définition parfaite. La différence est que les GPU ont un très grand nombre de cœurs, bien supérieur aux misérables 4 à 16 cœurs d'un CPU. Par contre, cela signifie que les problèmes rencontrés sur les processeurs multicœurs sont aussi présents sur les GPU. Le principal est ce qu'on appelle la ''cohérence des caches''. Pour comprendre à quoi cela fait référence, il faut faire un rappel sur les caches d'un processeur multicœurs.
Un processeur multicœurs dispose de beaucoup de caches. Certains sont des '''caches dédiés''', c'est à dire qu'ils sont reliés à un seul cœur, pas aux autres. D'autres sont des '''caches partagés''' entre plusieurs cœurs, voire entre tous les cœurs de la puce. Les GPU contiennent les deux, comme les CPU multicœurs, avec cependant des différences. La règle, valable pour les CPU et GPU, est que les caches de plus haut niveau (L1, parfois L2) sont dédiés à un cœur, alors que les caches de plus bas niveau (L2, L3) sont partagés entre plusieurs cœurs, voire tous. Typiquement, on a des caches L1 dédiés, des caches L2 partagés entre plusieurs cœurs, et un cache L3 partagé entre tous les cœurs. Mais cette règle est parfois violée, notamment sur les GPU. Sur les CPU multicœurs, les caches L1, de données et d'instruction, sont dédiés à un cœur. Et autant c'est le cas sur la plupart des GPU, ont a vu plus haut que certains GPU partagent leur cache L1 d’instructions.
Le fait que certains caches soient dédiés ou partagés entre un nombre limité de cœurs entraine des problèmes. Prenons deux processeurs qui ont chacun une copie d'une donnée dans leur cache. Si un processeur modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches. Pour corriger ce problème, les ingénieurs ont inventé des '''protocoles de cohérence des caches''' pour détecter les données périmées et les mettre à jour. Il existe beaucoup de protocoles de cohérence des caches et la plupart utilisent des techniques dites d'espionnage du bus où chaque cache étudie ce qui se passe sur le bus mémoire. Mais autant ces techniques sont faisables avec un nombre limité de cœurs, autant elles sont impraticables avec une centaine de coeurs. Les GPU doivent donc limiter la cohérence des caches à un niveau praticable.
[[File:Cohérence des caches.png|centre|vignette|upright=2|Cohérence des caches]]
En pratique, les caches d'un GPU sont gardés incohérents et aucun protocole de cache de cache n'est utilisé. Et ce n'est pas un problème, car le rendu 3D implique un parallélisme de donnée : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches. La cohérence des caches est donc un problème bien moins important sur les GPU que sur les CPU. En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même.
Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisé. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur doit insérer une instruction pour invalider le cache dans son programme avant de faire l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo, donc qu'il s'agit d'une donnée correcte. Nous avons vu plus haut que c'est cette technique qui est utilisée pour les caches de textures. Ce cas est assez particulier car les textures sont censées être accédée en lecture uniquement, sauf dans de rares cas de techniques de ''render-to-texture''. Aussi, ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches spécialisés fonctionnent sur le même principe. Même chose pour les caches généralistes, bien que certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.
{{NavChapitre | book=Les cartes graphiques
| prev=La microarchitecture des processeurs de shaders
| prevText=La microarchitecture des processeurs de shaders
| next=La hiérarchie mémoire d'un GPU
| netxText=La hiérarchie mémoire d'un GPU
}}{{autocat}}
m1locju4bqlpr43d0njy0rw0u9ih506
745283
745281
2025-06-24T13:43:07Z
Mewtow
31375
/* La cohérence des caches sur un GPU */
745283
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.
==La mémoire vidéo dédiée et l'accès à a RAM système==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
==La cohérence des caches sur un GPU==
Pour terminer ce chapitre, nous allons parler de la '''cohérence des caches'''. La cohérence des caches est un problème qui se manifeste quand des caches séparés contiennent la même donnée, une même copie d'une donnée en RAM. Avec un GPU, le problème à plusieurs niveaux.
===La cohérence des caches entre CPU et GPU===
Un ordinateur contient un processeur et un GPU, chacun avec leurs caches séparés. Le processeur ou le GPU peuvent alors modifier la donnée chacun dans leur cache, ce qui fait que la donnée en RAM, celle dans le cache du GPU et celle dans le cache du CPU, seront totalement différentes. Il y aura donc une incohérence : une lecture à une adresse ne donnera pas le même résultat selon que la lecture vienne du CPU, du GPU ou d'un périphérique DMA. Alors qu'il idéalement, on voudrait que les caches soient cohérents, à savoir qu'ils contiennent tous la même copie d'une donnée, ou presque.
Les transferts de données se font du CPU vers le GPU, via les fameuses copies de données mentionnées plus haut, où le processeur écrit dans la mémoire vidéo et laisse la main au GPU. Les transferts dans l'autre sens sont beaucoup plus rares. Éviter les problèmes de cohérence des caches se fait alors en deux temps. Premièrement, le processeur doit écrire les données en RAM, sans passer par les caches. Deuxièmement, le GPU doit lire la mémoire RAM sans passer par ses caches à lui.
Le processeur est conçu pour contourner les caches lors de certaines écritures, à savoir que ces écritures se font en RAM directement, sans passer par le cache. C'est une fonctionnalité très importante pour communiquer avec les périphériques. Les processeurs modernes sont capables de marquer des blocs de mémoire entiers comme étant non-cacheables, à savoir que toute lecture/écriture dedans se fait sans passer par le cache. Il suffit au CPU de copier les données à envoyer au GPU dans une de ces zone non-cacheable.
La situation est gérée différemment sur les GPU intégré, à condition qu'ils partagent leur cache avec le CPU. Dans ce cas, il n'y a pas de caches séparés pour le CPU et le GPU, mais un seul cache qui ne contient qu'une seule copie valide. Il n'y a pas de problème de cohérence des caches, CPU et GPU modifient la même donnée.
===La cohérence des caches à l'intérieur du GPU===
Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs auquel on aurait rajouté des ROPs, les circuits de la rastérisation et les unités de textures. Et les problèmes rencontrés sur les processeurs multicœurs sont aussi présents sur les GPU, la cohérence des caches ne fait pas exception.
Un processeur multicœurs dispose de beaucoup de caches. Certains sont des '''caches dédiés''', c'est à dire qu'ils sont reliés à un seul cœur, pas aux autres. D'autres sont des '''caches partagés''' entre plusieurs cœurs, voire entre tous les cœurs de la puce. Les GPU contiennent les deux, comme les CPU multicœurs, avec cependant des différences. La règle, valable pour les CPU et GPU, est que les caches de plus haut niveau (L1, parfois L2) sont dédiés à un cœur, alors que les caches de plus bas niveau (L2, L3) sont partagés entre plusieurs cœurs, voire tous. Typiquement, on a des caches L1 dédiés, des caches L2 partagés entre plusieurs cœurs, et un cache L3 partagé entre tous les cœurs. Mais cette règle est parfois violée, notamment sur les GPU. Sur les CPU multicœurs, les caches L1, de données et d'instruction, sont dédiés à un cœur. Et autant c'est le cas sur la plupart des GPU, ont a vu plus haut que certains GPU partagent leur cache L1 d’instructions.
Le fait que certains caches soient dédiés ou partagés entre un nombre limité de cœurs entraine des problèmes. Prenons deux processeurs qui ont chacun une copie d'une donnée dans leur cache. Si un processeur modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches. Pour corriger ce problème, les ingénieurs ont inventé des '''protocoles de cohérence des caches''' pour détecter les données périmées et les mettre à jour. Il existe beaucoup de protocoles de cohérence des caches et la plupart utilisent des techniques dites d'espionnage du bus où chaque cache étudie ce qui se passe sur le bus mémoire. Mais autant ces techniques sont faisables avec un nombre limité de cœurs, autant elles sont impraticables avec une centaine de cœurs. Les GPU doivent donc limiter la cohérence des caches à un niveau praticable.
[[File:Cohérence des caches.png|centre|vignette|upright=2|Cohérence des caches]]
En pratique, les caches d'un GPU sont gardés incohérents et aucun protocole de cache de cache n'est utilisé. Et ce n'est pas un problème, car le rendu 3D implique un parallélisme de donnée : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches. La cohérence des caches est donc un problème bien moins important sur les GPU que sur les CPU. En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même.
Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisé. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur doit insérer une instruction pour invalider le cache dans son programme avant de faire l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo, donc qu'il s'agit d'une donnée correcte. Nous avons vu plus haut que c'est cette technique qui est utilisée pour les caches de textures. Ce cas est assez particulier car les textures sont censées être accédée en lecture uniquement, sauf dans de rares cas de techniques de ''render-to-texture''. Aussi, ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches spécialisés fonctionnent sur le même principe. Même chose pour les caches généralistes, bien que certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.
{{NavChapitre | book=Les cartes graphiques
| prev=La microarchitecture des processeurs de shaders
| prevText=La microarchitecture des processeurs de shaders
| next=La hiérarchie mémoire d'un GPU
| netxText=La hiérarchie mémoire d'un GPU
}}{{autocat}}
09p4y12a9waxfzimgaxg97if4eu8swq
745284
745283
2025-06-24T13:47:55Z
Mewtow
31375
/* La cohérence des caches entre CPU et GPU */
745284
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.
==La mémoire vidéo dédiée et l'accès à a RAM système==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
==La cohérence des caches sur un GPU==
Pour terminer ce chapitre, nous allons parler de la '''cohérence des caches'''. La cohérence des caches est un problème qui se manifeste quand des caches séparés contiennent la même donnée, une même copie d'une donnée en RAM. Avec un GPU, le problème à plusieurs niveaux.
===La cohérence des caches entre CPU et GPU===
Un ordinateur contient un processeur et un GPU, chacun avec leurs caches séparés. Le processeur ou le GPU peuvent alors modifier la donnée chacun dans leur cache, ce qui fait que la donnée en RAM, celle dans le cache du GPU et celle dans le cache du CPU, seront totalement différentes. Il y aura donc une incohérence : une lecture à une adresse ne donnera pas le même résultat selon que la lecture vienne du CPU, du GPU ou d'un périphérique DMA. Alors qu'il idéalement, on voudrait que les caches soient cohérents, à savoir qu'ils contiennent tous la même copie d'une donnée, ou presque.
Les transferts de données se font du CPU vers le GPU, via les fameuses copies de données mentionnées plus haut, où le processeur écrit dans la mémoire vidéo et laisse la main au GPU. Les transferts dans l'autre sens sont beaucoup plus rares. Éviter les problèmes de cohérence des caches se fait alors en deux temps. Premièrement, le processeur doit écrire les données en RAM, sans passer par les caches. Deuxièmement, le GPU doit lire la mémoire RAM sans passer par ses caches à lui.
Le processeur est conçu pour contourner les caches lors de certaines écritures, à savoir que ces écritures se font en RAM directement, sans passer par le cache. C'est une fonctionnalité très importante pour communiquer avec les périphériques. Les processeurs modernes sont capables de marquer des blocs de mémoire entiers comme étant non-cacheables, à savoir que toute lecture/écriture dedans se fait sans passer par le cache. Il suffit au CPU de copier les données à envoyer au GPU dans une de ces zone non-cacheable.
Pour les GPU dédiés/soudés, cela a un lien avec la mémoire vidéo mappée en mémoire. Plus haut, nous avions dit que la mémoire vidéo est visible dans l'espace d'adressage du processeur, à savoir qu'un bloc de mémoire est détourné pour adresser non pas la RAM, mais la mémoire vidéo. Et bien ce bloc de mémoire entier est marqué comme étant non-cacheable. Du côté du GPU, les échanges entre RAM système et mémoire vidéo sont le fait du contrôleur DMA. La solution est alors toute trouvée : le contrôleur DMA accède directement en mémoire vidéo et en RAM système, sans passer par les caches du GPU. Si le contrôleur DMA contourne les caches, tout va bien.
La situation est gérée différemment sur les GPU intégré, à condition qu'ils partagent leur cache avec le CPU. Dans ce cas, il n'y a pas de caches séparés pour le CPU et le GPU, mais un seul cache qui ne contient qu'une seule copie valide. Il n'y a pas de problème de cohérence des caches, CPU et GPU modifient la même donnée.
===La cohérence des caches à l'intérieur du GPU===
Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs auquel on aurait rajouté des ROPs, les circuits de la rastérisation et les unités de textures. Et les problèmes rencontrés sur les processeurs multicœurs sont aussi présents sur les GPU, la cohérence des caches ne fait pas exception.
Un processeur multicœurs dispose de beaucoup de caches. Certains sont des '''caches dédiés''', c'est à dire qu'ils sont reliés à un seul cœur, pas aux autres. D'autres sont des '''caches partagés''' entre plusieurs cœurs, voire entre tous les cœurs de la puce. Les GPU contiennent les deux, comme les CPU multicœurs, avec cependant des différences. La règle, valable pour les CPU et GPU, est que les caches de plus haut niveau (L1, parfois L2) sont dédiés à un cœur, alors que les caches de plus bas niveau (L2, L3) sont partagés entre plusieurs cœurs, voire tous. Typiquement, on a des caches L1 dédiés, des caches L2 partagés entre plusieurs cœurs, et un cache L3 partagé entre tous les cœurs. Mais cette règle est parfois violée, notamment sur les GPU. Sur les CPU multicœurs, les caches L1, de données et d'instruction, sont dédiés à un cœur. Et autant c'est le cas sur la plupart des GPU, ont a vu plus haut que certains GPU partagent leur cache L1 d’instructions.
Le fait que certains caches soient dédiés ou partagés entre un nombre limité de cœurs entraine des problèmes. Prenons deux processeurs qui ont chacun une copie d'une donnée dans leur cache. Si un processeur modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches. Pour corriger ce problème, les ingénieurs ont inventé des '''protocoles de cohérence des caches''' pour détecter les données périmées et les mettre à jour. Il existe beaucoup de protocoles de cohérence des caches et la plupart utilisent des techniques dites d'espionnage du bus où chaque cache étudie ce qui se passe sur le bus mémoire. Mais autant ces techniques sont faisables avec un nombre limité de cœurs, autant elles sont impraticables avec une centaine de cœurs. Les GPU doivent donc limiter la cohérence des caches à un niveau praticable.
[[File:Cohérence des caches.png|centre|vignette|upright=2|Cohérence des caches]]
En pratique, les caches d'un GPU sont gardés incohérents et aucun protocole de cache de cache n'est utilisé. Et ce n'est pas un problème, car le rendu 3D implique un parallélisme de donnée : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches. La cohérence des caches est donc un problème bien moins important sur les GPU que sur les CPU. En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même.
Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisé. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur doit insérer une instruction pour invalider le cache dans son programme avant de faire l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo, donc qu'il s'agit d'une donnée correcte. Nous avons vu plus haut que c'est cette technique qui est utilisée pour les caches de textures. Ce cas est assez particulier car les textures sont censées être accédée en lecture uniquement, sauf dans de rares cas de techniques de ''render-to-texture''. Aussi, ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches spécialisés fonctionnent sur le même principe. Même chose pour les caches généralistes, bien que certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.
{{NavChapitre | book=Les cartes graphiques
| prev=La microarchitecture des processeurs de shaders
| prevText=La microarchitecture des processeurs de shaders
| next=La hiérarchie mémoire d'un GPU
| netxText=La hiérarchie mémoire d'un GPU
}}{{autocat}}
ehvnx58b2lfzgp124kqyjf4pxy9v84j
745285
745284
2025-06-24T13:48:59Z
Mewtow
31375
/* La cohérence des caches sur un GPU */
745285
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.
==La mémoire vidéo dédiée et l'accès à a RAM système==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
==La cohérence des caches sur un GPU==
Pour terminer ce chapitre, nous allons parler de la '''cohérence des caches'''. La cohérence des caches est un problème qui se manifeste quand des caches séparés contiennent la même donnée, une même copie d'une donnée en RAM. Avec un GPU, le problème à plusieurs niveaux.
===La cohérence des caches à l'intérieur du GPU===
Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs auquel on aurait rajouté des ROPs, les circuits de la rastérisation et les unités de textures. Et les problèmes rencontrés sur les processeurs multicœurs sont aussi présents sur les GPU, la cohérence des caches ne fait pas exception.
Un processeur multicœurs dispose de beaucoup de caches. Certains sont des '''caches dédiés''', c'est à dire qu'ils sont reliés à un seul cœur, pas aux autres. D'autres sont des '''caches partagés''' entre plusieurs cœurs, voire entre tous les cœurs de la puce. Les GPU contiennent les deux, comme les CPU multicœurs, avec cependant des différences. La règle, valable pour les CPU et GPU, est que les caches de plus haut niveau (L1, parfois L2) sont dédiés à un cœur, alors que les caches de plus bas niveau (L2, L3) sont partagés entre plusieurs cœurs, voire tous. Typiquement, on a des caches L1 dédiés, des caches L2 partagés entre plusieurs cœurs, et un cache L3 partagé entre tous les cœurs. Mais cette règle est parfois violée, notamment sur les GPU. Sur les CPU multicœurs, les caches L1, de données et d'instruction, sont dédiés à un cœur. Et autant c'est le cas sur la plupart des GPU, ont a vu plus haut que certains GPU partagent leur cache L1 d’instructions.
Le fait que certains caches soient dédiés ou partagés entre un nombre limité de cœurs entraine des problèmes. Prenons deux processeurs qui ont chacun une copie d'une donnée dans leur cache. Si un processeur modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches. Pour corriger ce problème, les ingénieurs ont inventé des '''protocoles de cohérence des caches''' pour détecter les données périmées et les mettre à jour. Il existe beaucoup de protocoles de cohérence des caches et la plupart utilisent des techniques dites d'espionnage du bus où chaque cache étudie ce qui se passe sur le bus mémoire. Mais autant ces techniques sont faisables avec un nombre limité de cœurs, autant elles sont impraticables avec une centaine de cœurs. Les GPU doivent donc limiter la cohérence des caches à un niveau praticable.
[[File:Cohérence des caches.png|centre|vignette|upright=2|Cohérence des caches]]
En pratique, les caches d'un GPU sont gardés incohérents et aucun protocole de cache de cache n'est utilisé. Et ce n'est pas un problème, car le rendu 3D implique un parallélisme de donnée : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches. La cohérence des caches est donc un problème bien moins important sur les GPU que sur les CPU. En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même.
Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisé. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur doit insérer une instruction pour invalider le cache dans son programme avant de faire l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo, donc qu'il s'agit d'une donnée correcte. Nous avons vu plus haut que c'est cette technique qui est utilisée pour les caches de textures. Ce cas est assez particulier car les textures sont censées être accédée en lecture uniquement, sauf dans de rares cas de techniques de ''render-to-texture''. Aussi, ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches spécialisés fonctionnent sur le même principe. Même chose pour les caches généralistes, bien que certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.
===La cohérence des caches entre CPU et GPU===
Un ordinateur contient un processeur et un GPU, chacun avec leurs caches séparés. Le processeur ou le GPU peuvent alors modifier la donnée chacun dans leur cache, ce qui fait que la donnée en RAM, celle dans le cache du GPU et celle dans le cache du CPU, seront totalement différentes. Il y aura donc une incohérence : une lecture à une adresse ne donnera pas le même résultat selon que la lecture vienne du CPU, du GPU ou d'un périphérique DMA. Alors qu'il idéalement, on voudrait que les caches soient cohérents, à savoir qu'ils contiennent tous la même copie d'une donnée, ou presque.
Les transferts de données se font du CPU vers le GPU, via les fameuses copies de données mentionnées plus haut, où le processeur écrit dans la mémoire vidéo et laisse la main au GPU. Les transferts dans l'autre sens sont beaucoup plus rares. Éviter les problèmes de cohérence des caches se fait alors en deux temps. Premièrement, le processeur doit écrire les données en RAM, sans passer par les caches. Deuxièmement, le GPU doit lire la mémoire RAM sans passer par ses caches à lui.
Le processeur est conçu pour contourner les caches lors de certaines écritures, à savoir que ces écritures se font en RAM directement, sans passer par le cache. C'est une fonctionnalité très importante pour communiquer avec les périphériques. Les processeurs modernes sont capables de marquer des blocs de mémoire entiers comme étant non-cacheables, à savoir que toute lecture/écriture dedans se fait sans passer par le cache. Il suffit au CPU de copier les données à envoyer au GPU dans une de ces zone non-cacheable.
Pour les GPU dédiés/soudés, cela a un lien avec la mémoire vidéo mappée en mémoire. Plus haut, nous avions dit que la mémoire vidéo est visible dans l'espace d'adressage du processeur, à savoir qu'un bloc de mémoire est détourné pour adresser non pas la RAM, mais la mémoire vidéo. Et bien ce bloc de mémoire entier est marqué comme étant non-cacheable. Du côté du GPU, les échanges entre RAM système et mémoire vidéo sont le fait du contrôleur DMA. La solution est alors toute trouvée : le contrôleur DMA accède directement en mémoire vidéo et en RAM système, sans passer par les caches du GPU. Il suffit d'invalider les caches du GPU après un transfert DMA et le tour est joué.
La situation est gérée différemment sur les GPU intégré, à condition qu'ils partagent leur cache avec le CPU. Dans ce cas, il n'y a pas de caches séparés pour le CPU et le GPU, mais un seul cache qui ne contient qu'une seule copie valide. Il n'y a pas de problème de cohérence des caches, CPU et GPU modifient la même donnée.
{{NavChapitre | book=Les cartes graphiques
| prev=La microarchitecture des processeurs de shaders
| prevText=La microarchitecture des processeurs de shaders
| next=La hiérarchie mémoire d'un GPU
| netxText=La hiérarchie mémoire d'un GPU
}}{{autocat}}
ad6he00iwzjhqrq6hdkirlxj3ijfs3e
745286
745285
2025-06-24T13:49:42Z
Mewtow
31375
/* La cohérence des caches à l'intérieur du GPU */
745286
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.
==La mémoire vidéo dédiée et l'accès à a RAM système==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
==La cohérence des caches sur un GPU==
Pour terminer ce chapitre, nous allons parler de la '''cohérence des caches'''. La cohérence des caches est un problème qui se manifeste quand des caches séparés contiennent la même donnée, une même copie d'une donnée en RAM. Avec un GPU, le problème à plusieurs niveaux.
===La cohérence des caches à l'intérieur du GPU===
Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs auquel on aurait rajouté des ROPs, les circuits de la rastérisation et les unités de textures. Et les problèmes rencontrés sur les processeurs multicœurs sont aussi présents sur les GPU, la cohérence des caches ne fait pas exception.
Un processeur multicœurs dispose de beaucoup de caches. Certains sont des '''caches dédiés''', c'est à dire qu'ils sont reliés à un seul cœur, pas aux autres. D'autres sont des '''caches partagés''' entre plusieurs cœurs, voire entre tous les cœurs de la puce. Les GPU contiennent les deux, comme les CPU multicœurs, avec cependant des différences. La règle, valable pour les CPU et GPU, est que les caches de plus haut niveau (L1, parfois L2) sont dédiés à un cœur, alors que les caches de plus bas niveau (L2, L3) sont partagés entre plusieurs cœurs, voire tous. Typiquement, on a des caches L1 dédiés, des caches L2 partagés entre plusieurs cœurs, et un cache L3 partagé entre tous les cœurs. Mais cette règle est parfois violée, notamment sur les GPU. Sur les CPU multicœurs, les caches L1, de données et d'instruction, sont dédiés à un cœur. Et autant c'est le cas sur la plupart des GPU, autant certains GPU partagent leur cache L1 d’instructions.
Le fait que certains caches soient dédiés ou partagés entre un nombre limité de cœurs entraine des problèmes. Prenons deux processeurs qui ont chacun une copie d'une donnée dans leur cache. Si un processeur modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches. Pour corriger ce problème, les ingénieurs ont inventé des '''protocoles de cohérence des caches''' pour détecter les données périmées et les mettre à jour. Il existe beaucoup de protocoles de cohérence des caches et la plupart utilisent des techniques dites d'espionnage du bus où chaque cache étudie ce qui se passe sur le bus mémoire. Mais autant ces techniques sont faisables avec un nombre limité de cœurs, autant elles sont impraticables avec une centaine de cœurs. Les GPU doivent donc limiter la cohérence des caches à un niveau praticable.
[[File:Cohérence des caches.png|centre|vignette|upright=2|Cohérence des caches]]
En pratique, les caches d'un GPU sont gardés incohérents et aucun protocole de cache de cache n'est utilisé. Et ce n'est pas un problème, car le rendu 3D implique un parallélisme de donnée : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches. La cohérence des caches est donc un problème bien moins important sur les GPU que sur les CPU. En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même.
Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisé. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur doit insérer une instruction pour invalider le cache dans son programme avant de faire l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo, donc qu'il s'agit d'une donnée correcte. Nous avons vu plus haut que c'est cette technique qui est utilisée pour les caches de textures. Ce cas est assez particulier car les textures sont censées être accédée en lecture uniquement, sauf dans de rares cas de techniques de ''render-to-texture''. Aussi, ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches spécialisés fonctionnent sur le même principe. Même chose pour les caches généralistes, bien que certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.
===La cohérence des caches entre CPU et GPU===
Un ordinateur contient un processeur et un GPU, chacun avec leurs caches séparés. Le processeur ou le GPU peuvent alors modifier la donnée chacun dans leur cache, ce qui fait que la donnée en RAM, celle dans le cache du GPU et celle dans le cache du CPU, seront totalement différentes. Il y aura donc une incohérence : une lecture à une adresse ne donnera pas le même résultat selon que la lecture vienne du CPU, du GPU ou d'un périphérique DMA. Alors qu'il idéalement, on voudrait que les caches soient cohérents, à savoir qu'ils contiennent tous la même copie d'une donnée, ou presque.
Les transferts de données se font du CPU vers le GPU, via les fameuses copies de données mentionnées plus haut, où le processeur écrit dans la mémoire vidéo et laisse la main au GPU. Les transferts dans l'autre sens sont beaucoup plus rares. Éviter les problèmes de cohérence des caches se fait alors en deux temps. Premièrement, le processeur doit écrire les données en RAM, sans passer par les caches. Deuxièmement, le GPU doit lire la mémoire RAM sans passer par ses caches à lui.
Le processeur est conçu pour contourner les caches lors de certaines écritures, à savoir que ces écritures se font en RAM directement, sans passer par le cache. C'est une fonctionnalité très importante pour communiquer avec les périphériques. Les processeurs modernes sont capables de marquer des blocs de mémoire entiers comme étant non-cacheables, à savoir que toute lecture/écriture dedans se fait sans passer par le cache. Il suffit au CPU de copier les données à envoyer au GPU dans une de ces zone non-cacheable.
Pour les GPU dédiés/soudés, cela a un lien avec la mémoire vidéo mappée en mémoire. Plus haut, nous avions dit que la mémoire vidéo est visible dans l'espace d'adressage du processeur, à savoir qu'un bloc de mémoire est détourné pour adresser non pas la RAM, mais la mémoire vidéo. Et bien ce bloc de mémoire entier est marqué comme étant non-cacheable. Du côté du GPU, les échanges entre RAM système et mémoire vidéo sont le fait du contrôleur DMA. La solution est alors toute trouvée : le contrôleur DMA accède directement en mémoire vidéo et en RAM système, sans passer par les caches du GPU. Il suffit d'invalider les caches du GPU après un transfert DMA et le tour est joué.
La situation est gérée différemment sur les GPU intégré, à condition qu'ils partagent leur cache avec le CPU. Dans ce cas, il n'y a pas de caches séparés pour le CPU et le GPU, mais un seul cache qui ne contient qu'une seule copie valide. Il n'y a pas de problème de cohérence des caches, CPU et GPU modifient la même donnée.
{{NavChapitre | book=Les cartes graphiques
| prev=La microarchitecture des processeurs de shaders
| prevText=La microarchitecture des processeurs de shaders
| next=La hiérarchie mémoire d'un GPU
| netxText=La hiérarchie mémoire d'un GPU
}}{{autocat}}
i2b6jtosotfi50rujqj2nnmoaosu77z
745290
745286
2025-06-24T13:51:25Z
Mewtow
31375
/* La cohérence des caches entre CPU et GPU */
745290
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.
==La mémoire vidéo dédiée et l'accès à a RAM système==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
==La cohérence des caches sur un GPU==
Pour terminer ce chapitre, nous allons parler de la '''cohérence des caches'''. La cohérence des caches est un problème qui se manifeste quand des caches séparés contiennent la même donnée, une même copie d'une donnée en RAM. Avec un GPU, le problème à plusieurs niveaux.
===La cohérence des caches à l'intérieur du GPU===
Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs auquel on aurait rajouté des ROPs, les circuits de la rastérisation et les unités de textures. Et les problèmes rencontrés sur les processeurs multicœurs sont aussi présents sur les GPU, la cohérence des caches ne fait pas exception.
Un processeur multicœurs dispose de beaucoup de caches. Certains sont des '''caches dédiés''', c'est à dire qu'ils sont reliés à un seul cœur, pas aux autres. D'autres sont des '''caches partagés''' entre plusieurs cœurs, voire entre tous les cœurs de la puce. Les GPU contiennent les deux, comme les CPU multicœurs, avec cependant des différences. La règle, valable pour les CPU et GPU, est que les caches de plus haut niveau (L1, parfois L2) sont dédiés à un cœur, alors que les caches de plus bas niveau (L2, L3) sont partagés entre plusieurs cœurs, voire tous. Typiquement, on a des caches L1 dédiés, des caches L2 partagés entre plusieurs cœurs, et un cache L3 partagé entre tous les cœurs. Mais cette règle est parfois violée, notamment sur les GPU. Sur les CPU multicœurs, les caches L1, de données et d'instruction, sont dédiés à un cœur. Et autant c'est le cas sur la plupart des GPU, autant certains GPU partagent leur cache L1 d’instructions.
Le fait que certains caches soient dédiés ou partagés entre un nombre limité de cœurs entraine des problèmes. Prenons deux processeurs qui ont chacun une copie d'une donnée dans leur cache. Si un processeur modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches. Pour corriger ce problème, les ingénieurs ont inventé des '''protocoles de cohérence des caches''' pour détecter les données périmées et les mettre à jour. Il existe beaucoup de protocoles de cohérence des caches et la plupart utilisent des techniques dites d'espionnage du bus où chaque cache étudie ce qui se passe sur le bus mémoire. Mais autant ces techniques sont faisables avec un nombre limité de cœurs, autant elles sont impraticables avec une centaine de cœurs. Les GPU doivent donc limiter la cohérence des caches à un niveau praticable.
[[File:Cohérence des caches.png|centre|vignette|upright=2|Cohérence des caches]]
En pratique, les caches d'un GPU sont gardés incohérents et aucun protocole de cache de cache n'est utilisé. Et ce n'est pas un problème, car le rendu 3D implique un parallélisme de donnée : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches. La cohérence des caches est donc un problème bien moins important sur les GPU que sur les CPU. En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même.
Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisé. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur doit insérer une instruction pour invalider le cache dans son programme avant de faire l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo, donc qu'il s'agit d'une donnée correcte. Nous avons vu plus haut que c'est cette technique qui est utilisée pour les caches de textures. Ce cas est assez particulier car les textures sont censées être accédée en lecture uniquement, sauf dans de rares cas de techniques de ''render-to-texture''. Aussi, ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches spécialisés fonctionnent sur le même principe. Même chose pour les caches généralistes, bien que certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.
===La cohérence des caches entre CPU et GPU===
Un ordinateur contient un processeur et un GPU, chacun avec leurs caches séparés. Le processeur ou le GPU peuvent alors modifier la donnée chacun dans leur cache, ce qui fait que la donnée en RAM, celle dans le cache du GPU et celle dans le cache du CPU, seront totalement différentes. Il y aura donc une incohérence : une lecture à une adresse ne donnera pas le même résultat selon que la lecture vienne du CPU, du GPU ou d'un périphérique DMA. Alors qu'il idéalement, on voudrait que les caches soient cohérents, à savoir qu'ils contiennent tous la même copie d'une donnée, ou presque.
Les transferts de données se font du CPU vers le GPU, via les fameuses copies de données mentionnées plus haut, où le processeur écrit dans la mémoire vidéo et laisse la main au GPU. Les transferts dans l'autre sens sont beaucoup plus rares. Éviter les problèmes de cohérence des caches se fait alors en deux temps. Premièrement, le processeur doit écrire les données en RAM, sans passer par les caches. Deuxièmement, le GPU doit lire la mémoire RAM sans passer par ses caches à lui.
Le processeur est conçu pour contourner les caches lors de certaines écritures, à savoir que ces écritures se font en RAM directement, sans passer par le cache. C'est une fonctionnalité très importante pour communiquer avec les périphériques. Les processeurs modernes sont capables de marquer des blocs de mémoire entiers comme étant non-cacheables, à savoir que toute lecture/écriture dedans se fait sans passer par le cache. Il suffit au CPU de copier les données à envoyer au GPU dans une de ces zone non-cacheable.
Pour les GPU dédiés/soudés, cela a un lien avec la mémoire vidéo mappée en mémoire. Plus haut, nous avions dit que la mémoire vidéo est visible dans l'espace d'adressage du processeur, à savoir qu'un bloc de mémoire est détourné pour adresser non pas la RAM, mais la mémoire vidéo. Et bien ce bloc de mémoire entier est marqué comme étant non-cacheable. Du côté du GPU, les échanges entre RAM système et mémoire vidéo sont le fait du contrôleur DMA. La solution est alors toute trouvée : le contrôleur DMA accède directement en mémoire vidéo et en RAM système, sans passer par les caches du GPU. Il suffit d'invalider les caches du GPU après un transfert DMA et le tour est joué.
La situation est gérée différemment sur les GPU intégré, à condition qu'ils partagent leur cache avec le CPU. Dans ce cas, il n'y a pas de caches séparés pour le CPU et le GPU, mais un seul cache qui ne contient qu'une seule copie valide. Il n'y a pas de problème de cohérence des caches, CPU et GPU modifient la même donnée.
{{NavChapitre | book=Les cartes graphiques
| prev=La hiérarchie mémoire d'un GPU
| prevText=La hiérarchie mémoire d'un GPU
| next=Le processeur de commandes
| netxText=Le processeur de commandes
}}{{autocat}}
13yljvywd7b70zrm1i8ic1do7mudbvj
745293
745290
2025-06-24T13:57:26Z
Mewtow
31375
/* La cohérence des caches à l'intérieur du GPU */
745293
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.
==La mémoire vidéo dédiée et l'accès à a RAM système==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
==La cohérence des caches sur un GPU==
Pour terminer ce chapitre, nous allons parler de la '''cohérence des caches'''. La cohérence des caches est un problème qui se manifeste quand des caches séparés contiennent la même donnée, une même copie d'une donnée en RAM. Avec un GPU, le problème à plusieurs niveaux.
===La cohérence des caches à l'intérieur du GPU===
Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs auquel on aurait rajouté des ROPs, les circuits de la rastérisation et les unités de textures. Il n'est donc pas étonnant que les problèmes rencontrés sur les processeurs multicœurs soient aussi présents sur les GPU, la cohérence des caches ne fait pas exception.
Un processeur multicœurs dispose de beaucoup de caches. Mais pour simplifier les explications, nous allons partir du principe que chaque cœur a son propre cache de données. Prenons deux processeurs/coeurs qui ont chacun une copie d'une donnée dans leur cache. Si un processeur modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches.
Pour corriger ce problème, les ingénieurs ont inventé des '''protocoles de cohérence des caches''' pour détecter les données périmées et les mettre à jour. Mais autant ces techniques sont faisables avec un nombre limité de cœurs, autant elles sont impraticables avec une centaine de cœurs. Les GPU doivent donc limiter la cohérence des caches à un niveau praticable.
[[File:Cohérence des caches.png|centre|vignette|upright=2|Cohérence des caches]]
La réalité est cependant plus complexe, dans le sens où il n'y a souvent pas un cache par cœur, mais une hiérarchie de cache assez complexe, avec un cache L1 par cœur, un cache L2 partagé entre plusieurs cœurs, des caches partagés entre tous les cœurs, etc. Certains GPU partagent leur cache L1 d’instructions entre plusieurs cœurs, d'autres non. Mais le principe reste valide tant qu'un cache n'est pas partagé entre tous les cœurs du CPU/GPU.
En pratique, les caches d'un GPU sont gardés incohérents et aucun protocole de cache de cache n'est utilisé. Et ce n'est pas un problème, car le rendu 3D implique un parallélisme de donnée : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches. La cohérence des caches est donc un problème bien moins important sur les GPU que sur les CPU. En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même.
Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisé. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur doit insérer une instruction pour invalider le cache dans son programme avant de faire l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo, donc qu'il s'agit d'une donnée correcte. Nous avons vu plus haut que c'est cette technique qui est utilisée pour les caches de textures. Ce cas est assez particulier car les textures sont censées être accédée en lecture uniquement, sauf dans de rares cas de techniques de ''render-to-texture''. Aussi, ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches spécialisés fonctionnent sur le même principe. Même chose pour les caches généralistes, bien que certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.
===La cohérence des caches entre CPU et GPU===
Un ordinateur contient un processeur et un GPU, chacun avec leurs caches séparés. Le processeur ou le GPU peuvent alors modifier la donnée chacun dans leur cache, ce qui fait que la donnée en RAM, celle dans le cache du GPU et celle dans le cache du CPU, seront totalement différentes. Il y aura donc une incohérence : une lecture à une adresse ne donnera pas le même résultat selon que la lecture vienne du CPU, du GPU ou d'un périphérique DMA. Alors qu'il idéalement, on voudrait que les caches soient cohérents, à savoir qu'ils contiennent tous la même copie d'une donnée, ou presque.
Les transferts de données se font du CPU vers le GPU, via les fameuses copies de données mentionnées plus haut, où le processeur écrit dans la mémoire vidéo et laisse la main au GPU. Les transferts dans l'autre sens sont beaucoup plus rares. Éviter les problèmes de cohérence des caches se fait alors en deux temps. Premièrement, le processeur doit écrire les données en RAM, sans passer par les caches. Deuxièmement, le GPU doit lire la mémoire RAM sans passer par ses caches à lui.
Le processeur est conçu pour contourner les caches lors de certaines écritures, à savoir que ces écritures se font en RAM directement, sans passer par le cache. C'est une fonctionnalité très importante pour communiquer avec les périphériques. Les processeurs modernes sont capables de marquer des blocs de mémoire entiers comme étant non-cacheables, à savoir que toute lecture/écriture dedans se fait sans passer par le cache. Il suffit au CPU de copier les données à envoyer au GPU dans une de ces zone non-cacheable.
Pour les GPU dédiés/soudés, cela a un lien avec la mémoire vidéo mappée en mémoire. Plus haut, nous avions dit que la mémoire vidéo est visible dans l'espace d'adressage du processeur, à savoir qu'un bloc de mémoire est détourné pour adresser non pas la RAM, mais la mémoire vidéo. Et bien ce bloc de mémoire entier est marqué comme étant non-cacheable. Du côté du GPU, les échanges entre RAM système et mémoire vidéo sont le fait du contrôleur DMA. La solution est alors toute trouvée : le contrôleur DMA accède directement en mémoire vidéo et en RAM système, sans passer par les caches du GPU. Il suffit d'invalider les caches du GPU après un transfert DMA et le tour est joué.
La situation est gérée différemment sur les GPU intégré, à condition qu'ils partagent leur cache avec le CPU. Dans ce cas, il n'y a pas de caches séparés pour le CPU et le GPU, mais un seul cache qui ne contient qu'une seule copie valide. Il n'y a pas de problème de cohérence des caches, CPU et GPU modifient la même donnée.
{{NavChapitre | book=Les cartes graphiques
| prev=La hiérarchie mémoire d'un GPU
| prevText=La hiérarchie mémoire d'un GPU
| next=Le processeur de commandes
| netxText=Le processeur de commandes
}}{{autocat}}
piro2cgi3oklmbxg5rv2hslu6gzsosy
745294
745293
2025-06-24T13:58:38Z
Mewtow
31375
/* La cohérence des caches à l'intérieur du GPU */
745294
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.
==La mémoire vidéo dédiée et l'accès à a RAM système==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
==La cohérence des caches sur un GPU==
Pour terminer ce chapitre, nous allons parler de la '''cohérence des caches'''. La cohérence des caches est un problème qui se manifeste quand des caches séparés contiennent la même donnée, une même copie d'une donnée en RAM. Avec un GPU, le problème à plusieurs niveaux.
===La cohérence des caches à l'intérieur du GPU===
Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs auquel on aurait rajouté des ROPs, les circuits de la rastérisation et les unités de textures. Il n'est donc pas étonnant que les problèmes rencontrés sur les processeurs multicœurs soient aussi présents sur les GPU, la cohérence des caches ne fait pas exception.
Un processeur multicœurs dispose de beaucoup de caches. Mais pour simplifier les explications, nous allons partir du principe que chaque cœur a son propre cache de données. Prenons deux processeurs/cœurs qui ont chacun une copie d'une donnée dans leur cache. Si un processeur modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches.
[[File:Cohérence des caches.png|centre|vignette|upright=2|Cohérence des caches]]
La réalité est cependant plus complexe, dans le sens où il n'y a souvent pas un cache par cœur, mais une hiérarchie de cache assez complexe, avec un cache L1 par cœur, un cache L2 partagé entre plusieurs cœurs, des caches partagés entre tous les cœurs, etc. Certains GPU partagent leur cache L1 d’instructions entre plusieurs cœurs, d'autres non. Mais le principe reste valide sur les GPU, tant qu'un cache n'est pas partagé entre tous les processeurs de shaders : un cache peut contenir une donnée invalide, à savoir qu'elle a été modifiée par dans le cache d'un autre processeur de shaders.
Pour corriger ce problème, les ingénieurs ont inventé des '''protocoles de cohérence des caches''' pour détecter les données périmées et les mettre à jour. Mais autant ces techniques sont faisables avec un nombre limité de cœurs, autant elles sont impraticables avec une centaine de cœurs. Les GPU doivent donc limiter la cohérence des caches à un niveau praticable.
En pratique, les caches d'un GPU sont gardés incohérents et aucun protocole de cache de cache n'est utilisé. Et ce n'est pas un problème, car le rendu 3D implique un parallélisme de donnée : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches. La cohérence des caches est donc un problème bien moins important sur les GPU que sur les CPU. En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même.
Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisé. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur doit insérer une instruction pour invalider le cache dans son programme avant de faire l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo, donc qu'il s'agit d'une donnée correcte. Nous avons vu plus haut que c'est cette technique qui est utilisée pour les caches de textures. Ce cas est assez particulier car les textures sont censées être accédée en lecture uniquement, sauf dans de rares cas de techniques de ''render-to-texture''. Aussi, ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches spécialisés fonctionnent sur le même principe. Même chose pour les caches généralistes, bien que certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.
===La cohérence des caches entre CPU et GPU===
Un ordinateur contient un processeur et un GPU, chacun avec leurs caches séparés. Le processeur ou le GPU peuvent alors modifier la donnée chacun dans leur cache, ce qui fait que la donnée en RAM, celle dans le cache du GPU et celle dans le cache du CPU, seront totalement différentes. Il y aura donc une incohérence : une lecture à une adresse ne donnera pas le même résultat selon que la lecture vienne du CPU, du GPU ou d'un périphérique DMA. Alors qu'il idéalement, on voudrait que les caches soient cohérents, à savoir qu'ils contiennent tous la même copie d'une donnée, ou presque.
Les transferts de données se font du CPU vers le GPU, via les fameuses copies de données mentionnées plus haut, où le processeur écrit dans la mémoire vidéo et laisse la main au GPU. Les transferts dans l'autre sens sont beaucoup plus rares. Éviter les problèmes de cohérence des caches se fait alors en deux temps. Premièrement, le processeur doit écrire les données en RAM, sans passer par les caches. Deuxièmement, le GPU doit lire la mémoire RAM sans passer par ses caches à lui.
Le processeur est conçu pour contourner les caches lors de certaines écritures, à savoir que ces écritures se font en RAM directement, sans passer par le cache. C'est une fonctionnalité très importante pour communiquer avec les périphériques. Les processeurs modernes sont capables de marquer des blocs de mémoire entiers comme étant non-cacheables, à savoir que toute lecture/écriture dedans se fait sans passer par le cache. Il suffit au CPU de copier les données à envoyer au GPU dans une de ces zone non-cacheable.
Pour les GPU dédiés/soudés, cela a un lien avec la mémoire vidéo mappée en mémoire. Plus haut, nous avions dit que la mémoire vidéo est visible dans l'espace d'adressage du processeur, à savoir qu'un bloc de mémoire est détourné pour adresser non pas la RAM, mais la mémoire vidéo. Et bien ce bloc de mémoire entier est marqué comme étant non-cacheable. Du côté du GPU, les échanges entre RAM système et mémoire vidéo sont le fait du contrôleur DMA. La solution est alors toute trouvée : le contrôleur DMA accède directement en mémoire vidéo et en RAM système, sans passer par les caches du GPU. Il suffit d'invalider les caches du GPU après un transfert DMA et le tour est joué.
La situation est gérée différemment sur les GPU intégré, à condition qu'ils partagent leur cache avec le CPU. Dans ce cas, il n'y a pas de caches séparés pour le CPU et le GPU, mais un seul cache qui ne contient qu'une seule copie valide. Il n'y a pas de problème de cohérence des caches, CPU et GPU modifient la même donnée.
{{NavChapitre | book=Les cartes graphiques
| prev=La hiérarchie mémoire d'un GPU
| prevText=La hiérarchie mémoire d'un GPU
| next=Le processeur de commandes
| netxText=Le processeur de commandes
}}{{autocat}}
osjwu1shv5pikm10bc3ffikiv8ejxev
745295
745294
2025-06-24T14:00:46Z
Mewtow
31375
/* La cohérence des caches à l'intérieur du GPU */
745295
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.
==La mémoire vidéo dédiée et l'accès à a RAM système==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
==La cohérence des caches sur un GPU==
Pour terminer ce chapitre, nous allons parler de la '''cohérence des caches'''. La cohérence des caches est un problème qui se manifeste quand des caches séparés contiennent la même donnée, une même copie d'une donnée en RAM. Avec un GPU, le problème à plusieurs niveaux.
===La cohérence des caches à l'intérieur du GPU===
Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs auquel on aurait rajouté des ROPs, les circuits de la rastérisation et les unités de textures. Il n'est donc pas étonnant que les problèmes rencontrés sur les processeurs multicœurs soient aussi présents sur les GPU, la cohérence des caches ne fait pas exception.
Un processeur multicœurs dispose de beaucoup de caches. Mais pour simplifier les explications, nous allons partir du principe que chaque cœur a son propre cache de données. Prenons deux processeurs/cœurs qui ont chacun une copie d'une donnée dans leur cache. Si un processeur modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches.
[[File:Cohérence des caches.png|centre|vignette|upright=2|Cohérence des caches]]
La réalité est cependant plus complexe, dans le sens où il n'y a souvent pas un cache par cœur, mais une hiérarchie de cache assez complexe, avec un cache L1 par cœur, un cache L2 partagé entre plusieurs cœurs, des caches partagés entre tous les cœurs, etc. Certains GPU partagent leur cache L1 d’instructions entre plusieurs cœurs, d'autres non. Mais le principe reste valide sur les GPU, tant qu'un cache n'est pas partagé entre tous les processeurs de shaders : un cache peut contenir une donnée invalide, à savoir qu'elle a été modifiée par dans le cache d'un autre processeur de shaders.
Pour corriger ce problème, les ingénieurs ont inventé des '''protocoles de cohérence des caches''' pour détecter les données périmées et les mettre à jour. Mais autant ces techniques sont faisables avec un nombre limité de cœurs, autant elles sont impraticables avec une centaine de cœurs. Mais heureusement, la cohérence des caches est un problème bien moins important sur les GPU que sur les CPU. En effet, le rendu 3D implique un parallélisme de donnée : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches.
En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même. Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisés. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur insére une instruction qui invalide le cache, avant l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo et donc qu'il s'agit d'une donnée correcte.
C'est cette technique qui est utilisée pour les caches de textures. Ce cas est assez particulier car les textures sont censées être accédée en lecture uniquement, sauf dans de rares cas de techniques de ''render-to-texture''. Aussi, ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches spécialisés fonctionnent sur le même principe. Même chose pour les caches généralistes, bien que certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.
===La cohérence des caches entre CPU et GPU===
Un ordinateur contient un processeur et un GPU, chacun avec leurs caches séparés. Le processeur ou le GPU peuvent alors modifier la donnée chacun dans leur cache, ce qui fait que la donnée en RAM, celle dans le cache du GPU et celle dans le cache du CPU, seront totalement différentes. Il y aura donc une incohérence : une lecture à une adresse ne donnera pas le même résultat selon que la lecture vienne du CPU, du GPU ou d'un périphérique DMA. Alors qu'il idéalement, on voudrait que les caches soient cohérents, à savoir qu'ils contiennent tous la même copie d'une donnée, ou presque.
Les transferts de données se font du CPU vers le GPU, via les fameuses copies de données mentionnées plus haut, où le processeur écrit dans la mémoire vidéo et laisse la main au GPU. Les transferts dans l'autre sens sont beaucoup plus rares. Éviter les problèmes de cohérence des caches se fait alors en deux temps. Premièrement, le processeur doit écrire les données en RAM, sans passer par les caches. Deuxièmement, le GPU doit lire la mémoire RAM sans passer par ses caches à lui.
Le processeur est conçu pour contourner les caches lors de certaines écritures, à savoir que ces écritures se font en RAM directement, sans passer par le cache. C'est une fonctionnalité très importante pour communiquer avec les périphériques. Les processeurs modernes sont capables de marquer des blocs de mémoire entiers comme étant non-cacheables, à savoir que toute lecture/écriture dedans se fait sans passer par le cache. Il suffit au CPU de copier les données à envoyer au GPU dans une de ces zone non-cacheable.
Pour les GPU dédiés/soudés, cela a un lien avec la mémoire vidéo mappée en mémoire. Plus haut, nous avions dit que la mémoire vidéo est visible dans l'espace d'adressage du processeur, à savoir qu'un bloc de mémoire est détourné pour adresser non pas la RAM, mais la mémoire vidéo. Et bien ce bloc de mémoire entier est marqué comme étant non-cacheable. Du côté du GPU, les échanges entre RAM système et mémoire vidéo sont le fait du contrôleur DMA. La solution est alors toute trouvée : le contrôleur DMA accède directement en mémoire vidéo et en RAM système, sans passer par les caches du GPU. Il suffit d'invalider les caches du GPU après un transfert DMA et le tour est joué.
La situation est gérée différemment sur les GPU intégré, à condition qu'ils partagent leur cache avec le CPU. Dans ce cas, il n'y a pas de caches séparés pour le CPU et le GPU, mais un seul cache qui ne contient qu'une seule copie valide. Il n'y a pas de problème de cohérence des caches, CPU et GPU modifient la même donnée.
{{NavChapitre | book=Les cartes graphiques
| prev=La hiérarchie mémoire d'un GPU
| prevText=La hiérarchie mémoire d'un GPU
| next=Le processeur de commandes
| netxText=Le processeur de commandes
}}{{autocat}}
ht39dyv2s2fqkpapb36bmh2aph44t6i
745296
745295
2025-06-24T14:02:34Z
Mewtow
31375
/* La cohérence des caches à l'intérieur du GPU */
745296
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.
==La mémoire vidéo dédiée et l'accès à a RAM système==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
==La cohérence des caches sur un GPU==
Pour terminer ce chapitre, nous allons parler de la '''cohérence des caches'''. La cohérence des caches est un problème qui se manifeste quand des caches séparés contiennent la même donnée, une même copie d'une donnée en RAM. Avec un GPU, le problème à plusieurs niveaux.
===La cohérence des caches à l'intérieur du GPU===
Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs auquel on aurait rajouté des ROPs, les circuits de la rastérisation et les unités de textures. Il n'est donc pas étonnant que les problèmes rencontrés sur les processeurs multicœurs soient aussi présents sur les GPU, la cohérence des caches ne fait pas exception.
Un processeur multicœurs dispose de beaucoup de caches. Mais pour simplifier les explications, nous allons partir du principe que chaque cœur a son propre cache de données. Prenons deux processeurs/cœurs qui ont chacun une copie d'une donnée dans leur cache. Si un processeur modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches.
[[File:Cohérence des caches.png|centre|vignette|upright=2|Cohérence des caches]]
La réalité est cependant plus complexe, dans le sens où il n'y a souvent pas un cache par cœur, mais une hiérarchie de cache assez complexe, avec un cache L1 par cœur, un cache L2 partagé entre plusieurs cœurs, des caches partagés entre tous les cœurs, etc. Certains GPU partagent leur cache L1 d’instructions entre plusieurs cœurs, d'autres non. Mais le principe reste valide sur les GPU, tant qu'un cache n'est pas partagé entre tous les processeurs de shaders : un cache peut contenir une donnée invalide, à savoir qu'elle a été modifiée par dans le cache d'un autre processeur de shaders.
Pour corriger ce problème, les ingénieurs ont inventé des '''protocoles de cohérence des caches''' pour détecter les données périmées et les mettre à jour. Mais autant ces techniques sont faisables avec un nombre limité de cœurs, autant elles sont impraticables avec une centaine de cœurs. Mais heureusement, la cohérence des caches est un problème bien moins important sur les GPU que sur les CPU. En effet, le rendu 3D implique un parallélisme de donnée : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches.
En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même. Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisés. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur insére une instruction qui invalide le cache, avant l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo et donc qu'il s'agit d'une donnée correcte.
C'est cette technique qui est utilisée pour les caches de textures, pour supporter les techniques de ''render-to-texture''. Dans le chapitre précédent, on a vu que lorsque d'une texture est modifiée par le GPU, les caches de texture sont réinitialisés. Les opérations de ''render-to-texture'' étant assez rares, il vaut mieux faire cela que de rendre les caches de texture accesibles en écriture. Ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches du GPU sont gérés avec le même principe. Pour les caches généralistes, certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.
===La cohérence des caches entre CPU et GPU===
Un ordinateur contient un processeur et un GPU, chacun avec leurs caches séparés. Le processeur ou le GPU peuvent alors modifier la donnée chacun dans leur cache, ce qui fait que la donnée en RAM, celle dans le cache du GPU et celle dans le cache du CPU, seront totalement différentes. Il y aura donc une incohérence : une lecture à une adresse ne donnera pas le même résultat selon que la lecture vienne du CPU, du GPU ou d'un périphérique DMA. Alors qu'il idéalement, on voudrait que les caches soient cohérents, à savoir qu'ils contiennent tous la même copie d'une donnée, ou presque.
Les transferts de données se font du CPU vers le GPU, via les fameuses copies de données mentionnées plus haut, où le processeur écrit dans la mémoire vidéo et laisse la main au GPU. Les transferts dans l'autre sens sont beaucoup plus rares. Éviter les problèmes de cohérence des caches se fait alors en deux temps. Premièrement, le processeur doit écrire les données en RAM, sans passer par les caches. Deuxièmement, le GPU doit lire la mémoire RAM sans passer par ses caches à lui.
Le processeur est conçu pour contourner les caches lors de certaines écritures, à savoir que ces écritures se font en RAM directement, sans passer par le cache. C'est une fonctionnalité très importante pour communiquer avec les périphériques. Les processeurs modernes sont capables de marquer des blocs de mémoire entiers comme étant non-cacheables, à savoir que toute lecture/écriture dedans se fait sans passer par le cache. Il suffit au CPU de copier les données à envoyer au GPU dans une de ces zone non-cacheable.
Pour les GPU dédiés/soudés, cela a un lien avec la mémoire vidéo mappée en mémoire. Plus haut, nous avions dit que la mémoire vidéo est visible dans l'espace d'adressage du processeur, à savoir qu'un bloc de mémoire est détourné pour adresser non pas la RAM, mais la mémoire vidéo. Et bien ce bloc de mémoire entier est marqué comme étant non-cacheable. Du côté du GPU, les échanges entre RAM système et mémoire vidéo sont le fait du contrôleur DMA. La solution est alors toute trouvée : le contrôleur DMA accède directement en mémoire vidéo et en RAM système, sans passer par les caches du GPU. Il suffit d'invalider les caches du GPU après un transfert DMA et le tour est joué.
La situation est gérée différemment sur les GPU intégré, à condition qu'ils partagent leur cache avec le CPU. Dans ce cas, il n'y a pas de caches séparés pour le CPU et le GPU, mais un seul cache qui ne contient qu'une seule copie valide. Il n'y a pas de problème de cohérence des caches, CPU et GPU modifient la même donnée.
{{NavChapitre | book=Les cartes graphiques
| prev=La hiérarchie mémoire d'un GPU
| prevText=La hiérarchie mémoire d'un GPU
| next=Le processeur de commandes
| netxText=Le processeur de commandes
}}{{autocat}}
1svxvg3rplnnbc4b1d5uth654viysuq
745297
745296
2025-06-24T14:09:35Z
Mewtow
31375
/* La mémoire vidéo dédiée et l'accès à a RAM système */
745297
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.
==La mémoire vidéo dédiée==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
==La cohérence des caches sur un GPU==
Pour terminer ce chapitre, nous allons parler de la '''cohérence des caches'''. La cohérence des caches est un problème qui se manifeste quand des caches séparés contiennent la même donnée, une même copie d'une donnée en RAM. Avec un GPU, le problème à plusieurs niveaux.
===La cohérence des caches à l'intérieur du GPU===
Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs auquel on aurait rajouté des ROPs, les circuits de la rastérisation et les unités de textures. Il n'est donc pas étonnant que les problèmes rencontrés sur les processeurs multicœurs soient aussi présents sur les GPU, la cohérence des caches ne fait pas exception.
Un processeur multicœurs dispose de beaucoup de caches. Mais pour simplifier les explications, nous allons partir du principe que chaque cœur a son propre cache de données. Prenons deux processeurs/cœurs qui ont chacun une copie d'une donnée dans leur cache. Si un processeur modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches.
[[File:Cohérence des caches.png|centre|vignette|upright=2|Cohérence des caches]]
La réalité est cependant plus complexe, dans le sens où il n'y a souvent pas un cache par cœur, mais une hiérarchie de cache assez complexe, avec un cache L1 par cœur, un cache L2 partagé entre plusieurs cœurs, des caches partagés entre tous les cœurs, etc. Certains GPU partagent leur cache L1 d’instructions entre plusieurs cœurs, d'autres non. Mais le principe reste valide sur les GPU, tant qu'un cache n'est pas partagé entre tous les processeurs de shaders : un cache peut contenir une donnée invalide, à savoir qu'elle a été modifiée par dans le cache d'un autre processeur de shaders.
Pour corriger ce problème, les ingénieurs ont inventé des '''protocoles de cohérence des caches''' pour détecter les données périmées et les mettre à jour. Mais autant ces techniques sont faisables avec un nombre limité de cœurs, autant elles sont impraticables avec une centaine de cœurs. Mais heureusement, la cohérence des caches est un problème bien moins important sur les GPU que sur les CPU. En effet, le rendu 3D implique un parallélisme de donnée : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches.
En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même. Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisés. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur insére une instruction qui invalide le cache, avant l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo et donc qu'il s'agit d'une donnée correcte.
C'est cette technique qui est utilisée pour les caches de textures, pour supporter les techniques de ''render-to-texture''. Dans le chapitre précédent, on a vu que lorsque d'une texture est modifiée par le GPU, les caches de texture sont réinitialisés. Les opérations de ''render-to-texture'' étant assez rares, il vaut mieux faire cela que de rendre les caches de texture accesibles en écriture. Ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches du GPU sont gérés avec le même principe. Pour les caches généralistes, certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.
===La cohérence des caches entre CPU et GPU===
Un ordinateur contient un processeur et un GPU, chacun avec leurs caches séparés. Le processeur ou le GPU peuvent alors modifier la donnée chacun dans leur cache, ce qui fait que la donnée en RAM, celle dans le cache du GPU et celle dans le cache du CPU, seront totalement différentes. Il y aura donc une incohérence : une lecture à une adresse ne donnera pas le même résultat selon que la lecture vienne du CPU, du GPU ou d'un périphérique DMA. Alors qu'il idéalement, on voudrait que les caches soient cohérents, à savoir qu'ils contiennent tous la même copie d'une donnée, ou presque.
Les transferts de données se font du CPU vers le GPU, via les fameuses copies de données mentionnées plus haut, où le processeur écrit dans la mémoire vidéo et laisse la main au GPU. Les transferts dans l'autre sens sont beaucoup plus rares. Éviter les problèmes de cohérence des caches se fait alors en deux temps. Premièrement, le processeur doit écrire les données en RAM, sans passer par les caches. Deuxièmement, le GPU doit lire la mémoire RAM sans passer par ses caches à lui.
Le processeur est conçu pour contourner les caches lors de certaines écritures, à savoir que ces écritures se font en RAM directement, sans passer par le cache. C'est une fonctionnalité très importante pour communiquer avec les périphériques. Les processeurs modernes sont capables de marquer des blocs de mémoire entiers comme étant non-cacheables, à savoir que toute lecture/écriture dedans se fait sans passer par le cache. Il suffit au CPU de copier les données à envoyer au GPU dans une de ces zone non-cacheable.
Pour les GPU dédiés/soudés, cela a un lien avec la mémoire vidéo mappée en mémoire. Plus haut, nous avions dit que la mémoire vidéo est visible dans l'espace d'adressage du processeur, à savoir qu'un bloc de mémoire est détourné pour adresser non pas la RAM, mais la mémoire vidéo. Et bien ce bloc de mémoire entier est marqué comme étant non-cacheable. Du côté du GPU, les échanges entre RAM système et mémoire vidéo sont le fait du contrôleur DMA. La solution est alors toute trouvée : le contrôleur DMA accède directement en mémoire vidéo et en RAM système, sans passer par les caches du GPU. Il suffit d'invalider les caches du GPU après un transfert DMA et le tour est joué.
La situation est gérée différemment sur les GPU intégré, à condition qu'ils partagent leur cache avec le CPU. Dans ce cas, il n'y a pas de caches séparés pour le CPU et le GPU, mais un seul cache qui ne contient qu'une seule copie valide. Il n'y a pas de problème de cohérence des caches, CPU et GPU modifient la même donnée.
{{NavChapitre | book=Les cartes graphiques
| prev=La hiérarchie mémoire d'un GPU
| prevText=La hiérarchie mémoire d'un GPU
| next=Le processeur de commandes
| netxText=Le processeur de commandes
}}{{autocat}}
i1rc82gmrn5rbjvr9cjton7lqyi7uch
745298
745297
2025-06-24T14:18:37Z
Mewtow
31375
/* La cohérence des caches entre CPU et GPU */
745298
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.
==La mémoire vidéo dédiée==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
==La cohérence des caches sur un GPU==
Pour terminer ce chapitre, nous allons parler de la '''cohérence des caches'''. La cohérence des caches est un problème qui se manifeste quand des caches séparés contiennent la même donnée, une même copie d'une donnée en RAM. Avec un GPU, le problème à plusieurs niveaux.
===La cohérence des caches à l'intérieur du GPU===
Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs auquel on aurait rajouté des ROPs, les circuits de la rastérisation et les unités de textures. Il n'est donc pas étonnant que les problèmes rencontrés sur les processeurs multicœurs soient aussi présents sur les GPU, la cohérence des caches ne fait pas exception.
Un processeur multicœurs dispose de beaucoup de caches. Mais pour simplifier les explications, nous allons partir du principe que chaque cœur a son propre cache de données. Prenons deux processeurs/cœurs qui ont chacun une copie d'une donnée dans leur cache. Si un processeur modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches.
[[File:Cohérence des caches.png|centre|vignette|upright=2|Cohérence des caches]]
La réalité est cependant plus complexe, dans le sens où il n'y a souvent pas un cache par cœur, mais une hiérarchie de cache assez complexe, avec un cache L1 par cœur, un cache L2 partagé entre plusieurs cœurs, des caches partagés entre tous les cœurs, etc. Certains GPU partagent leur cache L1 d’instructions entre plusieurs cœurs, d'autres non. Mais le principe reste valide sur les GPU, tant qu'un cache n'est pas partagé entre tous les processeurs de shaders : un cache peut contenir une donnée invalide, à savoir qu'elle a été modifiée par dans le cache d'un autre processeur de shaders.
Pour corriger ce problème, les ingénieurs ont inventé des '''protocoles de cohérence des caches''' pour détecter les données périmées et les mettre à jour. Mais autant ces techniques sont faisables avec un nombre limité de cœurs, autant elles sont impraticables avec une centaine de cœurs. Mais heureusement, la cohérence des caches est un problème bien moins important sur les GPU que sur les CPU. En effet, le rendu 3D implique un parallélisme de donnée : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches.
En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même. Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisés. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur insére une instruction qui invalide le cache, avant l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo et donc qu'il s'agit d'une donnée correcte.
C'est cette technique qui est utilisée pour les caches de textures, pour supporter les techniques de ''render-to-texture''. Dans le chapitre précédent, on a vu que lorsque d'une texture est modifiée par le GPU, les caches de texture sont réinitialisés. Les opérations de ''render-to-texture'' étant assez rares, il vaut mieux faire cela que de rendre les caches de texture accesibles en écriture. Ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches du GPU sont gérés avec le même principe. Pour les caches généralistes, certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.
===La cohérence des caches entre CPU et GPU===
Un ordinateur contient un processeur et un GPU, chacun avec leurs caches séparés. Le processeur ou le GPU peuvent alors modifier la donnée chacun dans leur cache, ce qui fait que la donnée en RAM, celle dans le cache du GPU et celle dans le cache du CPU, seront totalement différentes. Il y aura donc une incohérence : une lecture à une adresse ne donnera pas le même résultat selon que la lecture vienne du CPU, du GPU ou d'un périphérique DMA. Alors qu'il idéalement, on voudrait que les caches soient cohérents, à savoir qu'ils contiennent tous la même copie d'une donnée, ou presque.
Les transferts de données se font du CPU vers le GPU, via les fameuses copies de données mentionnées plus haut, où le processeur écrit dans la mémoire vidéo et laisse la main au GPU. Les transferts dans l'autre sens sont beaucoup plus rares. Éviter les problèmes de cohérence des caches se fait alors en deux temps. Premièrement, le processeur doit écrire les données en RAM, sans passer par les caches. Deuxièmement, le GPU doit lire la mémoire RAM sans passer par ses caches à lui.
Le processeur est conçu pour contourner les caches lors de certaines écritures, à savoir que ces écritures se font en RAM directement, sans passer par le cache. C'est une fonctionnalité très importante pour communiquer avec les périphériques. Les processeurs modernes sont capables de marquer des blocs de mémoire entiers comme étant non-cacheables, à savoir que toute lecture/écriture dedans se fait sans passer par le cache. Il suffit au CPU de copier les données à envoyer au GPU dans une de ces zone non-cacheable.
Pour les GPU dédiés/soudés, cela a un lien avec la mémoire vidéo mappée en mémoire. Plus haut, nous avions dit que la mémoire vidéo est visible dans l'espace d'adressage du processeur, à savoir qu'un bloc de mémoire est détourné pour adresser non pas la RAM, mais la mémoire vidéo. Et bien ce bloc de mémoire entier est marqué comme étant non-cacheable. Du côté du GPU, les échanges entre RAM système et mémoire vidéo sont le fait du contrôleur DMA. La solution est alors toute trouvée : le contrôleur DMA accède directement en mémoire vidéo et en RAM système, sans passer par les caches du GPU. Il suffit d'invalider les caches du GPU après un transfert DMA et le tour est joué.
La situation est gérée différemment sur les GPU intégré, vu qu'ils utilisent la mémoire unifiée. Les données ne sont pas copiée de la RAM système à la mémoire vidéo, le GPU lit directement les données en RAM, il n'y a pas de contrôleur DMA qui contournerait les caches. Si le GPU est conçu pour, il n'y a pas besoin de marquer les données comme non-cacheables. Le cas le plus simple est celui où le CPU et le GPU partagent leur cache L3/L4. Dans ce cas, il n'y a qu'un seul cache L3/L4 qui ne contient qu'une seule copie valide, écrite par le CPU et lue par le GPU. Il faut juste garantir que la donnée soit lue par le GPU depuis le L3, mais c'est là une question d'inclusivité du cache, qui ne nous concerne pas ici. Si le CPU et le GPU ne partagent pas de cache, il suffit que le GPU puisse lire les caches du CPU. C'est la méthode utilisée sur l'APU Trinity d'AMD.
{{NavChapitre | book=Les cartes graphiques
| prev=La hiérarchie mémoire d'un GPU
| prevText=La hiérarchie mémoire d'un GPU
| next=Le processeur de commandes
| netxText=Le processeur de commandes
}}{{autocat}}
3htwcnkpeoyoyjmfftdfvyrarh4m1fs
745299
745298
2025-06-24T14:26:11Z
Mewtow
31375
/* La cohérence des caches entre CPU et GPU */
745299
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.
==La mémoire vidéo dédiée==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
==La cohérence des caches sur un GPU==
Pour terminer ce chapitre, nous allons parler de la '''cohérence des caches'''. La cohérence des caches est un problème qui se manifeste quand des caches séparés contiennent la même donnée, une même copie d'une donnée en RAM. Avec un GPU, le problème à plusieurs niveaux.
===La cohérence des caches à l'intérieur du GPU===
Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs auquel on aurait rajouté des ROPs, les circuits de la rastérisation et les unités de textures. Il n'est donc pas étonnant que les problèmes rencontrés sur les processeurs multicœurs soient aussi présents sur les GPU, la cohérence des caches ne fait pas exception.
Un processeur multicœurs dispose de beaucoup de caches. Mais pour simplifier les explications, nous allons partir du principe que chaque cœur a son propre cache de données. Prenons deux processeurs/cœurs qui ont chacun une copie d'une donnée dans leur cache. Si un processeur modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches.
[[File:Cohérence des caches.png|centre|vignette|upright=2|Cohérence des caches]]
La réalité est cependant plus complexe, dans le sens où il n'y a souvent pas un cache par cœur, mais une hiérarchie de cache assez complexe, avec un cache L1 par cœur, un cache L2 partagé entre plusieurs cœurs, des caches partagés entre tous les cœurs, etc. Certains GPU partagent leur cache L1 d’instructions entre plusieurs cœurs, d'autres non. Mais le principe reste valide sur les GPU, tant qu'un cache n'est pas partagé entre tous les processeurs de shaders : un cache peut contenir une donnée invalide, à savoir qu'elle a été modifiée par dans le cache d'un autre processeur de shaders.
Pour corriger ce problème, les ingénieurs ont inventé des '''protocoles de cohérence des caches''' pour détecter les données périmées et les mettre à jour. Mais autant ces techniques sont faisables avec un nombre limité de cœurs, autant elles sont impraticables avec une centaine de cœurs. Mais heureusement, la cohérence des caches est un problème bien moins important sur les GPU que sur les CPU. En effet, le rendu 3D implique un parallélisme de donnée : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches.
En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même. Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisés. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur insére une instruction qui invalide le cache, avant l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo et donc qu'il s'agit d'une donnée correcte.
C'est cette technique qui est utilisée pour les caches de textures, pour supporter les techniques de ''render-to-texture''. Dans le chapitre précédent, on a vu que lorsque d'une texture est modifiée par le GPU, les caches de texture sont réinitialisés. Les opérations de ''render-to-texture'' étant assez rares, il vaut mieux faire cela que de rendre les caches de texture accesibles en écriture. Ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches du GPU sont gérés avec le même principe. Pour les caches généralistes, certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.
===La cohérence des caches entre CPU et GPU===
Un ordinateur contient un processeur et un GPU, chacun avec leurs caches séparés. Le processeur ou le GPU peuvent alors modifier la donnée chacun dans leur cache, ce qui fait que la donnée en RAM, celle dans le cache du GPU et celle dans le cache du CPU, seront totalement différentes. Il y aura donc une incohérence : une lecture à une adresse ne donnera pas le même résultat selon que la lecture vienne du CPU, du GPU ou d'un périphérique DMA. Alors qu'il idéalement, on voudrait que les caches soient cohérents, à savoir qu'ils contiennent tous la même copie d'une donnée, ou presque.
: Le problème se manifeste aussi bien avec la mémoire unifiée qu'avec une mémoire vidéo séparée.
Les transferts de données se font du CPU vers le GPU, via les fameuses copies de données mentionnées plus haut, où le processeur écrit dans la mémoire vidéo et laisse la main au GPU. Les transferts dans l'autre sens sont beaucoup plus rares. Éviter les problèmes de cohérence des caches se fait alors en deux temps. Premièrement, le processeur doit écrire les données en RAM, sans passer par les caches. Deuxièmement, le GPU doit lire la mémoire RAM sans passer par ses caches à lui.
Le processeur est conçu pour contourner les caches lors de certaines écritures, à savoir que ces écritures se font en RAM directement, sans passer par le cache. C'est une fonctionnalité très importante pour communiquer avec les périphériques. Les processeurs modernes sont capables de marquer des blocs de mémoire entiers comme étant non-cacheables, à savoir que toute lecture/écriture dedans se fait sans passer par le cache. Il suffit au CPU de copier les données à envoyer au GPU dans une de ces zone non-cacheable.
Pour les GPU dédiés/soudés, cela a un lien avec la mémoire vidéo mappée en mémoire. Plus haut, nous avions dit que la mémoire vidéo est visible dans l'espace d'adressage du processeur, à savoir qu'un bloc de mémoire est détourné pour adresser non pas la RAM, mais la mémoire vidéo. Et bien ce bloc de mémoire entier est marqué comme étant non-cacheable. Du côté du GPU, les échanges entre RAM système et mémoire vidéo sont le fait du contrôleur DMA. La solution est alors toute trouvée : le contrôleur DMA accède directement en mémoire vidéo et en RAM système, sans passer par les caches du GPU. Il suffit d'invalider les caches du GPU après un transfert DMA et le tour est joué.
La situation est gérée différemment sur les GPU intégré, vu qu'ils utilisent la mémoire unifiée. Les données ne sont pas copiée de la RAM système à la mémoire vidéo, le GPU lit directement les données en RAM, il n'y a pas de contrôleur DMA qui contournerait les caches. Si le GPU est conçu pour, il n'y a pas besoin de marquer les données comme non-cacheables. Le cas le plus simple est celui où le CPU et le GPU partagent leur cache L3/L4. Dans ce cas, il n'y a qu'un seul cache L3/L4 qui ne contient qu'une seule copie valide, écrite par le CPU et lue par le GPU. Il faut juste garantir que la donnée soit lue par le GPU depuis le L3, mais c'est là une question d'inclusivité du cache, qui ne nous concerne pas ici. Si le CPU et le GPU ne partagent pas de cache, il suffit que le GPU puisse lire les caches du CPU. C'est la méthode utilisée sur l'APU Trinity d'AMD.
{{NavChapitre | book=Les cartes graphiques
| prev=La hiérarchie mémoire d'un GPU
| prevText=La hiérarchie mémoire d'un GPU
| next=Le processeur de commandes
| netxText=Le processeur de commandes
}}{{autocat}}
4birdv1a48u3vd6og9f5ehyaq0f6pbr
745300
745299
2025-06-24T14:28:39Z
Mewtow
31375
/* La cohérence des caches entre CPU et GPU */
745300
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.
==La mémoire vidéo dédiée==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
==La cohérence des caches sur un GPU==
Pour terminer ce chapitre, nous allons parler de la '''cohérence des caches'''. La cohérence des caches est un problème qui se manifeste quand des caches séparés contiennent la même donnée, une même copie d'une donnée en RAM. Avec un GPU, le problème à plusieurs niveaux.
===La cohérence des caches à l'intérieur du GPU===
Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs auquel on aurait rajouté des ROPs, les circuits de la rastérisation et les unités de textures. Il n'est donc pas étonnant que les problèmes rencontrés sur les processeurs multicœurs soient aussi présents sur les GPU, la cohérence des caches ne fait pas exception.
Un processeur multicœurs dispose de beaucoup de caches. Mais pour simplifier les explications, nous allons partir du principe que chaque cœur a son propre cache de données. Prenons deux processeurs/cœurs qui ont chacun une copie d'une donnée dans leur cache. Si un processeur modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches.
[[File:Cohérence des caches.png|centre|vignette|upright=2|Cohérence des caches]]
La réalité est cependant plus complexe, dans le sens où il n'y a souvent pas un cache par cœur, mais une hiérarchie de cache assez complexe, avec un cache L1 par cœur, un cache L2 partagé entre plusieurs cœurs, des caches partagés entre tous les cœurs, etc. Certains GPU partagent leur cache L1 d’instructions entre plusieurs cœurs, d'autres non. Mais le principe reste valide sur les GPU, tant qu'un cache n'est pas partagé entre tous les processeurs de shaders : un cache peut contenir une donnée invalide, à savoir qu'elle a été modifiée par dans le cache d'un autre processeur de shaders.
Pour corriger ce problème, les ingénieurs ont inventé des '''protocoles de cohérence des caches''' pour détecter les données périmées et les mettre à jour. Mais autant ces techniques sont faisables avec un nombre limité de cœurs, autant elles sont impraticables avec une centaine de cœurs. Mais heureusement, la cohérence des caches est un problème bien moins important sur les GPU que sur les CPU. En effet, le rendu 3D implique un parallélisme de donnée : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches.
En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même. Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisés. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur insére une instruction qui invalide le cache, avant l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo et donc qu'il s'agit d'une donnée correcte.
C'est cette technique qui est utilisée pour les caches de textures, pour supporter les techniques de ''render-to-texture''. Dans le chapitre précédent, on a vu que lorsque d'une texture est modifiée par le GPU, les caches de texture sont réinitialisés. Les opérations de ''render-to-texture'' étant assez rares, il vaut mieux faire cela que de rendre les caches de texture accesibles en écriture. Ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches du GPU sont gérés avec le même principe. Pour les caches généralistes, certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.
===La cohérence des caches entre CPU et GPU===
Un ordinateur contient un processeur et un GPU, chacun avec leurs caches séparés. Le processeur ou le GPU peuvent alors modifier la donnée chacun dans leur cache, ce qui fait que la donnée en RAM, celle dans le cache du GPU et celle dans le cache du CPU, seront totalement différentes. Il y aura donc une incohérence : une lecture à une adresse ne donnera pas le même résultat selon que la lecture vienne du CPU, du GPU ou d'un périphérique DMA. Alors qu'il idéalement, on voudrait que les caches soient cohérents, à savoir qu'ils contiennent tous la même copie d'une donnée, ou presque.
: Le problème se manifeste aussi bien avec la mémoire unifiée qu'avec une mémoire vidéo séparée.
[[File:Cohérence des caches entre CPU et GPU avec mémoire unifiée.png|centre|vignette|upright=2|Cohérence des caches entre CPU et GPU avec mémoire unifiée]]
Les transferts de données se font du CPU vers le GPU, via les fameuses copies de données mentionnées plus haut, où le processeur écrit dans la mémoire vidéo et laisse la main au GPU. Les transferts dans l'autre sens sont beaucoup plus rares. Éviter les problèmes de cohérence des caches se fait alors en deux temps. Premièrement, le processeur doit écrire les données en RAM, sans passer par les caches. Deuxièmement, le GPU doit lire la mémoire RAM sans passer par ses caches à lui.
Le processeur est conçu pour contourner les caches lors de certaines écritures, à savoir que ces écritures se font en RAM directement, sans passer par le cache. C'est une fonctionnalité très importante pour communiquer avec les périphériques. Les processeurs modernes sont capables de marquer des blocs de mémoire entiers comme étant non-cacheables, à savoir que toute lecture/écriture dedans se fait sans passer par le cache. Il suffit au CPU de copier les données à envoyer au GPU dans une de ces zone non-cacheable.
Pour les GPU dédiés/soudés, cela a un lien avec la mémoire vidéo mappée en mémoire. Plus haut, nous avions dit que la mémoire vidéo est visible dans l'espace d'adressage du processeur, à savoir qu'un bloc de mémoire est détourné pour adresser non pas la RAM, mais la mémoire vidéo. Et bien ce bloc de mémoire entier est marqué comme étant non-cacheable. Du côté du GPU, les échanges entre RAM système et mémoire vidéo sont le fait du contrôleur DMA. La solution est alors toute trouvée : le contrôleur DMA accède directement en mémoire vidéo et en RAM système, sans passer par les caches du GPU. Il suffit d'invalider les caches du GPU après un transfert DMA et le tour est joué.
La situation est gérée différemment sur les GPU intégré, vu qu'ils utilisent la mémoire unifiée. Les données ne sont pas copiée de la RAM système à la mémoire vidéo, le GPU lit directement les données en RAM, il n'y a pas de contrôleur DMA qui contournerait les caches. Si le GPU est conçu pour, il n'y a pas besoin de marquer les données comme non-cacheables. Le cas le plus simple est celui où le CPU et le GPU partagent leur cache L3/L4. Dans ce cas, il n'y a qu'un seul cache L3/L4 qui ne contient qu'une seule copie valide, écrite par le CPU et lue par le GPU. Il faut juste garantir que la donnée soit lue par le GPU depuis le L3, mais c'est là une question d'inclusivité du cache, qui ne nous concerne pas ici. Si le CPU et le GPU ne partagent pas de cache, il suffit que le GPU puisse lire les caches du CPU. C'est la méthode utilisée sur l'APU Trinity d'AMD.
{{NavChapitre | book=Les cartes graphiques
| prev=La hiérarchie mémoire d'un GPU
| prevText=La hiérarchie mémoire d'un GPU
| next=Le processeur de commandes
| netxText=Le processeur de commandes
}}{{autocat}}
6bpmn9hkybm3wy02a31w80papl00mkt
745301
745300
2025-06-24T14:32:53Z
Mewtow
31375
/* La cohérence des caches entre CPU et GPU */
745301
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.
==La mémoire vidéo dédiée==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
==La cohérence des caches sur un GPU==
Pour terminer ce chapitre, nous allons parler de la '''cohérence des caches'''. La cohérence des caches est un problème qui se manifeste quand des caches séparés contiennent la même donnée, une même copie d'une donnée en RAM. Avec un GPU, le problème à plusieurs niveaux.
===La cohérence des caches à l'intérieur du GPU===
Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs auquel on aurait rajouté des ROPs, les circuits de la rastérisation et les unités de textures. Il n'est donc pas étonnant que les problèmes rencontrés sur les processeurs multicœurs soient aussi présents sur les GPU, la cohérence des caches ne fait pas exception.
Un processeur multicœurs dispose de beaucoup de caches. Mais pour simplifier les explications, nous allons partir du principe que chaque cœur a son propre cache de données. Prenons deux processeurs/cœurs qui ont chacun une copie d'une donnée dans leur cache. Si un processeur modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches.
[[File:Cohérence des caches.png|centre|vignette|upright=2|Cohérence des caches]]
La réalité est cependant plus complexe, dans le sens où il n'y a souvent pas un cache par cœur, mais une hiérarchie de cache assez complexe, avec un cache L1 par cœur, un cache L2 partagé entre plusieurs cœurs, des caches partagés entre tous les cœurs, etc. Certains GPU partagent leur cache L1 d’instructions entre plusieurs cœurs, d'autres non. Mais le principe reste valide sur les GPU, tant qu'un cache n'est pas partagé entre tous les processeurs de shaders : un cache peut contenir une donnée invalide, à savoir qu'elle a été modifiée par dans le cache d'un autre processeur de shaders.
Pour corriger ce problème, les ingénieurs ont inventé des '''protocoles de cohérence des caches''' pour détecter les données périmées et les mettre à jour. Mais autant ces techniques sont faisables avec un nombre limité de cœurs, autant elles sont impraticables avec une centaine de cœurs. Mais heureusement, la cohérence des caches est un problème bien moins important sur les GPU que sur les CPU. En effet, le rendu 3D implique un parallélisme de donnée : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches.
En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même. Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisés. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur insére une instruction qui invalide le cache, avant l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo et donc qu'il s'agit d'une donnée correcte.
C'est cette technique qui est utilisée pour les caches de textures, pour supporter les techniques de ''render-to-texture''. Dans le chapitre précédent, on a vu que lorsque d'une texture est modifiée par le GPU, les caches de texture sont réinitialisés. Les opérations de ''render-to-texture'' étant assez rares, il vaut mieux faire cela que de rendre les caches de texture accesibles en écriture. Ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches du GPU sont gérés avec le même principe. Pour les caches généralistes, certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.
===La cohérence des caches entre CPU et GPU===
Un ordinateur contient un processeur et un GPU, chacun avec leurs caches séparés. Le processeur ou le GPU peuvent alors modifier la donnée chacun dans leur cache, ce qui fait que la donnée en RAM, celle dans le cache du GPU et celle dans le cache du CPU, seront totalement différentes. Il y aura donc une incohérence : une lecture à une adresse ne donnera pas le même résultat selon que la lecture vienne du CPU, du GPU ou d'un périphérique DMA. Alors qu'il idéalement, on voudrait que les caches soient cohérents, à savoir qu'ils contiennent tous la même copie d'une donnée, ou presque.
[[File:Cohérence des caches entre CPU et GPU avec mémoire unifiée.png|centre|vignette|upright=2|Cohérence des caches entre CPU et GPU avec mémoire unifiée]]
Le problème se manifeste aussi bien avec la mémoire unifiée qu'avec une mémoire vidéo séparée. Le schéma précédent illustre ce qui se passe avec la mémoire unifiée. Avec une mémoire vidéo séparée, le problème reste cependant le même, avec cependant le que les données sont copiées de la RAM système vers la mémoire vidéo. Le problème est alors que le processeur écrit dans son cache, mais que les écritures ne sont pas toutes répercutées en RAM. Si les données en RAM système ne sont pas les bonnes, la copie donnera un mauvais résultat. Un point important est donc de garantir que la RAM système ait les bonnes données avant de les copier en mémoire vidéo.
Pour cela, le processeur est conçu pour contourner les caches lors de certaines écritures, à savoir que ces écritures se font en RAM directement, sans passer par le cache. C'est une fonctionnalité très importante pour communiquer avec les périphériques. Les processeurs modernes sont capables de marquer des blocs de mémoire entiers comme étant non-cacheables, à savoir que toute lecture/écriture dedans se fait sans passer par le cache. Il suffit au CPU de copier les données à envoyer au GPU dans une de ces zone non-cacheable.
Pour les GPU dédiés/soudés, cela a un lien avec la mémoire vidéo mappée en mémoire. Plus haut, nous avions dit que la mémoire vidéo est visible dans l'espace d'adressage du processeur, à savoir qu'un bloc de mémoire est détourné pour adresser non pas la RAM, mais la mémoire vidéo. Et bien ce bloc de mémoire entier est marqué comme étant non-cacheable. Du côté du GPU, les échanges entre RAM système et mémoire vidéo sont le fait du contrôleur DMA. La solution est alors toute trouvée : le contrôleur DMA accède directement en mémoire vidéo et en RAM système, sans passer par les caches du GPU. Il suffit d'invalider les caches du GPU après un transfert DMA, pour que le GPU lise les données directement en RAM et le tour est joué.
La situation est gérée différemment sur les GPU intégré, vu qu'ils utilisent la mémoire unifiée. Les données ne sont pas copiée de la RAM système à la mémoire vidéo, le GPU lit directement les données en RAM, il n'y a pas de contrôleur DMA qui contournerait les caches. Si le GPU est conçu pour, il n'y a pas besoin de marquer les données comme non-cacheables. Le cas le plus simple est celui où le CPU et le GPU partagent leur cache L3/L4. Dans ce cas, il n'y a qu'un seul cache L3/L4 qui ne contient qu'une seule copie valide, écrite par le CPU et lue par le GPU. Il faut juste garantir que la donnée soit lue par le GPU depuis le L3, mais c'est là une question d'inclusivité du cache, qui ne nous concerne pas ici. Si le CPU et le GPU ne partagent pas de cache, il suffit que le GPU puisse lire les caches du CPU. C'est la méthode utilisée sur l'APU Trinity d'AMD.
{{NavChapitre | book=Les cartes graphiques
| prev=La hiérarchie mémoire d'un GPU
| prevText=La hiérarchie mémoire d'un GPU
| next=Le processeur de commandes
| netxText=Le processeur de commandes
}}{{autocat}}
6qt7xgaitbpvd6rhv7nzhms6jotnh2r
745302
745301
2025-06-24T14:41:53Z
Mewtow
31375
/* La cohérence des caches sur un GPU */
745302
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.
==La mémoire vidéo dédiée==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
==La cohérence des caches sur un GPU==
Pour terminer ce chapitre, nous allons parler de la '''cohérence des caches'''. La cohérence des caches est un problème qui se manifeste quand des caches séparés contiennent la même donnée, une même copie d'une donnée en RAM. Avec un GPU, le problème à plusieurs niveaux.
===La cohérence des caches et le DMA des GPU dédiés===
Un GPU dédié échange des données avec la RAM système via des transferts DMA, qui copient des données de la RAM système vers la mémoire vidéo. Et les transferts DMA posent des problèmes avec les caches intégrés au GPU, dans une situation bien précise. Le problème est le suivant : imaginez qu'un transfert DMA remplace des données par d'autres, à savoir qu'il écrase des données inutiles pour les remplacer par des données utiles. C'est très fréquent, les pilotes graphiques libèrent souvent de la mémoire vidéo pour la réallouer immédiatement après, afin de ne pas gaspiller de VRAM.
Sans intervention particulière, les données inutiles sont encore dans le cache ! Le remplacement des données aura été fait en mémoire vidéo, pas dans les caches du GPU. Le cache associe toujours l'ancienne donnée écrasée à l'adresse mémoire où elles étaient. Et tout accès ultérieur à ces adresses mémoire renverra la donnée écrasée.
[[File:Cache incoherence write.svg|centre|vignette|upright=2|Cohérence des caches avec DMA.]]
Pour éviter cela, le GPU invalide ses caches en cas de transfert DMA. Par invalider, on veut dire que le cache est réinitialisé, mis à zéro, il est rendu vierge de toute donnée. Ainsi, tout accès mémoire ultérieur se fera en mémoire RAM sans passer par le cache. Les données lues depuis la RAM seront ensuite copiées dans le cache, mais ce seront les données valides écrites après le transfert DMA. Le contenu du cache est alors reconstitué. L'invalidation est automatique sur les anciens GPU, elle est réalisée par le processeur de commande. Sur les GPU modernes, elle est réalisée par le programmeur, comme on va le voir dans la section immédiatement suivante.
===La cohérence des caches entre processeurs de shaders===
Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs auquel on aurait rajouté des ROPs, les circuits de la rastérisation et les unités de textures. Il n'est donc pas étonnant que les problèmes rencontrés sur les processeurs multicœurs soient aussi présents sur les GPU, la cohérence des caches ne fait pas exception.
Un processeur multicœurs dispose de beaucoup de caches. Mais pour simplifier les explications, nous allons partir du principe que chaque cœur a son propre cache de données. Prenons deux processeurs/cœurs qui ont chacun une copie d'une donnée dans leur cache. Si un processeur modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches.
[[File:Cohérence des caches.png|centre|vignette|upright=2|Cohérence des caches]]
La réalité est cependant plus complexe, dans le sens où il n'y a souvent pas un cache par cœur, mais une hiérarchie de cache assez complexe, avec un cache L1 par cœur, un cache L2 partagé entre plusieurs cœurs, des caches partagés entre tous les cœurs, etc. Certains GPU partagent leur cache L1 d’instructions entre plusieurs cœurs, d'autres non. Mais le principe reste valide sur les GPU, tant qu'un cache n'est pas partagé entre tous les processeurs de shaders : un cache peut contenir une donnée invalide, à savoir qu'elle a été modifiée par dans le cache d'un autre processeur de shaders.
Pour corriger ce problème, les ingénieurs ont inventé des '''protocoles de cohérence des caches''' pour détecter les données périmées et les mettre à jour. Mais autant ces techniques sont faisables avec un nombre limité de cœurs, autant elles sont impraticables avec une centaine de cœurs. Mais heureusement, la cohérence des caches est un problème bien moins important sur les GPU que sur les CPU. En effet, le rendu 3D implique un parallélisme de donnée : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches.
En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même. Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisés. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur insére une instruction qui invalide le cache, avant l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo et donc qu'il s'agit d'une donnée correcte.
C'est cette technique qui est utilisée pour les caches de textures, pour supporter les techniques de ''render-to-texture''. Dans le chapitre précédent, on a vu que lorsque d'une texture est modifiée par le GPU, les caches de texture sont réinitialisés. Les opérations de ''render-to-texture'' étant assez rares, il vaut mieux faire cela que de rendre les caches de texture accesibles en écriture. Ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches du GPU sont gérés avec le même principe. Pour les caches généralistes, certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.
===La cohérence des caches entre CPU et GPU===
Un ordinateur contient un processeur et un GPU, chacun avec leurs caches séparés. Le processeur ou le GPU peuvent alors modifier la donnée chacun dans leur cache, ce qui fait que la donnée en RAM, celle dans le cache du GPU et celle dans le cache du CPU, seront totalement différentes. Il y aura donc une incohérence : une lecture à une adresse ne donnera pas le même résultat selon que la lecture vienne du CPU, du GPU ou d'un périphérique DMA. Alors qu'il idéalement, on voudrait que les caches soient cohérents, à savoir qu'ils contiennent tous la même copie d'une donnée, ou presque.
[[File:Cohérence des caches entre CPU et GPU avec mémoire unifiée.png|centre|vignette|upright=2|Cohérence des caches entre CPU et GPU avec mémoire unifiée]]
Le problème se manifeste aussi bien avec la mémoire unifiée qu'avec une mémoire vidéo séparée. Le schéma précédent illustre ce qui se passe avec la mémoire unifiée. Avec une mémoire vidéo séparée, le problème reste cependant le même, avec cependant le que les données sont copiées de la RAM système vers la mémoire vidéo. Le problème est alors que le processeur écrit dans son cache, mais que les écritures ne sont pas toutes répercutées en RAM. Si les données en RAM système ne sont pas les bonnes, la copie donnera un mauvais résultat. Un point important est donc de garantir que la RAM système ait les bonnes données avant de les copier en mémoire vidéo.
Pour cela, le processeur est conçu pour contourner les caches lors de certaines écritures, à savoir que ces écritures se font en RAM directement, sans passer par le cache. C'est une fonctionnalité très importante pour communiquer avec les périphériques. Les processeurs modernes sont capables de marquer des blocs de mémoire entiers comme étant non-cacheables, à savoir que toute lecture/écriture dedans se fait sans passer par le cache. Il suffit au CPU de copier les données à envoyer au GPU dans une de ces zone non-cacheable.
Pour les GPU dédiés/soudés, cela a un lien avec la mémoire vidéo mappée en mémoire. Plus haut, nous avions dit que la mémoire vidéo est visible dans l'espace d'adressage du processeur, à savoir qu'un bloc de mémoire est détourné pour adresser non pas la RAM, mais la mémoire vidéo. Et bien ce bloc de mémoire entier est marqué comme étant non-cacheable. Du côté du GPU, les échanges entre RAM système et mémoire vidéo sont le fait du contrôleur DMA. La solution est alors toute trouvée : le contrôleur DMA accède directement en mémoire vidéo et en RAM système, sans passer par les caches du GPU. Il suffit d'invalider les caches du GPU après un transfert DMA, pour que le GPU lise les données directement en RAM et le tour est joué.
La situation est gérée différemment sur les GPU intégré, vu qu'ils utilisent la mémoire unifiée. Les données ne sont pas copiée de la RAM système à la mémoire vidéo, le GPU lit directement les données en RAM, il n'y a pas de contrôleur DMA qui contournerait les caches. Si le GPU est conçu pour, il n'y a pas besoin de marquer les données comme non-cacheables. Le cas le plus simple est celui où le CPU et le GPU partagent leur cache L3/L4. Dans ce cas, il n'y a qu'un seul cache L3/L4 qui ne contient qu'une seule copie valide, écrite par le CPU et lue par le GPU. Il faut juste garantir que la donnée soit lue par le GPU depuis le L3, mais c'est là une question d'inclusivité du cache, qui ne nous concerne pas ici. Si le CPU et le GPU ne partagent pas de cache, il suffit que le GPU puisse lire les caches du CPU. C'est la méthode utilisée sur l'APU Trinity d'AMD.
{{NavChapitre | book=Les cartes graphiques
| prev=La hiérarchie mémoire d'un GPU
| prevText=La hiérarchie mémoire d'un GPU
| next=Le processeur de commandes
| netxText=Le processeur de commandes
}}{{autocat}}
doh5tmwrdv87ddx9ypxpi05eyafa9t5
745303
745302
2025-06-24T14:44:52Z
Mewtow
31375
/* La cohérence des caches et le DMA des GPU dédiés */
745303
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.
==La mémoire vidéo dédiée==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
==La cohérence des caches sur un GPU==
Pour terminer ce chapitre, nous allons parler de la '''cohérence des caches'''. La cohérence des caches est un problème qui se manifeste quand des caches séparés contiennent la même donnée, une même copie d'une donnée en RAM. Avec un GPU, le problème à plusieurs niveaux.
===La cohérence des caches et le DMA des GPU dédiés===
Un GPU dédié échange des données avec la RAM système via des transferts DMA, qui copient des données de la RAM système vers la mémoire vidéo. Et les transferts DMA posent des problèmes avec les caches intégrés au GPU, dans une situation bien précise. Le problème est le suivant : imaginez qu'un transfert DMA remplace des données par d'autres, à savoir qu'il écrase des données inutiles pour les remplacer par des données utiles. C'est très fréquent, les pilotes graphiques libèrent souvent de la mémoire vidéo pour la réallouer immédiatement après, afin de ne pas gaspiller de VRAM.
Sans intervention particulière, les données inutiles sont encore dans le cache ! Le remplacement des données aura été fait en mémoire vidéo, pas dans les caches du GPU. Le cache associe toujours l'ancienne donnée écrasée à l'adresse mémoire où elles étaient. Et tout accès ultérieur à ces adresses mémoire renverra la donnée écrasée.
[[File:Cache incoherence write.svg|centre|vignette|upright=2|Cohérence des caches avec DMA.]]
Pour éviter cela, le GPU invalide ses caches en cas de transfert DMA. Par invalider, on veut dire que le cache est réinitialisé, mis à zéro, il est rendu vierge de toute donnée. Ainsi, tout accès mémoire ultérieur se fera en mémoire RAM sans passer par le cache. Les données lues depuis la RAM seront ensuite copiées dans le cache, mais ce seront les données valides écrites après le transfert DMA. Le contenu du cache est alors reconstitué. L'invalidation est automatique sur les anciens GPU, elle est réalisée par le processeur de commande. Sur les GPU modernes, elle est réalisée par le programmeur, comme on va le voir dans la section immédiatement suivante.
La même chose a lieu, mais avec la mémoire unifiée. La mémoire unifiée n'implique pas de transfert DMA entre CPU et GPU, vu qu'il n'y a qu'une seule RAM unifiée. Par contre, lorsque le processeur veut envoyer des données au GPU, par exemple des textures ou un tampon de sommets, il doit forcer le GPU à invalider ses caches.
===La cohérence des caches entre processeurs de shaders===
Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs auquel on aurait rajouté des ROPs, les circuits de la rastérisation et les unités de textures. Il n'est donc pas étonnant que les problèmes rencontrés sur les processeurs multicœurs soient aussi présents sur les GPU, la cohérence des caches ne fait pas exception.
Un processeur multicœurs dispose de beaucoup de caches. Mais pour simplifier les explications, nous allons partir du principe que chaque cœur a son propre cache de données. Prenons deux processeurs/cœurs qui ont chacun une copie d'une donnée dans leur cache. Si un processeur modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches.
[[File:Cohérence des caches.png|centre|vignette|upright=2|Cohérence des caches]]
La réalité est cependant plus complexe, dans le sens où il n'y a souvent pas un cache par cœur, mais une hiérarchie de cache assez complexe, avec un cache L1 par cœur, un cache L2 partagé entre plusieurs cœurs, des caches partagés entre tous les cœurs, etc. Certains GPU partagent leur cache L1 d’instructions entre plusieurs cœurs, d'autres non. Mais le principe reste valide sur les GPU, tant qu'un cache n'est pas partagé entre tous les processeurs de shaders : un cache peut contenir une donnée invalide, à savoir qu'elle a été modifiée par dans le cache d'un autre processeur de shaders.
Pour corriger ce problème, les ingénieurs ont inventé des '''protocoles de cohérence des caches''' pour détecter les données périmées et les mettre à jour. Mais autant ces techniques sont faisables avec un nombre limité de cœurs, autant elles sont impraticables avec une centaine de cœurs. Mais heureusement, la cohérence des caches est un problème bien moins important sur les GPU que sur les CPU. En effet, le rendu 3D implique un parallélisme de donnée : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches.
En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même. Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisés. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur insére une instruction qui invalide le cache, avant l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo et donc qu'il s'agit d'une donnée correcte.
C'est cette technique qui est utilisée pour les caches de textures, pour supporter les techniques de ''render-to-texture''. Dans le chapitre précédent, on a vu que lorsque d'une texture est modifiée par le GPU, les caches de texture sont réinitialisés. Les opérations de ''render-to-texture'' étant assez rares, il vaut mieux faire cela que de rendre les caches de texture accesibles en écriture. Ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches du GPU sont gérés avec le même principe. Pour les caches généralistes, certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.
===La cohérence des caches entre CPU et GPU===
Un ordinateur contient un processeur et un GPU, chacun avec leurs caches séparés. Le processeur ou le GPU peuvent alors modifier la donnée chacun dans leur cache, ce qui fait que la donnée en RAM, celle dans le cache du GPU et celle dans le cache du CPU, seront totalement différentes. Il y aura donc une incohérence : une lecture à une adresse ne donnera pas le même résultat selon que la lecture vienne du CPU, du GPU ou d'un périphérique DMA. Alors qu'il idéalement, on voudrait que les caches soient cohérents, à savoir qu'ils contiennent tous la même copie d'une donnée, ou presque.
[[File:Cohérence des caches entre CPU et GPU avec mémoire unifiée.png|centre|vignette|upright=2|Cohérence des caches entre CPU et GPU avec mémoire unifiée]]
Le problème se manifeste aussi bien avec la mémoire unifiée qu'avec une mémoire vidéo séparée. Le schéma précédent illustre ce qui se passe avec la mémoire unifiée. Avec une mémoire vidéo séparée, le problème reste cependant le même, avec cependant le que les données sont copiées de la RAM système vers la mémoire vidéo. Le problème est alors que le processeur écrit dans son cache, mais que les écritures ne sont pas toutes répercutées en RAM. Si les données en RAM système ne sont pas les bonnes, la copie donnera un mauvais résultat. Un point important est donc de garantir que la RAM système ait les bonnes données avant de les copier en mémoire vidéo.
Pour cela, le processeur est conçu pour contourner les caches lors de certaines écritures, à savoir que ces écritures se font en RAM directement, sans passer par le cache. C'est une fonctionnalité très importante pour communiquer avec les périphériques. Les processeurs modernes sont capables de marquer des blocs de mémoire entiers comme étant non-cacheables, à savoir que toute lecture/écriture dedans se fait sans passer par le cache. Il suffit au CPU de copier les données à envoyer au GPU dans une de ces zone non-cacheable.
Pour les GPU dédiés/soudés, cela a un lien avec la mémoire vidéo mappée en mémoire. Plus haut, nous avions dit que la mémoire vidéo est visible dans l'espace d'adressage du processeur, à savoir qu'un bloc de mémoire est détourné pour adresser non pas la RAM, mais la mémoire vidéo. Et bien ce bloc de mémoire entier est marqué comme étant non-cacheable. Du côté du GPU, les échanges entre RAM système et mémoire vidéo sont le fait du contrôleur DMA. La solution est alors toute trouvée : le contrôleur DMA accède directement en mémoire vidéo et en RAM système, sans passer par les caches du GPU. Il suffit d'invalider les caches du GPU après un transfert DMA, pour que le GPU lise les données directement en RAM et le tour est joué.
La situation est gérée différemment sur les GPU intégré, vu qu'ils utilisent la mémoire unifiée. Les données ne sont pas copiée de la RAM système à la mémoire vidéo, le GPU lit directement les données en RAM, il n'y a pas de contrôleur DMA qui contournerait les caches. Si le GPU est conçu pour, il n'y a pas besoin de marquer les données comme non-cacheables. Le cas le plus simple est celui où le CPU et le GPU partagent leur cache L3/L4. Dans ce cas, il n'y a qu'un seul cache L3/L4 qui ne contient qu'une seule copie valide, écrite par le CPU et lue par le GPU. Il faut juste garantir que la donnée soit lue par le GPU depuis le L3, mais c'est là une question d'inclusivité du cache, qui ne nous concerne pas ici. Si le CPU et le GPU ne partagent pas de cache, il suffit que le GPU puisse lire les caches du CPU. C'est la méthode utilisée sur l'APU Trinity d'AMD.
{{NavChapitre | book=Les cartes graphiques
| prev=La hiérarchie mémoire d'un GPU
| prevText=La hiérarchie mémoire d'un GPU
| next=Le processeur de commandes
| netxText=Le processeur de commandes
}}{{autocat}}
5rkn6g4utmsrf5e8otu43vydfvvq6d0
745304
745303
2025-06-24T14:47:04Z
Mewtow
31375
/* La cohérence des caches sur un GPU */
745304
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.
==La mémoire vidéo dédiée==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
==La cohérence des caches sur un GPU==
Pour terminer ce chapitre, nous allons parler de la '''cohérence des caches'''. La cohérence des caches est un problème qui se manifeste quand des caches séparés contiennent la même donnée, une même copie d'une donnée en RAM. Avec un GPU, le problème à plusieurs niveaux.
===La cohérence des caches et le DMA des GPU dédiés===
Un GPU dédié échange des données avec la RAM système via des transferts DMA, qui copient des données de la RAM système vers la mémoire vidéo. Et les transferts DMA posent des problèmes avec les caches intégrés au GPU, dans une situation bien précise. Le problème est le suivant : imaginez qu'un transfert DMA remplace des données par d'autres, à savoir qu'il écrase des données inutiles pour les remplacer par des données utiles. C'est très fréquent, les pilotes graphiques libèrent souvent de la mémoire vidéo pour la réallouer immédiatement après, afin de ne pas gaspiller de VRAM.
Sans intervention particulière, les données inutiles sont encore dans le cache ! Le remplacement des données aura été fait en mémoire vidéo, pas dans les caches du GPU. Le cache associe toujours l'ancienne donnée écrasée à l'adresse mémoire où elles étaient. Et tout accès ultérieur à ces adresses mémoire renverra la donnée écrasée.
[[File:Cache incoherence write.svg|centre|vignette|upright=2|Cohérence des caches avec DMA.]]
Pour éviter cela, le GPU invalide ses caches en cas de transfert DMA. Par invalider, on veut dire que le cache est réinitialisé, mis à zéro, il est rendu vierge de toute donnée. Ainsi, tout accès mémoire ultérieur se fera en mémoire RAM sans passer par le cache. Les données lues depuis la RAM seront ensuite copiées dans le cache, mais ce seront les données valides écrites après le transfert DMA. Le contenu du cache est alors reconstitué. L'invalidation est automatique sur les anciens GPU, elle est réalisée par le processeur de commande. Sur les GPU modernes, elle est réalisée par le programmeur, comme on va le voir dans la section immédiatement suivante.
La même chose a lieu, mais avec la mémoire unifiée. La mémoire unifiée n'implique pas de transfert DMA entre CPU et GPU, vu qu'il n'y a qu'une seule RAM unifiée. Par contre, lorsque le processeur veut envoyer des données au GPU, par exemple des textures ou un tampon de sommets, il doit forcer le GPU à invalider ses caches.
===La cohérence des caches entre CPU et GPU===
Un ordinateur contient un processeur et un GPU, chacun avec leurs caches séparés. Le processeur ou le GPU peuvent alors modifier la donnée chacun dans leur cache, ce qui fait que la donnée en RAM, celle dans le cache du GPU et celle dans le cache du CPU, seront totalement différentes. Il y aura donc une incohérence : une lecture à une adresse ne donnera pas le même résultat selon que la lecture vienne du CPU, du GPU ou d'un périphérique DMA. Alors qu'il idéalement, on voudrait que les caches soient cohérents, à savoir qu'ils contiennent tous la même copie d'une donnée, ou presque.
[[File:Cohérence des caches entre CPU et GPU avec mémoire unifiée.png|centre|vignette|upright=2|Cohérence des caches entre CPU et GPU avec mémoire unifiée]]
Le problème se manifeste aussi bien avec la mémoire unifiée qu'avec une mémoire vidéo séparée. Le schéma précédent illustre ce qui se passe avec la mémoire unifiée. Avec une mémoire vidéo séparée, le problème reste cependant le même, avec cependant le que les données sont copiées de la RAM système vers la mémoire vidéo. Le problème est alors que le processeur écrit dans son cache, mais que les écritures ne sont pas toutes répercutées en RAM. Si les données en RAM système ne sont pas les bonnes, la copie donnera un mauvais résultat. Un point important est donc de garantir que la RAM système ait les bonnes données avant de les copier en mémoire vidéo.
Pour cela, le processeur est conçu pour contourner les caches lors de certaines écritures, à savoir que ces écritures se font en RAM directement, sans passer par le cache. C'est une fonctionnalité très importante pour communiquer avec les périphériques. Les processeurs modernes sont capables de marquer des blocs de mémoire entiers comme étant non-cacheables, à savoir que toute lecture/écriture dedans se fait sans passer par le cache. Il suffit au CPU de copier les données à envoyer au GPU dans une de ces zone non-cacheable.
Pour les GPU dédiés/soudés, cela a un lien avec la mémoire vidéo mappée en mémoire. Plus haut, nous avions dit que la mémoire vidéo est visible dans l'espace d'adressage du processeur, à savoir qu'un bloc de mémoire est détourné pour adresser non pas la RAM, mais la mémoire vidéo. Et bien ce bloc de mémoire entier est marqué comme étant non-cacheable. Du côté du GPU, les échanges entre RAM système et mémoire vidéo sont le fait du contrôleur DMA. La solution est alors toute trouvée : le contrôleur DMA accède directement en mémoire vidéo et en RAM système, sans passer par les caches du GPU. Il suffit d'invalider les caches du GPU après un transfert DMA, pour que le GPU lise les données directement en RAM et le tour est joué.
La situation est gérée différemment sur les GPU intégré, vu qu'ils utilisent la mémoire unifiée. Les données ne sont pas copiée de la RAM système à la mémoire vidéo, le GPU lit directement les données en RAM, il n'y a pas de contrôleur DMA qui contournerait les caches. Si le GPU est conçu pour, il n'y a pas besoin de marquer les données comme non-cacheables. Le cas le plus simple est celui où le CPU et le GPU partagent leur cache L3/L4. Dans ce cas, il n'y a qu'un seul cache L3/L4 qui ne contient qu'une seule copie valide, écrite par le CPU et lue par le GPU. Il faut juste garantir que la donnée soit lue par le GPU depuis le L3, mais c'est là une question d'inclusivité du cache, qui ne nous concerne pas ici. Si le CPU et le GPU ne partagent pas de cache, il suffit que le GPU puisse lire les caches du CPU. C'est la méthode utilisée sur l'APU Trinity d'AMD.
===La cohérence des caches entre processeurs de shaders===
Pour terminer, il faut voir la cohérence des caches entre processeurs de shaders. Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs auquel on aurait rajouté des ROPs, les circuits de la rastérisation et les unités de textures. Il n'est donc pas étonnant que les problèmes rencontrés sur les processeurs multicœurs soient aussi présents sur les GPU, la cohérence des caches ne fait pas exception.
Pour simplifier les explications, nous allons partir du principe que chaque processeur de shaders a son propre cache de données. Prenons deux processeur de shaders qui ont chacun une copie d'une donnée dans leur cache. Si un processeur de shaders modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches.
[[File:Cohérence des caches.png|centre|vignette|upright=2|Cohérence des caches]]
La réalité est cependant plus complexe, dans le sens où il n'y a souvent pas un cache par processeur de shaders, mais une hiérarchie de cache assez complexe, avec un cache L1 par processeur de shaders, un cache L2 partagé entre plusieurs processeur de shaders, des caches partagés entre tous les processeur de shaders, etc. Certains GPU partagent leur cache L1 d’instructions entre plusieurs processeur de shaders, d'autres non. Mais le principe reste valide, tant qu'un cache n'est pas partagé entre tous les processeurs de shaders : un cache peut contenir une donnée invalide, à savoir qu'elle a été modifiée dans le cache d'un autre processeur de shaders.
Pour corriger ce problème, les ingénieurs ont inventé des '''protocoles de cohérence des caches''' pour détecter les données périmées et les mettre à jour. Mais autant ces techniques sont faisables sur des CPU avec un nombre limité de cœurs, autant elles sont impraticables avec un GPU contenant une centaine de cœurs. Heureusement, la cohérence des caches est un problème bien moins important sur les GPU que sur les CPU. En effet, le rendu 3D implique un parallélisme de données : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches.
En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même. Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisés. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur insère une instruction qui invalide le cache, avant l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo et donc qu'il s'agit d'une donnée correcte.
C'est cette technique qui est utilisée pour les caches de textures, pour supporter les techniques de ''render-to-texture''. Dans le chapitre précédent, on a vu que lorsque d'une texture est modifiée par le GPU, les caches de texture sont réinitialisés. Les opérations de ''render-to-texture'' étant assez rares, il vaut mieux faire cela que de rendre les caches de texture accesibles en écriture. Ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches du GPU sont gérés avec le même principe. Pour les caches généralistes, certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.
{{NavChapitre | book=Les cartes graphiques
| prev=La hiérarchie mémoire d'un GPU
| prevText=La hiérarchie mémoire d'un GPU
| next=Le processeur de commandes
| netxText=Le processeur de commandes
}}{{autocat}}
k73lfhavn2d206poj94acddeuehne16
745307
745304
2025-06-24T19:54:03Z
Mewtow
31375
/* La cohérence des caches sur un GPU */
745307
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.
==La mémoire vidéo dédiée==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
==La cohérence des caches sur un GPU==
Pour terminer ce chapitre, nous allons parler de la '''cohérence des caches'''. La cohérence des caches est un problème qui se manifeste à plusieurs niveaux, quand on parle d'un GPU. Mais la situation la plus courante est celle où le processeur veut envoyer des données à la carte graphique. Le cas de la mémoire unifiée est un peu à part, aussi étudions le cas des GPU dédiés avant. Sur un GPU dédié, un tel transfert se fait en trois temps : le CPU écrit des données en mémoire RAM, les données sont copiées de la RAM vers la mémoire vidéo, puis les données sont lues par le GPU en mémoire vidéo. La cohérence des caches se manifeste entre chaque étape.
===La cohérence des caches et le DMA des GPU dédiés===
Un GPU dédié échange des données avec la RAM système via des transferts DMA, qui copient des données de la RAM système vers la mémoire vidéo. Et les transferts DMA posent des problèmes avec les caches intégrés au GPU, dans une situation bien précise. Le problème est le suivant : imaginez qu'un transfert DMA remplace des données par d'autres, à savoir qu'il écrase des données inutiles pour les remplacer par des données utiles. C'est très fréquent, les pilotes graphiques libèrent souvent de la mémoire vidéo pour la réallouer immédiatement après, afin de ne pas gaspiller de VRAM.
Sans intervention particulière, les données inutiles sont encore dans le cache ! Le remplacement des données aura été fait en mémoire vidéo, pas dans les caches du GPU. Le cache associe toujours l'ancienne donnée écrasée à l'adresse mémoire où elles étaient. Et tout accès ultérieur à ces adresses mémoire renverra la donnée écrasée.
[[File:Cache incoherence write.svg|centre|vignette|upright=2|Cohérence des caches avec DMA.]]
Pour éviter cela, le GPU invalide ses caches en cas de transfert DMA. Par invalider, on veut dire que le cache est réinitialisé, mis à zéro, il est rendu vierge de toute donnée. Ainsi, tout accès mémoire ultérieur se fera en mémoire RAM sans passer par le cache. Les données lues depuis la RAM seront ensuite copiées dans le cache, mais ce seront les données valides écrites après le transfert DMA. Le contenu du cache est alors reconstitué. L'invalidation est automatique sur les anciens GPU, elle est réalisée par le processeur de commande. Sur les GPU modernes, elle est réalisée par le programmeur, comme on va le voir dans la section immédiatement suivante.
La même chose a lieu, mais avec la mémoire unifiée. La mémoire unifiée n'implique pas de transfert DMA entre CPU et GPU, vu qu'il n'y a qu'une seule RAM unifiée. Par contre, lorsque le processeur veut envoyer des données au GPU, par exemple des textures ou un tampon de sommets, il doit forcer le GPU à invalider ses caches.
===La cohérence des caches entre CPU et GPU===
Un ordinateur contient un processeur et un GPU, chacun avec leurs caches séparés. Le processeur ou le GPU peuvent alors modifier la donnée chacun dans leur cache, ce qui fait que la donnée en RAM, celle dans le cache du GPU et celle dans le cache du CPU, seront totalement différentes. Il y aura donc une incohérence : une lecture à une adresse ne donnera pas le même résultat selon que la lecture vienne du CPU, du GPU ou d'un périphérique DMA. Alors qu'il idéalement, on voudrait que les caches soient cohérents, à savoir qu'ils contiennent tous la même copie d'une donnée, ou presque.
[[File:Cohérence des caches entre CPU et GPU avec mémoire unifiée.png|centre|vignette|upright=2|Cohérence des caches entre CPU et GPU avec mémoire unifiée]]
Le problème se manifeste aussi bien avec la mémoire unifiée qu'avec une mémoire vidéo séparée. Le schéma précédent illustre ce qui se passe avec la mémoire unifiée. Avec une mémoire vidéo séparée, le problème reste cependant le même, avec cependant le que les données sont copiées de la RAM système vers la mémoire vidéo. Le problème est alors que le processeur écrit dans son cache, mais que les écritures ne sont pas toutes répercutées en RAM. Si les données en RAM système ne sont pas les bonnes, la copie donnera un mauvais résultat. Un point important est donc de garantir que la RAM système ait les bonnes données avant de les copier en mémoire vidéo.
Pour cela, le processeur est conçu pour contourner les caches lors de certaines écritures, à savoir que ces écritures se font en RAM directement, sans passer par le cache. C'est une fonctionnalité très importante pour communiquer avec les périphériques. Les processeurs modernes sont capables de marquer des blocs de mémoire entiers comme étant non-cacheables, à savoir que toute lecture/écriture dedans se fait sans passer par le cache. Il suffit au CPU de copier les données à envoyer au GPU dans une de ces zone non-cacheable.
Pour les GPU dédiés/soudés, cela a un lien avec la mémoire vidéo mappée en mémoire. Plus haut, nous avions dit que la mémoire vidéo est visible dans l'espace d'adressage du processeur, à savoir qu'un bloc de mémoire est détourné pour adresser non pas la RAM, mais la mémoire vidéo. Et bien ce bloc de mémoire entier est marqué comme étant non-cacheable. Du côté du GPU, les échanges entre RAM système et mémoire vidéo sont le fait du contrôleur DMA. La solution est alors toute trouvée : le contrôleur DMA accède directement en mémoire vidéo et en RAM système, sans passer par les caches du GPU. Il suffit d'invalider les caches du GPU après un transfert DMA, pour que le GPU lise les données directement en RAM et le tour est joué.
La situation est gérée différemment sur les GPU intégré, vu qu'ils utilisent la mémoire unifiée. Les données ne sont pas copiée de la RAM système à la mémoire vidéo, le GPU lit directement les données en RAM, il n'y a pas de contrôleur DMA qui contournerait les caches. Si le GPU est conçu pour, il n'y a pas besoin de marquer les données comme non-cacheables. Le cas le plus simple est celui où le CPU et le GPU partagent leur cache L3/L4. Dans ce cas, il n'y a qu'un seul cache L3/L4 qui ne contient qu'une seule copie valide, écrite par le CPU et lue par le GPU. Il faut juste garantir que la donnée soit lue par le GPU depuis le L3, mais c'est là une question d'inclusivité du cache, qui ne nous concerne pas ici. Si le CPU et le GPU ne partagent pas de cache, il suffit que le GPU puisse lire les caches du CPU. C'est la méthode utilisée sur l'APU Trinity d'AMD.
===La cohérence des caches entre processeurs de shaders===
Pour terminer, il faut voir la cohérence des caches entre processeurs de shaders. Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs auquel on aurait rajouté des ROPs, les circuits de la rastérisation et les unités de textures. Il n'est donc pas étonnant que les problèmes rencontrés sur les processeurs multicœurs soient aussi présents sur les GPU, la cohérence des caches ne fait pas exception.
Pour simplifier les explications, nous allons partir du principe que chaque processeur de shaders a son propre cache de données. Prenons deux processeur de shaders qui ont chacun une copie d'une donnée dans leur cache. Si un processeur de shaders modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches.
[[File:Cohérence des caches.png|centre|vignette|upright=2|Cohérence des caches]]
La réalité est cependant plus complexe, dans le sens où il n'y a souvent pas un cache par processeur de shaders, mais une hiérarchie de cache assez complexe, avec un cache L1 par processeur de shaders, un cache L2 partagé entre plusieurs processeur de shaders, des caches partagés entre tous les processeur de shaders, etc. Certains GPU partagent leur cache L1 d’instructions entre plusieurs processeur de shaders, d'autres non. Mais le principe reste valide, tant qu'un cache n'est pas partagé entre tous les processeurs de shaders : un cache peut contenir une donnée invalide, à savoir qu'elle a été modifiée dans le cache d'un autre processeur de shaders.
Pour corriger ce problème, les ingénieurs ont inventé des '''protocoles de cohérence des caches''' pour détecter les données périmées et les mettre à jour. Mais autant ces techniques sont faisables sur des CPU avec un nombre limité de cœurs, autant elles sont impraticables avec un GPU contenant une centaine de cœurs. Heureusement, la cohérence des caches est un problème bien moins important sur les GPU que sur les CPU. En effet, le rendu 3D implique un parallélisme de données : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches.
En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même. Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisés. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur insère une instruction qui invalide le cache, avant l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo et donc qu'il s'agit d'une donnée correcte.
C'est cette technique qui est utilisée pour les caches de textures, pour supporter les techniques de ''render-to-texture''. Dans le chapitre précédent, on a vu que lorsque d'une texture est modifiée par le GPU, les caches de texture sont réinitialisés. Les opérations de ''render-to-texture'' étant assez rares, il vaut mieux faire cela que de rendre les caches de texture accesibles en écriture. Ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches du GPU sont gérés avec le même principe. Pour les caches généralistes, certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.
{{NavChapitre | book=Les cartes graphiques
| prev=La hiérarchie mémoire d'un GPU
| prevText=La hiérarchie mémoire d'un GPU
| next=Le processeur de commandes
| netxText=Le processeur de commandes
}}{{autocat}}
h6zj04qxsvogmaj2mydrkcf09reea82
745308
745307
2025-06-24T19:56:36Z
Mewtow
31375
/* La cohérence des caches sur un GPU */
745308
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.
==La mémoire vidéo dédiée==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
==La cohérence des caches sur un GPU==
Pour terminer ce chapitre, nous allons parler de la '''cohérence des caches'''. La cohérence des caches est un problème qui se manifeste à plusieurs niveaux, quand on parle d'un GPU. Mais la situation la plus courante est celle où le processeur veut envoyer des données à la carte graphique. Le cas de la mémoire unifiée est un peu à part, aussi étudions le cas des GPU dédiés avant. Sur un GPU dédié, un tel transfert se fait en trois temps :
* le CPU écrit des données en mémoire RAM ;
* les données sont copiées de la RAM vers la mémoire vidéo, via un transfert DMA ;
* les données sont lues par le GPU en mémoire vidéo.
La cohérence des caches se manifeste entre chaque étape : entre l'étape 1 et 2, entre l'étape 2 et 3. Voyons d'abord le problème entre les deux dernières étapes, à savoir une fois que le transfert DMA est terminé et que le GPU veut lire les données.
===La cohérence des caches et le DMA des GPU dédiés===
Une situation bien précise pose problème : imaginez qu'un transfert DMA remplace des données par d'autres, à savoir qu'il écrase des données inutiles pour les remplacer par des données utiles. C'est très fréquent, les pilotes graphiques libèrent souvent de la mémoire vidéo pour la réallouer immédiatement après, afin de ne pas gaspiller de VRAM. Sans intervention particulière, les données inutiles sont encore dans le cache ! Le remplacement des données aura été fait en mémoire vidéo, pas dans les caches du GPU. Le cache associe toujours l'ancienne donnée écrasée à l'adresse mémoire où elles étaient. Et tout accès ultérieur à ces adresses mémoire renverra la donnée écrasée.
[[File:Cache incoherence write.svg|centre|vignette|upright=2|Cohérence des caches avec DMA.]]
Pour éviter cela, le GPU invalide ses caches en cas de transfert DMA. Par invalider, on veut dire que le cache est réinitialisé, mis à zéro, il est rendu vierge de toute donnée. Ainsi, tout accès mémoire ultérieur se fera en mémoire RAM sans passer par le cache. Les données lues depuis la RAM seront ensuite copiées dans le cache, mais ce seront les données valides écrites après le transfert DMA. Le contenu du cache est alors reconstitué. L'invalidation est automatique sur les anciens GPU, elle est réalisée par le processeur de commande. Sur les GPU modernes, elle est réalisée par le programmeur, comme on va le voir dans la section immédiatement suivante.
La même chose a lieu, mais avec la mémoire unifiée. La mémoire unifiée n'implique pas de transfert DMA entre CPU et GPU, vu qu'il n'y a qu'une seule RAM unifiée. Par contre, lorsque le processeur veut envoyer des données au GPU, par exemple des textures ou un tampon de sommets, il doit forcer le GPU à invalider ses caches.
===La cohérence des caches entre CPU et GPU===
Un ordinateur contient un processeur et un GPU, chacun avec leurs caches séparés. Le processeur ou le GPU peuvent alors modifier la donnée chacun dans leur cache, ce qui fait que la donnée en RAM, celle dans le cache du GPU et celle dans le cache du CPU, seront totalement différentes. Il y aura donc une incohérence : une lecture à une adresse ne donnera pas le même résultat selon que la lecture vienne du CPU, du GPU ou d'un périphérique DMA. Alors qu'il idéalement, on voudrait que les caches soient cohérents, à savoir qu'ils contiennent tous la même copie d'une donnée, ou presque.
[[File:Cohérence des caches entre CPU et GPU avec mémoire unifiée.png|centre|vignette|upright=2|Cohérence des caches entre CPU et GPU avec mémoire unifiée]]
Le problème se manifeste aussi bien avec la mémoire unifiée qu'avec une mémoire vidéo séparée. Le schéma précédent illustre ce qui se passe avec la mémoire unifiée. Avec une mémoire vidéo séparée, le problème reste cependant le même, avec cependant le que les données sont copiées de la RAM système vers la mémoire vidéo. Le problème est alors que le processeur écrit dans son cache, mais que les écritures ne sont pas toutes répercutées en RAM. Si les données en RAM système ne sont pas les bonnes, la copie donnera un mauvais résultat. Un point important est donc de garantir que la RAM système ait les bonnes données avant de les copier en mémoire vidéo.
Pour cela, le processeur est conçu pour contourner les caches lors de certaines écritures, à savoir que ces écritures se font en RAM directement, sans passer par le cache. C'est une fonctionnalité très importante pour communiquer avec les périphériques. Les processeurs modernes sont capables de marquer des blocs de mémoire entiers comme étant non-cacheables, à savoir que toute lecture/écriture dedans se fait sans passer par le cache. Il suffit au CPU de copier les données à envoyer au GPU dans une de ces zone non-cacheable.
Pour les GPU dédiés/soudés, cela a un lien avec la mémoire vidéo mappée en mémoire. Plus haut, nous avions dit que la mémoire vidéo est visible dans l'espace d'adressage du processeur, à savoir qu'un bloc de mémoire est détourné pour adresser non pas la RAM, mais la mémoire vidéo. Et bien ce bloc de mémoire entier est marqué comme étant non-cacheable. Du côté du GPU, les échanges entre RAM système et mémoire vidéo sont le fait du contrôleur DMA. La solution est alors toute trouvée : le contrôleur DMA accède directement en mémoire vidéo et en RAM système, sans passer par les caches du GPU. Il suffit d'invalider les caches du GPU après un transfert DMA, pour que le GPU lise les données directement en RAM et le tour est joué.
La situation est gérée différemment sur les GPU intégré, vu qu'ils utilisent la mémoire unifiée. Les données ne sont pas copiée de la RAM système à la mémoire vidéo, le GPU lit directement les données en RAM, il n'y a pas de contrôleur DMA qui contournerait les caches. Si le GPU est conçu pour, il n'y a pas besoin de marquer les données comme non-cacheables. Le cas le plus simple est celui où le CPU et le GPU partagent leur cache L3/L4. Dans ce cas, il n'y a qu'un seul cache L3/L4 qui ne contient qu'une seule copie valide, écrite par le CPU et lue par le GPU. Il faut juste garantir que la donnée soit lue par le GPU depuis le L3, mais c'est là une question d'inclusivité du cache, qui ne nous concerne pas ici. Si le CPU et le GPU ne partagent pas de cache, il suffit que le GPU puisse lire les caches du CPU. C'est la méthode utilisée sur l'APU Trinity d'AMD.
===La cohérence des caches entre processeurs de shaders===
Pour terminer, il faut voir la cohérence des caches entre processeurs de shaders. Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs auquel on aurait rajouté des ROPs, les circuits de la rastérisation et les unités de textures. Il n'est donc pas étonnant que les problèmes rencontrés sur les processeurs multicœurs soient aussi présents sur les GPU, la cohérence des caches ne fait pas exception.
Pour simplifier les explications, nous allons partir du principe que chaque processeur de shaders a son propre cache de données. Prenons deux processeur de shaders qui ont chacun une copie d'une donnée dans leur cache. Si un processeur de shaders modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches.
[[File:Cohérence des caches.png|centre|vignette|upright=2|Cohérence des caches]]
La réalité est cependant plus complexe, dans le sens où il n'y a souvent pas un cache par processeur de shaders, mais une hiérarchie de cache assez complexe, avec un cache L1 par processeur de shaders, un cache L2 partagé entre plusieurs processeur de shaders, des caches partagés entre tous les processeur de shaders, etc. Certains GPU partagent leur cache L1 d’instructions entre plusieurs processeur de shaders, d'autres non. Mais le principe reste valide, tant qu'un cache n'est pas partagé entre tous les processeurs de shaders : un cache peut contenir une donnée invalide, à savoir qu'elle a été modifiée dans le cache d'un autre processeur de shaders.
Pour corriger ce problème, les ingénieurs ont inventé des '''protocoles de cohérence des caches''' pour détecter les données périmées et les mettre à jour. Mais autant ces techniques sont faisables sur des CPU avec un nombre limité de cœurs, autant elles sont impraticables avec un GPU contenant une centaine de cœurs. Heureusement, la cohérence des caches est un problème bien moins important sur les GPU que sur les CPU. En effet, le rendu 3D implique un parallélisme de données : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches.
En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même. Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisés. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur insère une instruction qui invalide le cache, avant l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo et donc qu'il s'agit d'une donnée correcte.
C'est cette technique qui est utilisée pour les caches de textures, pour supporter les techniques de ''render-to-texture''. Dans le chapitre précédent, on a vu que lorsque d'une texture est modifiée par le GPU, les caches de texture sont réinitialisés. Les opérations de ''render-to-texture'' étant assez rares, il vaut mieux faire cela que de rendre les caches de texture accesibles en écriture. Ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches du GPU sont gérés avec le même principe. Pour les caches généralistes, certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.
{{NavChapitre | book=Les cartes graphiques
| prev=La hiérarchie mémoire d'un GPU
| prevText=La hiérarchie mémoire d'un GPU
| next=Le processeur de commandes
| netxText=Le processeur de commandes
}}{{autocat}}
p0i90kfod3m12ve9h41iwfjvetboppo
745309
745308
2025-06-24T20:01:31Z
Mewtow
31375
/* La cohérence des caches sur un GPU */
745309
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.
==La mémoire vidéo dédiée==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
==La cohérence des caches sur un GPU==
Pour terminer ce chapitre, nous allons parler de la '''cohérence des caches'''. Un ordinateur contient un processeur et un GPU, chacun avec leurs caches séparés. Le processeur ou le GPU peuvent alors modifier la donnée chacun dans leur cache, ce qui fait que la donnée en RAM, celle dans le cache du GPU et celle dans le cache du CPU, seront totalement différentes. Il y aura donc une incohérence : une lecture à une adresse ne donnera pas le même résultat selon que la lecture vienne du CPU, du GPU ou d'un périphérique DMA. Alors qu'il idéalement, on voudrait que les caches soient cohérents, à savoir qu'ils contiennent tous la même copie d'une donnée, ou presque.
[[File:Cohérence des caches entre CPU et GPU avec mémoire unifiée.png|centre|vignette|upright=2|Cohérence des caches entre CPU et GPU avec mémoire unifiée]]
Le problème se manifeste aussi bien avec la mémoire unifiée qu'avec une mémoire vidéo séparée. Le schéma précédent illustre ce qui se passe avec la mémoire unifiée. Avec une mémoire vidéo séparée, le problème reste cependant le même, avec cependant le que les données sont copiées de la RAM système vers la mémoire vidéo. Le problème est alors que le processeur écrit dans son cache, mais que les écritures ne sont pas toutes répercutées en RAM. Si les données en RAM système ne sont pas les bonnes, la copie donnera un mauvais résultat. Un point important est donc de garantir que la RAM système ait les bonnes données avant de les copier en mémoire vidéo.
La cohérence des caches est un problème qui se manifeste à plusieurs niveaux, quand on parle d'un GPU. Mais la situation la plus courante est celle où le processeur veut envoyer des données à la carte graphique. Le cas de la mémoire unifiée est un peu à part, aussi étudions le cas des GPU dédiés avant. Sur un GPU dédié, un tel transfert se fait en trois temps :
* le CPU écrit des données en mémoire RAM ;
* les données sont copiées de la RAM vers la mémoire vidéo, via un transfert DMA ;
* les données sont lues par le GPU en mémoire vidéo.
La cohérence des caches se manifeste entre chaque étape : entre l'étape 1 et 2, entre l'étape 2 et 3. Voyons d'abord le problème entre les deux dernières étapes, à savoir une fois que le transfert DMA est terminé et que le GPU veut lire les données.
===La cohérence des caches et le DMA des GPU dédiés===
Après un transfert DMA, une situation bien précise pose problème : quand un transfert DMA remplace des données par d'autres, à savoir qu'il écrase des données devenues inutiles, pour les remplacer par des données utiles. C'est très fréquent, les pilotes graphiques libèrent souvent de la mémoire vidéo pour la réallouer immédiatement après, afin de ne pas gaspiller de VRAM. Sans intervention du GPU, le remplacement des données aura été fait en mémoire vidéo, pas dans les caches du GPU. Et tout accès ultérieur au cache renverra la donnée écrasée.
[[File:Cache incoherence write.svg|centre|vignette|upright=2|Cohérence des caches avec DMA.]]
Pour éviter cela, le GPU invalide ses caches en cas de transfert DMA. Par invalider, on veut dire que le cache est réinitialisé, mis à zéro, il est rendu vierge de toute donnée. Ainsi, tout accès mémoire ultérieur se fera en mémoire RAM sans passer par le cache. Les données lues depuis la RAM seront ensuite copiées dans le cache, mais ce seront les données valides écrites après le transfert DMA. Le contenu du cache est alors reconstitué au fur et à mesure des accès mémoire. L'invalidation est automatique sur les anciens GPU, elle est réalisée par le processeur de commande. Sur les GPU modernes, elle est réalisée par le programmeur, comme on va le voir dans la section immédiatement suivante.
La même chose a lieu, mais avec la mémoire unifiée. La mémoire unifiée n'implique pas de transfert DMA entre CPU et GPU, vu qu'il n'y a qu'une seule RAM unifiée. Par contre, lorsque le processeur envoie des données au GPU, par exemple des textures ou un tampon de sommets, il force le GPU à invalider ses caches.
===La cohérence des caches entre CPU et GPU===
Au niveau du processeur, le problème est l'inverse de ce qui se passe avec les transferts DMA. Avec les transferts DMA, la mémoire vidéo est modifiée, mais pas les caches. Avec le processeur, c'est l'inverse. Si le processeur veut envoyer des données au GPU, il doit écrire ces données dans la mémoire RAM, avant de faire le transfert DMA. Mais la présence de caches pose problème : les écritures peuvent être interceptées par le cache et ne pas être propagées en RAM.
Pour cela, le processeur est conçu pour contourner les caches lors de certaines écritures, à savoir que ces écritures se font en RAM directement, sans passer par le cache. C'est une fonctionnalité très importante pour communiquer avec les périphériques. Les processeurs modernes sont capables de marquer des blocs de mémoire entiers comme étant non-cacheables, à savoir que toute lecture/écriture dedans se fait sans passer par le cache. Il suffit au CPU de copier les données à envoyer au GPU dans une de ces zone non-cacheable.
Pour les GPU dédiés/soudés, cela a un lien avec la mémoire vidéo mappée en mémoire. Plus haut, nous avions dit que la mémoire vidéo est visible dans l'espace d'adressage du processeur, à savoir qu'un bloc de mémoire est détourné pour adresser non pas la RAM, mais la mémoire vidéo. Et bien ce bloc de mémoire entier est marqué comme étant non-cacheable. Du côté du GPU, les échanges entre RAM système et mémoire vidéo sont le fait du contrôleur DMA. La solution est alors toute trouvée : le contrôleur DMA accède directement en mémoire vidéo et en RAM système, sans passer par les caches du GPU. Il suffit d'invalider les caches du GPU après un transfert DMA, pour que le GPU lise les données directement en RAM et le tour est joué.
La situation est gérée différemment sur les GPU intégré, vu qu'ils utilisent la mémoire unifiée. Les données ne sont pas copiée de la RAM système à la mémoire vidéo, le GPU lit directement les données en RAM, il n'y a pas de contrôleur DMA qui contournerait les caches. Si le GPU est conçu pour, il n'y a pas besoin de marquer les données comme non-cacheables. Le cas le plus simple est celui où le CPU et le GPU partagent leur cache L3/L4. Dans ce cas, il n'y a qu'un seul cache L3/L4 qui ne contient qu'une seule copie valide, écrite par le CPU et lue par le GPU. Il faut juste garantir que la donnée soit lue par le GPU depuis le L3, mais c'est là une question d'inclusivité du cache, qui ne nous concerne pas ici. Si le CPU et le GPU ne partagent pas de cache, il suffit que le GPU puisse lire les caches du CPU. C'est la méthode utilisée sur l'APU Trinity d'AMD.
===La cohérence des caches entre processeurs de shaders===
Pour terminer, il faut voir la cohérence des caches entre processeurs de shaders. Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs auquel on aurait rajouté des ROPs, les circuits de la rastérisation et les unités de textures. Il n'est donc pas étonnant que les problèmes rencontrés sur les processeurs multicœurs soient aussi présents sur les GPU, la cohérence des caches ne fait pas exception.
Pour simplifier les explications, nous allons partir du principe que chaque processeur de shaders a son propre cache de données. Prenons deux processeur de shaders qui ont chacun une copie d'une donnée dans leur cache. Si un processeur de shaders modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches.
[[File:Cohérence des caches.png|centre|vignette|upright=2|Cohérence des caches]]
La réalité est cependant plus complexe, dans le sens où il n'y a souvent pas un cache par processeur de shaders, mais une hiérarchie de cache assez complexe, avec un cache L1 par processeur de shaders, un cache L2 partagé entre plusieurs processeur de shaders, des caches partagés entre tous les processeur de shaders, etc. Certains GPU partagent leur cache L1 d’instructions entre plusieurs processeur de shaders, d'autres non. Mais le principe reste valide, tant qu'un cache n'est pas partagé entre tous les processeurs de shaders : un cache peut contenir une donnée invalide, à savoir qu'elle a été modifiée dans le cache d'un autre processeur de shaders.
Pour corriger ce problème, les ingénieurs ont inventé des '''protocoles de cohérence des caches''' pour détecter les données périmées et les mettre à jour. Mais autant ces techniques sont faisables sur des CPU avec un nombre limité de cœurs, autant elles sont impraticables avec un GPU contenant une centaine de cœurs. Heureusement, la cohérence des caches est un problème bien moins important sur les GPU que sur les CPU. En effet, le rendu 3D implique un parallélisme de données : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches.
En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même. Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisés. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur insère une instruction qui invalide le cache, avant l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo et donc qu'il s'agit d'une donnée correcte.
C'est cette technique qui est utilisée pour les caches de textures, pour supporter les techniques de ''render-to-texture''. Dans le chapitre précédent, on a vu que lorsque d'une texture est modifiée par le GPU, les caches de texture sont réinitialisés. Les opérations de ''render-to-texture'' étant assez rares, il vaut mieux faire cela que de rendre les caches de texture accesibles en écriture. Ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches du GPU sont gérés avec le même principe. Pour les caches généralistes, certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.
{{NavChapitre | book=Les cartes graphiques
| prev=La hiérarchie mémoire d'un GPU
| prevText=La hiérarchie mémoire d'un GPU
| next=Le processeur de commandes
| netxText=Le processeur de commandes
}}{{autocat}}
6uywamjzconzggdsyc6it5rnr4d76s3
745310
745309
2025-06-24T20:01:58Z
Mewtow
31375
/* La cohérence des caches et le DMA des GPU dédiés */
745310
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.
==La mémoire vidéo dédiée==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
==La cohérence des caches sur un GPU==
Pour terminer ce chapitre, nous allons parler de la '''cohérence des caches'''. Un ordinateur contient un processeur et un GPU, chacun avec leurs caches séparés. Le processeur ou le GPU peuvent alors modifier la donnée chacun dans leur cache, ce qui fait que la donnée en RAM, celle dans le cache du GPU et celle dans le cache du CPU, seront totalement différentes. Il y aura donc une incohérence : une lecture à une adresse ne donnera pas le même résultat selon que la lecture vienne du CPU, du GPU ou d'un périphérique DMA. Alors qu'il idéalement, on voudrait que les caches soient cohérents, à savoir qu'ils contiennent tous la même copie d'une donnée, ou presque.
[[File:Cohérence des caches entre CPU et GPU avec mémoire unifiée.png|centre|vignette|upright=2|Cohérence des caches entre CPU et GPU avec mémoire unifiée]]
Le problème se manifeste aussi bien avec la mémoire unifiée qu'avec une mémoire vidéo séparée. Le schéma précédent illustre ce qui se passe avec la mémoire unifiée. Avec une mémoire vidéo séparée, le problème reste cependant le même, avec cependant le que les données sont copiées de la RAM système vers la mémoire vidéo. Le problème est alors que le processeur écrit dans son cache, mais que les écritures ne sont pas toutes répercutées en RAM. Si les données en RAM système ne sont pas les bonnes, la copie donnera un mauvais résultat. Un point important est donc de garantir que la RAM système ait les bonnes données avant de les copier en mémoire vidéo.
La cohérence des caches est un problème qui se manifeste à plusieurs niveaux, quand on parle d'un GPU. Mais la situation la plus courante est celle où le processeur veut envoyer des données à la carte graphique. Le cas de la mémoire unifiée est un peu à part, aussi étudions le cas des GPU dédiés avant. Sur un GPU dédié, un tel transfert se fait en trois temps :
* le CPU écrit des données en mémoire RAM ;
* les données sont copiées de la RAM vers la mémoire vidéo, via un transfert DMA ;
* les données sont lues par le GPU en mémoire vidéo.
La cohérence des caches se manifeste entre chaque étape : entre l'étape 1 et 2, entre l'étape 2 et 3. Voyons d'abord le problème entre les deux dernières étapes, à savoir une fois que le transfert DMA est terminé et que le GPU veut lire les données.
===La cohérence des caches pour les lectures GPU en mémoire vidéo===
Après un transfert DMA, une situation bien précise pose problème : quand un transfert DMA remplace des données par d'autres, à savoir qu'il écrase des données devenues inutiles, pour les remplacer par des données utiles. C'est très fréquent, les pilotes graphiques libèrent souvent de la mémoire vidéo pour la réallouer immédiatement après, afin de ne pas gaspiller de VRAM. Sans intervention du GPU, le remplacement des données aura été fait en mémoire vidéo, pas dans les caches du GPU. Et tout accès ultérieur au cache renverra la donnée écrasée.
[[File:Cache incoherence write.svg|centre|vignette|upright=2|Cohérence des caches avec DMA.]]
Pour éviter cela, le GPU invalide ses caches en cas de transfert DMA. Par invalider, on veut dire que le cache est réinitialisé, mis à zéro, il est rendu vierge de toute donnée. Ainsi, tout accès mémoire ultérieur se fera en mémoire RAM sans passer par le cache. Les données lues depuis la RAM seront ensuite copiées dans le cache, mais ce seront les données valides écrites après le transfert DMA. Le contenu du cache est alors reconstitué au fur et à mesure des accès mémoire. L'invalidation est automatique sur les anciens GPU, elle est réalisée par le processeur de commande. Sur les GPU modernes, elle est réalisée par le programmeur, comme on va le voir dans la section immédiatement suivante.
La même chose a lieu, mais avec la mémoire unifiée. La mémoire unifiée n'implique pas de transfert DMA entre CPU et GPU, vu qu'il n'y a qu'une seule RAM unifiée. Par contre, lorsque le processeur envoie des données au GPU, par exemple des textures ou un tampon de sommets, il force le GPU à invalider ses caches.
===La cohérence des caches entre CPU et GPU===
Au niveau du processeur, le problème est l'inverse de ce qui se passe avec les transferts DMA. Avec les transferts DMA, la mémoire vidéo est modifiée, mais pas les caches. Avec le processeur, c'est l'inverse. Si le processeur veut envoyer des données au GPU, il doit écrire ces données dans la mémoire RAM, avant de faire le transfert DMA. Mais la présence de caches pose problème : les écritures peuvent être interceptées par le cache et ne pas être propagées en RAM.
Pour cela, le processeur est conçu pour contourner les caches lors de certaines écritures, à savoir que ces écritures se font en RAM directement, sans passer par le cache. C'est une fonctionnalité très importante pour communiquer avec les périphériques. Les processeurs modernes sont capables de marquer des blocs de mémoire entiers comme étant non-cacheables, à savoir que toute lecture/écriture dedans se fait sans passer par le cache. Il suffit au CPU de copier les données à envoyer au GPU dans une de ces zone non-cacheable.
Pour les GPU dédiés/soudés, cela a un lien avec la mémoire vidéo mappée en mémoire. Plus haut, nous avions dit que la mémoire vidéo est visible dans l'espace d'adressage du processeur, à savoir qu'un bloc de mémoire est détourné pour adresser non pas la RAM, mais la mémoire vidéo. Et bien ce bloc de mémoire entier est marqué comme étant non-cacheable. Du côté du GPU, les échanges entre RAM système et mémoire vidéo sont le fait du contrôleur DMA. La solution est alors toute trouvée : le contrôleur DMA accède directement en mémoire vidéo et en RAM système, sans passer par les caches du GPU. Il suffit d'invalider les caches du GPU après un transfert DMA, pour que le GPU lise les données directement en RAM et le tour est joué.
La situation est gérée différemment sur les GPU intégré, vu qu'ils utilisent la mémoire unifiée. Les données ne sont pas copiée de la RAM système à la mémoire vidéo, le GPU lit directement les données en RAM, il n'y a pas de contrôleur DMA qui contournerait les caches. Si le GPU est conçu pour, il n'y a pas besoin de marquer les données comme non-cacheables. Le cas le plus simple est celui où le CPU et le GPU partagent leur cache L3/L4. Dans ce cas, il n'y a qu'un seul cache L3/L4 qui ne contient qu'une seule copie valide, écrite par le CPU et lue par le GPU. Il faut juste garantir que la donnée soit lue par le GPU depuis le L3, mais c'est là une question d'inclusivité du cache, qui ne nous concerne pas ici. Si le CPU et le GPU ne partagent pas de cache, il suffit que le GPU puisse lire les caches du CPU. C'est la méthode utilisée sur l'APU Trinity d'AMD.
===La cohérence des caches entre processeurs de shaders===
Pour terminer, il faut voir la cohérence des caches entre processeurs de shaders. Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs auquel on aurait rajouté des ROPs, les circuits de la rastérisation et les unités de textures. Il n'est donc pas étonnant que les problèmes rencontrés sur les processeurs multicœurs soient aussi présents sur les GPU, la cohérence des caches ne fait pas exception.
Pour simplifier les explications, nous allons partir du principe que chaque processeur de shaders a son propre cache de données. Prenons deux processeur de shaders qui ont chacun une copie d'une donnée dans leur cache. Si un processeur de shaders modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches.
[[File:Cohérence des caches.png|centre|vignette|upright=2|Cohérence des caches]]
La réalité est cependant plus complexe, dans le sens où il n'y a souvent pas un cache par processeur de shaders, mais une hiérarchie de cache assez complexe, avec un cache L1 par processeur de shaders, un cache L2 partagé entre plusieurs processeur de shaders, des caches partagés entre tous les processeur de shaders, etc. Certains GPU partagent leur cache L1 d’instructions entre plusieurs processeur de shaders, d'autres non. Mais le principe reste valide, tant qu'un cache n'est pas partagé entre tous les processeurs de shaders : un cache peut contenir une donnée invalide, à savoir qu'elle a été modifiée dans le cache d'un autre processeur de shaders.
Pour corriger ce problème, les ingénieurs ont inventé des '''protocoles de cohérence des caches''' pour détecter les données périmées et les mettre à jour. Mais autant ces techniques sont faisables sur des CPU avec un nombre limité de cœurs, autant elles sont impraticables avec un GPU contenant une centaine de cœurs. Heureusement, la cohérence des caches est un problème bien moins important sur les GPU que sur les CPU. En effet, le rendu 3D implique un parallélisme de données : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches.
En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même. Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisés. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur insère une instruction qui invalide le cache, avant l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo et donc qu'il s'agit d'une donnée correcte.
C'est cette technique qui est utilisée pour les caches de textures, pour supporter les techniques de ''render-to-texture''. Dans le chapitre précédent, on a vu que lorsque d'une texture est modifiée par le GPU, les caches de texture sont réinitialisés. Les opérations de ''render-to-texture'' étant assez rares, il vaut mieux faire cela que de rendre les caches de texture accesibles en écriture. Ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches du GPU sont gérés avec le même principe. Pour les caches généralistes, certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.
{{NavChapitre | book=Les cartes graphiques
| prev=La hiérarchie mémoire d'un GPU
| prevText=La hiérarchie mémoire d'un GPU
| next=Le processeur de commandes
| netxText=Le processeur de commandes
}}{{autocat}}
0ffmdboiag6scs347riiqlpswivrly1
745311
745310
2025-06-24T20:02:12Z
Mewtow
31375
/* La cohérence des caches entre CPU et GPU */
745311
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.
==La mémoire vidéo dédiée==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
==La cohérence des caches sur un GPU==
Pour terminer ce chapitre, nous allons parler de la '''cohérence des caches'''. Un ordinateur contient un processeur et un GPU, chacun avec leurs caches séparés. Le processeur ou le GPU peuvent alors modifier la donnée chacun dans leur cache, ce qui fait que la donnée en RAM, celle dans le cache du GPU et celle dans le cache du CPU, seront totalement différentes. Il y aura donc une incohérence : une lecture à une adresse ne donnera pas le même résultat selon que la lecture vienne du CPU, du GPU ou d'un périphérique DMA. Alors qu'il idéalement, on voudrait que les caches soient cohérents, à savoir qu'ils contiennent tous la même copie d'une donnée, ou presque.
[[File:Cohérence des caches entre CPU et GPU avec mémoire unifiée.png|centre|vignette|upright=2|Cohérence des caches entre CPU et GPU avec mémoire unifiée]]
Le problème se manifeste aussi bien avec la mémoire unifiée qu'avec une mémoire vidéo séparée. Le schéma précédent illustre ce qui se passe avec la mémoire unifiée. Avec une mémoire vidéo séparée, le problème reste cependant le même, avec cependant le que les données sont copiées de la RAM système vers la mémoire vidéo. Le problème est alors que le processeur écrit dans son cache, mais que les écritures ne sont pas toutes répercutées en RAM. Si les données en RAM système ne sont pas les bonnes, la copie donnera un mauvais résultat. Un point important est donc de garantir que la RAM système ait les bonnes données avant de les copier en mémoire vidéo.
La cohérence des caches est un problème qui se manifeste à plusieurs niveaux, quand on parle d'un GPU. Mais la situation la plus courante est celle où le processeur veut envoyer des données à la carte graphique. Le cas de la mémoire unifiée est un peu à part, aussi étudions le cas des GPU dédiés avant. Sur un GPU dédié, un tel transfert se fait en trois temps :
* le CPU écrit des données en mémoire RAM ;
* les données sont copiées de la RAM vers la mémoire vidéo, via un transfert DMA ;
* les données sont lues par le GPU en mémoire vidéo.
La cohérence des caches se manifeste entre chaque étape : entre l'étape 1 et 2, entre l'étape 2 et 3. Voyons d'abord le problème entre les deux dernières étapes, à savoir une fois que le transfert DMA est terminé et que le GPU veut lire les données.
===La cohérence des caches pour les lectures GPU en mémoire vidéo===
Après un transfert DMA, une situation bien précise pose problème : quand un transfert DMA remplace des données par d'autres, à savoir qu'il écrase des données devenues inutiles, pour les remplacer par des données utiles. C'est très fréquent, les pilotes graphiques libèrent souvent de la mémoire vidéo pour la réallouer immédiatement après, afin de ne pas gaspiller de VRAM. Sans intervention du GPU, le remplacement des données aura été fait en mémoire vidéo, pas dans les caches du GPU. Et tout accès ultérieur au cache renverra la donnée écrasée.
[[File:Cache incoherence write.svg|centre|vignette|upright=2|Cohérence des caches avec DMA.]]
Pour éviter cela, le GPU invalide ses caches en cas de transfert DMA. Par invalider, on veut dire que le cache est réinitialisé, mis à zéro, il est rendu vierge de toute donnée. Ainsi, tout accès mémoire ultérieur se fera en mémoire RAM sans passer par le cache. Les données lues depuis la RAM seront ensuite copiées dans le cache, mais ce seront les données valides écrites après le transfert DMA. Le contenu du cache est alors reconstitué au fur et à mesure des accès mémoire. L'invalidation est automatique sur les anciens GPU, elle est réalisée par le processeur de commande. Sur les GPU modernes, elle est réalisée par le programmeur, comme on va le voir dans la section immédiatement suivante.
La même chose a lieu, mais avec la mémoire unifiée. La mémoire unifiée n'implique pas de transfert DMA entre CPU et GPU, vu qu'il n'y a qu'une seule RAM unifiée. Par contre, lorsque le processeur envoie des données au GPU, par exemple des textures ou un tampon de sommets, il force le GPU à invalider ses caches.
===La cohérence des caches au niveau du processeur===
Au niveau du processeur, le problème est l'inverse de ce qui se passe avec les transferts DMA. Avec les transferts DMA, la mémoire vidéo est modifiée, mais pas les caches. Avec le processeur, c'est l'inverse. Si le processeur veut envoyer des données au GPU, il doit écrire ces données dans la mémoire RAM, avant de faire le transfert DMA. Mais la présence de caches pose problème : les écritures peuvent être interceptées par le cache et ne pas être propagées en RAM.
Pour cela, le processeur est conçu pour contourner les caches lors de certaines écritures, à savoir que ces écritures se font en RAM directement, sans passer par le cache. C'est une fonctionnalité très importante pour communiquer avec les périphériques. Les processeurs modernes sont capables de marquer des blocs de mémoire entiers comme étant non-cacheables, à savoir que toute lecture/écriture dedans se fait sans passer par le cache. Il suffit au CPU de copier les données à envoyer au GPU dans une de ces zone non-cacheable.
Pour les GPU dédiés/soudés, cela a un lien avec la mémoire vidéo mappée en mémoire. Plus haut, nous avions dit que la mémoire vidéo est visible dans l'espace d'adressage du processeur, à savoir qu'un bloc de mémoire est détourné pour adresser non pas la RAM, mais la mémoire vidéo. Et bien ce bloc de mémoire entier est marqué comme étant non-cacheable. Du côté du GPU, les échanges entre RAM système et mémoire vidéo sont le fait du contrôleur DMA. La solution est alors toute trouvée : le contrôleur DMA accède directement en mémoire vidéo et en RAM système, sans passer par les caches du GPU. Il suffit d'invalider les caches du GPU après un transfert DMA, pour que le GPU lise les données directement en RAM et le tour est joué.
La situation est gérée différemment sur les GPU intégré, vu qu'ils utilisent la mémoire unifiée. Les données ne sont pas copiée de la RAM système à la mémoire vidéo, le GPU lit directement les données en RAM, il n'y a pas de contrôleur DMA qui contournerait les caches. Si le GPU est conçu pour, il n'y a pas besoin de marquer les données comme non-cacheables. Le cas le plus simple est celui où le CPU et le GPU partagent leur cache L3/L4. Dans ce cas, il n'y a qu'un seul cache L3/L4 qui ne contient qu'une seule copie valide, écrite par le CPU et lue par le GPU. Il faut juste garantir que la donnée soit lue par le GPU depuis le L3, mais c'est là une question d'inclusivité du cache, qui ne nous concerne pas ici. Si le CPU et le GPU ne partagent pas de cache, il suffit que le GPU puisse lire les caches du CPU. C'est la méthode utilisée sur l'APU Trinity d'AMD.
===La cohérence des caches entre processeurs de shaders===
Pour terminer, il faut voir la cohérence des caches entre processeurs de shaders. Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs auquel on aurait rajouté des ROPs, les circuits de la rastérisation et les unités de textures. Il n'est donc pas étonnant que les problèmes rencontrés sur les processeurs multicœurs soient aussi présents sur les GPU, la cohérence des caches ne fait pas exception.
Pour simplifier les explications, nous allons partir du principe que chaque processeur de shaders a son propre cache de données. Prenons deux processeur de shaders qui ont chacun une copie d'une donnée dans leur cache. Si un processeur de shaders modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches.
[[File:Cohérence des caches.png|centre|vignette|upright=2|Cohérence des caches]]
La réalité est cependant plus complexe, dans le sens où il n'y a souvent pas un cache par processeur de shaders, mais une hiérarchie de cache assez complexe, avec un cache L1 par processeur de shaders, un cache L2 partagé entre plusieurs processeur de shaders, des caches partagés entre tous les processeur de shaders, etc. Certains GPU partagent leur cache L1 d’instructions entre plusieurs processeur de shaders, d'autres non. Mais le principe reste valide, tant qu'un cache n'est pas partagé entre tous les processeurs de shaders : un cache peut contenir une donnée invalide, à savoir qu'elle a été modifiée dans le cache d'un autre processeur de shaders.
Pour corriger ce problème, les ingénieurs ont inventé des '''protocoles de cohérence des caches''' pour détecter les données périmées et les mettre à jour. Mais autant ces techniques sont faisables sur des CPU avec un nombre limité de cœurs, autant elles sont impraticables avec un GPU contenant une centaine de cœurs. Heureusement, la cohérence des caches est un problème bien moins important sur les GPU que sur les CPU. En effet, le rendu 3D implique un parallélisme de données : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches.
En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même. Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisés. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur insère une instruction qui invalide le cache, avant l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo et donc qu'il s'agit d'une donnée correcte.
C'est cette technique qui est utilisée pour les caches de textures, pour supporter les techniques de ''render-to-texture''. Dans le chapitre précédent, on a vu que lorsque d'une texture est modifiée par le GPU, les caches de texture sont réinitialisés. Les opérations de ''render-to-texture'' étant assez rares, il vaut mieux faire cela que de rendre les caches de texture accesibles en écriture. Ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches du GPU sont gérés avec le même principe. Pour les caches généralistes, certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.
{{NavChapitre | book=Les cartes graphiques
| prev=La hiérarchie mémoire d'un GPU
| prevText=La hiérarchie mémoire d'un GPU
| next=Le processeur de commandes
| netxText=Le processeur de commandes
}}{{autocat}}
4sbav3c0vh9oqxiv5mvkrh6z8od8xk9
745312
745311
2025-06-24T20:06:05Z
Mewtow
31375
/* La cohérence des caches sur un GPU */
745312
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.
==La mémoire vidéo dédiée==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
==La cohérence des caches sur un GPU==
Pour terminer ce chapitre, nous allons parler de la '''cohérence des caches'''. La cohérence des caches est un problème qui se manifeste à plusieurs niveaux, quand on parle d'un GPU. Mais la situation la plus courante est celle où le processeur échange des données avec la carte graphique.
Un ordinateur contient un processeur et un GPU, chacun avec leurs caches séparés. Le processeur ou le GPU peuvent alors modifier la donnée chacun dans leur cache. Le résultat est que la donnée en RAM, celle dans le cache du GPU et celle dans le cache du CPU, seront totalement différentes. Il y a donc une incohérence : une lecture à une adresse ne donnera pas le même résultat selon que la lecture vienne du CPU, du GPU ou d'un périphérique DMA. Alors qu'il idéalement, on voudrait que les caches soient cohérents, à savoir qu'ils contiennent tous la même copie d'une donnée, ou presque.
[[File:Cohérence des caches entre CPU et GPU avec mémoire unifiée.png|centre|vignette|upright=2|Cohérence des caches entre CPU et GPU avec mémoire unifiée]]
Le problème se manifeste aussi bien avec la mémoire unifiée qu'avec une mémoire vidéo séparée. Le schéma précédent illustre ce qui se passe avec la mémoire unifiée. Avec une mémoire vidéo séparée, il faut prendre en compte le fait que les données sont copiées de la RAM système vers la mémoire vidéo. Le cas de la mémoire unifiée est un peu à part, aussi étudions le cas des GPU dédiés avant. Sur un GPU dédié, le processeur envoie des données au GPU en trois temps :
* le CPU écrit des données en mémoire RAM ;
* les données sont copiées de la RAM vers la mémoire vidéo, via un transfert DMA ;
* les données sont lues par le GPU en mémoire vidéo.
La cohérence des caches se manifeste entre chaque étape : entre l'étape 1 et 2, entre l'étape 2 et 3. Voyons d'abord le problème entre les deux dernières étapes, à savoir une fois que le transfert DMA est terminé et que le GPU veut lire les données.
===La cohérence des caches pour les lectures GPU en mémoire vidéo===
Après un transfert DMA, une situation bien précise pose problème : quand un transfert DMA remplace des données par d'autres, à savoir qu'il écrase des données devenues inutiles, pour les remplacer par des données utiles. C'est très fréquent, les pilotes graphiques libèrent souvent de la mémoire vidéo pour la réallouer immédiatement après, afin de ne pas gaspiller de VRAM. Sans intervention du GPU, le remplacement des données aura été fait en mémoire vidéo, pas dans les caches du GPU. Et tout accès ultérieur au cache renverra la donnée écrasée.
[[File:Cache incoherence write.svg|centre|vignette|upright=2|Cohérence des caches avec DMA.]]
Pour éviter cela, le GPU invalide ses caches en cas de transfert DMA. Par invalider, on veut dire que le cache est réinitialisé, mis à zéro, il est rendu vierge de toute donnée. Ainsi, tout accès mémoire ultérieur se fera en mémoire RAM sans passer par le cache. Les données lues depuis la RAM seront ensuite copiées dans le cache, mais ce seront les données valides écrites après le transfert DMA. Le contenu du cache est alors reconstitué au fur et à mesure des accès mémoire. L'invalidation est automatique sur les anciens GPU, elle est réalisée par le processeur de commande. Sur les GPU modernes, elle est réalisée par le programmeur, comme on va le voir dans la section immédiatement suivante.
La même chose a lieu, mais avec la mémoire unifiée. La mémoire unifiée n'implique pas de transfert DMA entre CPU et GPU, vu qu'il n'y a qu'une seule RAM unifiée. Par contre, lorsque le processeur envoie des données au GPU, par exemple des textures ou un tampon de sommets, il force le GPU à invalider ses caches.
===La cohérence des caches au niveau du processeur===
Au niveau du processeur, le problème est l'inverse de ce qui se passe avec les transferts DMA. Avec les transferts DMA, la mémoire vidéo est modifiée, mais pas les caches. Avec le processeur, c'est l'inverse. Si le processeur veut envoyer des données au GPU, il doit écrire ces données dans la mémoire RAM, avant de faire le transfert DMA. Mais la présence de caches pose problème : les écritures peuvent être interceptées par le cache et ne pas être propagées en RAM.
Pour cela, le processeur est conçu pour contourner les caches lors de certaines écritures, à savoir que ces écritures se font en RAM directement, sans passer par le cache. C'est une fonctionnalité très importante pour communiquer avec les périphériques. Les processeurs modernes sont capables de marquer des blocs de mémoire entiers comme étant non-cacheables, à savoir que toute lecture/écriture dedans se fait sans passer par le cache. Il suffit au CPU de copier les données à envoyer au GPU dans une de ces zone non-cacheable.
Pour les GPU dédiés/soudés, cela a un lien avec la mémoire vidéo mappée en mémoire. Plus haut, nous avions dit que la mémoire vidéo est visible dans l'espace d'adressage du processeur, à savoir qu'un bloc de mémoire est détourné pour adresser non pas la RAM, mais la mémoire vidéo. Et bien ce bloc de mémoire entier est marqué comme étant non-cacheable. Du côté du GPU, les échanges entre RAM système et mémoire vidéo sont le fait du contrôleur DMA. La solution est alors toute trouvée : le contrôleur DMA accède directement en mémoire vidéo et en RAM système, sans passer par les caches du GPU. Il suffit d'invalider les caches du GPU après un transfert DMA, pour que le GPU lise les données directement en RAM et le tour est joué.
La situation est gérée différemment sur les GPU intégré, vu qu'ils utilisent la mémoire unifiée. Les données ne sont pas copiée de la RAM système à la mémoire vidéo, le GPU lit directement les données en RAM, il n'y a pas de contrôleur DMA qui contournerait les caches. Si le GPU est conçu pour, il n'y a pas besoin de marquer les données comme non-cacheables. Le cas le plus simple est celui où le CPU et le GPU partagent leur cache L3/L4. Dans ce cas, il n'y a qu'un seul cache L3/L4 qui ne contient qu'une seule copie valide, écrite par le CPU et lue par le GPU. Il faut juste garantir que la donnée soit lue par le GPU depuis le L3, mais c'est là une question d'inclusivité du cache, qui ne nous concerne pas ici. Si le CPU et le GPU ne partagent pas de cache, il suffit que le GPU puisse lire les caches du CPU. C'est la méthode utilisée sur l'APU Trinity d'AMD.
===La cohérence des caches entre processeurs de shaders===
Pour terminer, il faut voir la cohérence des caches entre processeurs de shaders. Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs auquel on aurait rajouté des ROPs, les circuits de la rastérisation et les unités de textures. Il n'est donc pas étonnant que les problèmes rencontrés sur les processeurs multicœurs soient aussi présents sur les GPU, la cohérence des caches ne fait pas exception.
Pour simplifier les explications, nous allons partir du principe que chaque processeur de shaders a son propre cache de données. Prenons deux processeur de shaders qui ont chacun une copie d'une donnée dans leur cache. Si un processeur de shaders modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches.
[[File:Cohérence des caches.png|centre|vignette|upright=2|Cohérence des caches]]
La réalité est cependant plus complexe, dans le sens où il n'y a souvent pas un cache par processeur de shaders, mais une hiérarchie de cache assez complexe, avec un cache L1 par processeur de shaders, un cache L2 partagé entre plusieurs processeur de shaders, des caches partagés entre tous les processeur de shaders, etc. Certains GPU partagent leur cache L1 d’instructions entre plusieurs processeur de shaders, d'autres non. Mais le principe reste valide, tant qu'un cache n'est pas partagé entre tous les processeurs de shaders : un cache peut contenir une donnée invalide, à savoir qu'elle a été modifiée dans le cache d'un autre processeur de shaders.
Pour corriger ce problème, les ingénieurs ont inventé des '''protocoles de cohérence des caches''' pour détecter les données périmées et les mettre à jour. Mais autant ces techniques sont faisables sur des CPU avec un nombre limité de cœurs, autant elles sont impraticables avec un GPU contenant une centaine de cœurs. Heureusement, la cohérence des caches est un problème bien moins important sur les GPU que sur les CPU. En effet, le rendu 3D implique un parallélisme de données : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches.
En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même. Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisés. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur insère une instruction qui invalide le cache, avant l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo et donc qu'il s'agit d'une donnée correcte.
C'est cette technique qui est utilisée pour les caches de textures, pour supporter les techniques de ''render-to-texture''. Dans le chapitre précédent, on a vu que lorsque d'une texture est modifiée par le GPU, les caches de texture sont réinitialisés. Les opérations de ''render-to-texture'' étant assez rares, il vaut mieux faire cela que de rendre les caches de texture accesibles en écriture. Ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches du GPU sont gérés avec le même principe. Pour les caches généralistes, certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.
{{NavChapitre | book=Les cartes graphiques
| prev=La hiérarchie mémoire d'un GPU
| prevText=La hiérarchie mémoire d'un GPU
| next=Le processeur de commandes
| netxText=Le processeur de commandes
}}{{autocat}}
bfvb9cmp37sqlwya4wtirnjzew02vx0
745313
745312
2025-06-24T20:08:16Z
Mewtow
31375
/* La cohérence des caches pour les lectures GPU en mémoire vidéo */
745313
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.
==La mémoire vidéo dédiée==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
==La cohérence des caches sur un GPU==
Pour terminer ce chapitre, nous allons parler de la '''cohérence des caches'''. La cohérence des caches est un problème qui se manifeste à plusieurs niveaux, quand on parle d'un GPU. Mais la situation la plus courante est celle où le processeur échange des données avec la carte graphique.
Un ordinateur contient un processeur et un GPU, chacun avec leurs caches séparés. Le processeur ou le GPU peuvent alors modifier la donnée chacun dans leur cache. Le résultat est que la donnée en RAM, celle dans le cache du GPU et celle dans le cache du CPU, seront totalement différentes. Il y a donc une incohérence : une lecture à une adresse ne donnera pas le même résultat selon que la lecture vienne du CPU, du GPU ou d'un périphérique DMA. Alors qu'il idéalement, on voudrait que les caches soient cohérents, à savoir qu'ils contiennent tous la même copie d'une donnée, ou presque.
[[File:Cohérence des caches entre CPU et GPU avec mémoire unifiée.png|centre|vignette|upright=2|Cohérence des caches entre CPU et GPU avec mémoire unifiée]]
Le problème se manifeste aussi bien avec la mémoire unifiée qu'avec une mémoire vidéo séparée. Le schéma précédent illustre ce qui se passe avec la mémoire unifiée. Avec une mémoire vidéo séparée, il faut prendre en compte le fait que les données sont copiées de la RAM système vers la mémoire vidéo. Le cas de la mémoire unifiée est un peu à part, aussi étudions le cas des GPU dédiés avant. Sur un GPU dédié, le processeur envoie des données au GPU en trois temps :
* le CPU écrit des données en mémoire RAM ;
* les données sont copiées de la RAM vers la mémoire vidéo, via un transfert DMA ;
* les données sont lues par le GPU en mémoire vidéo.
La cohérence des caches se manifeste entre chaque étape : entre l'étape 1 et 2, entre l'étape 2 et 3. Voyons d'abord le problème entre les deux dernières étapes, à savoir une fois que le transfert DMA est terminé et que le GPU veut lire les données.
===La cohérence des caches pour les lectures GPU en mémoire vidéo===
Après un transfert DMA, une situation bien précise pose problème : quand un transfert DMA remplace des données par d'autres, à savoir qu'il écrase des données devenues inutiles, pour les remplacer par des données utiles. C'est très fréquent, les pilotes graphiques libèrent souvent de la mémoire vidéo pour la réallouer immédiatement après, afin de ne pas gaspiller de VRAM. Sans intervention du GPU, le remplacement des données aura été fait en mémoire vidéo, pas dans les caches du GPU. Et tout accès ultérieur au cache renverra la donnée écrasée.
[[File:Cache incoherence write.svg|centre|vignette|upright=2|Cohérence des caches avec DMA.]]
Pour éviter cela, le GPU invalide ses caches en cas de transfert DMA. Par invalider, on veut dire que le cache est réinitialisé, mis à zéro, il est rendu vierge de toute donnée. Ainsi, tout accès mémoire ultérieur se fera en mémoire RAM sans passer par le cache. Les données lues depuis la RAM seront ensuite copiées dans le cache, mais ce seront les données valides écrites après le transfert DMA. Le contenu du cache est alors reconstitué au fur et à mesure des accès mémoire. L'invalidation est automatique sur les anciens GPU, elle est réalisée par le processeur de commande. Sur les GPU modernes, elle est réalisée par le programmeur, comme on va le voir dans la section immédiatement suivante.
La même chose a lieu, mais avec la mémoire unifiée. La mémoire unifiée n'implique pas de transfert DMA entre CPU et GPU, vu qu'il n'y a qu'une seule RAM unifiée. Par contre, le processeur écrit directement en mémoire RAM, mais les écritures ne sont pas propagées dans les caches du GPU. Pour corriger cela, lorsque le processeur envoie des données au GPU, il force le GPU à invalider ses caches.
===La cohérence des caches au niveau du processeur===
Au niveau du processeur, le problème est l'inverse de ce qui se passe avec les transferts DMA. Avec les transferts DMA, la mémoire vidéo est modifiée, mais pas les caches. Avec le processeur, c'est l'inverse. Si le processeur veut envoyer des données au GPU, il doit écrire ces données dans la mémoire RAM, avant de faire le transfert DMA. Mais la présence de caches pose problème : les écritures peuvent être interceptées par le cache et ne pas être propagées en RAM.
Pour cela, le processeur est conçu pour contourner les caches lors de certaines écritures, à savoir que ces écritures se font en RAM directement, sans passer par le cache. C'est une fonctionnalité très importante pour communiquer avec les périphériques. Les processeurs modernes sont capables de marquer des blocs de mémoire entiers comme étant non-cacheables, à savoir que toute lecture/écriture dedans se fait sans passer par le cache. Il suffit au CPU de copier les données à envoyer au GPU dans une de ces zone non-cacheable.
Pour les GPU dédiés/soudés, cela a un lien avec la mémoire vidéo mappée en mémoire. Plus haut, nous avions dit que la mémoire vidéo est visible dans l'espace d'adressage du processeur, à savoir qu'un bloc de mémoire est détourné pour adresser non pas la RAM, mais la mémoire vidéo. Et bien ce bloc de mémoire entier est marqué comme étant non-cacheable. Du côté du GPU, les échanges entre RAM système et mémoire vidéo sont le fait du contrôleur DMA. La solution est alors toute trouvée : le contrôleur DMA accède directement en mémoire vidéo et en RAM système, sans passer par les caches du GPU. Il suffit d'invalider les caches du GPU après un transfert DMA, pour que le GPU lise les données directement en RAM et le tour est joué.
La situation est gérée différemment sur les GPU intégré, vu qu'ils utilisent la mémoire unifiée. Les données ne sont pas copiée de la RAM système à la mémoire vidéo, le GPU lit directement les données en RAM, il n'y a pas de contrôleur DMA qui contournerait les caches. Si le GPU est conçu pour, il n'y a pas besoin de marquer les données comme non-cacheables. Le cas le plus simple est celui où le CPU et le GPU partagent leur cache L3/L4. Dans ce cas, il n'y a qu'un seul cache L3/L4 qui ne contient qu'une seule copie valide, écrite par le CPU et lue par le GPU. Il faut juste garantir que la donnée soit lue par le GPU depuis le L3, mais c'est là une question d'inclusivité du cache, qui ne nous concerne pas ici. Si le CPU et le GPU ne partagent pas de cache, il suffit que le GPU puisse lire les caches du CPU. C'est la méthode utilisée sur l'APU Trinity d'AMD.
===La cohérence des caches entre processeurs de shaders===
Pour terminer, il faut voir la cohérence des caches entre processeurs de shaders. Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs auquel on aurait rajouté des ROPs, les circuits de la rastérisation et les unités de textures. Il n'est donc pas étonnant que les problèmes rencontrés sur les processeurs multicœurs soient aussi présents sur les GPU, la cohérence des caches ne fait pas exception.
Pour simplifier les explications, nous allons partir du principe que chaque processeur de shaders a son propre cache de données. Prenons deux processeur de shaders qui ont chacun une copie d'une donnée dans leur cache. Si un processeur de shaders modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches.
[[File:Cohérence des caches.png|centre|vignette|upright=2|Cohérence des caches]]
La réalité est cependant plus complexe, dans le sens où il n'y a souvent pas un cache par processeur de shaders, mais une hiérarchie de cache assez complexe, avec un cache L1 par processeur de shaders, un cache L2 partagé entre plusieurs processeur de shaders, des caches partagés entre tous les processeur de shaders, etc. Certains GPU partagent leur cache L1 d’instructions entre plusieurs processeur de shaders, d'autres non. Mais le principe reste valide, tant qu'un cache n'est pas partagé entre tous les processeurs de shaders : un cache peut contenir une donnée invalide, à savoir qu'elle a été modifiée dans le cache d'un autre processeur de shaders.
Pour corriger ce problème, les ingénieurs ont inventé des '''protocoles de cohérence des caches''' pour détecter les données périmées et les mettre à jour. Mais autant ces techniques sont faisables sur des CPU avec un nombre limité de cœurs, autant elles sont impraticables avec un GPU contenant une centaine de cœurs. Heureusement, la cohérence des caches est un problème bien moins important sur les GPU que sur les CPU. En effet, le rendu 3D implique un parallélisme de données : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches.
En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même. Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisés. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur insère une instruction qui invalide le cache, avant l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo et donc qu'il s'agit d'une donnée correcte.
C'est cette technique qui est utilisée pour les caches de textures, pour supporter les techniques de ''render-to-texture''. Dans le chapitre précédent, on a vu que lorsque d'une texture est modifiée par le GPU, les caches de texture sont réinitialisés. Les opérations de ''render-to-texture'' étant assez rares, il vaut mieux faire cela que de rendre les caches de texture accesibles en écriture. Ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches du GPU sont gérés avec le même principe. Pour les caches généralistes, certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.
{{NavChapitre | book=Les cartes graphiques
| prev=La hiérarchie mémoire d'un GPU
| prevText=La hiérarchie mémoire d'un GPU
| next=Le processeur de commandes
| netxText=Le processeur de commandes
}}{{autocat}}
975zlge6ht6lgzn70fqv405hc8ijqac
745314
745313
2025-06-24T20:09:24Z
Mewtow
31375
/* La cohérence des caches sur un GPU */
745314
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.
==La mémoire vidéo dédiée==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
==La cohérence des caches sur un GPU==
Pour terminer ce chapitre, nous allons parler de la '''cohérence des caches'''. La cohérence des caches est un problème qui se manifeste à plusieurs niveaux, quand on parle d'un GPU. Mais la situation la plus courante est celle où le processeur échange des données avec la carte graphique.
Un ordinateur contient un processeur et un GPU, chacun avec leurs caches séparés. Le processeur ou le GPU peuvent alors modifier la donnée chacun dans leur cache. Le résultat est que la donnée en RAM, celle dans le cache du GPU et celle dans le cache du CPU, seront totalement différentes. Il y a donc une incohérence : une lecture à une adresse ne donnera pas le même résultat selon que la lecture vienne du CPU, du GPU ou d'un périphérique DMA. Alors qu'il idéalement, on voudrait que les caches soient cohérents, à savoir qu'ils contiennent tous la même copie d'une donnée, ou presque.
[[File:Cohérence des caches entre CPU et GPU avec mémoire unifiée.png|centre|vignette|upright=2|Cohérence des caches entre CPU et GPU avec mémoire unifiée]]
Le problème se manifeste aussi bien avec la mémoire unifiée qu'avec une mémoire vidéo séparée. Le schéma précédent illustre ce qui se passe avec la mémoire unifiée. Avec une mémoire vidéo séparée, il faut prendre en compte le fait que les données sont copiées de la RAM système vers la mémoire vidéo. Le cas de la mémoire unifiée est un peu à part, aussi étudions le cas des GPU dédiés avant. Sur un GPU dédié, le processeur envoie des données au GPU en trois temps :
* le CPU écrit des données en mémoire RAM ;
* les données sont copiées de la RAM vers la mémoire vidéo, via un transfert DMA ;
* les données sont lues par le GPU en mémoire vidéo.
La cohérence des caches se manifeste entre chaque étape : entre l'étape 1 et 2, entre l'étape 2 et 3. Voyons d'abord le problème entre les deux dernières étapes, à savoir une fois que le transfert DMA est terminé et que le GPU veut lire les données.
===La cohérence des caches pour les lectures GPU en mémoire vidéo===
Après un transfert DMA, une situation bien précise pose problème : quand un transfert DMA remplace des données par d'autres, à savoir qu'il écrase des données devenues inutiles, pour les remplacer par des données utiles. C'est très fréquent, les pilotes graphiques libèrent souvent de la mémoire vidéo pour la réallouer immédiatement après, afin de ne pas gaspiller de VRAM. Sans intervention du GPU, le remplacement des données aura été fait en mémoire vidéo, pas dans les caches du GPU. Et tout accès ultérieur au cache renverra la donnée écrasée.
[[File:Cache incoherence write.svg|centre|vignette|upright=2|Cohérence des caches avec DMA.]]
Pour éviter cela, le GPU invalide ses caches en cas de transfert DMA. Par invalider, on veut dire que le cache est réinitialisé, mis à zéro, il est rendu vierge de toute donnée. Ainsi, tout accès mémoire ultérieur se fera en mémoire RAM sans passer par le cache. Les données lues depuis la RAM seront ensuite copiées dans le cache, mais ce seront les données valides écrites après le transfert DMA. Le contenu du cache est alors reconstitué au fur et à mesure des accès mémoire. L'invalidation est automatique sur les anciens GPU, elle est réalisée par le processeur de commande. Sur les GPU modernes, elle est réalisée par le programmeur, comme on va le voir dans la section immédiatement suivante.
La même chose a lieu, mais avec la mémoire unifiée. La mémoire unifiée n'implique pas de transfert DMA entre CPU et GPU, vu qu'il n'y a qu'une seule RAM unifiée. Par contre, le processeur écrit directement en mémoire RAM, mais les écritures ne sont pas propagées dans les caches du GPU. Pour corriger cela, lorsque le processeur envoie des données au GPU, il force le GPU à invalider ses caches.
===La cohérence des caches au niveau du processeur===
Au niveau du processeur, le problème est l'inverse de ce qui se passe avec les transferts DMA. Avec les transferts DMA, la mémoire vidéo est modifiée, mais pas les caches. Avec le processeur, c'est l'inverse. Si le processeur veut envoyer des données au GPU, il doit écrire ces données dans la mémoire RAM, avant de faire le transfert DMA. Mais la présence de caches pose problème : les écritures peuvent être interceptées par le cache et ne pas être propagées en RAM.
Pour cela, le processeur est conçu pour contourner les caches lors de certaines écritures, à savoir que ces écritures se font en RAM directement, sans passer par le cache. C'est une fonctionnalité très importante pour communiquer avec les périphériques. Les processeurs modernes sont capables de marquer des blocs de mémoire entiers comme étant non-cacheables, à savoir que toute lecture/écriture dedans se fait sans passer par le cache. Il suffit au CPU de copier les données à envoyer au GPU dans une de ces zone non-cacheable.
Pour les GPU dédiés/soudés, cela a un lien avec la mémoire vidéo mappée en mémoire. Plus haut, nous avions dit que la mémoire vidéo est visible dans l'espace d'adressage du processeur, à savoir qu'un bloc de mémoire est détourné pour adresser non pas la RAM, mais la mémoire vidéo. Et bien ce bloc de mémoire entier est marqué comme étant non-cacheable. Du côté du GPU, les échanges entre RAM système et mémoire vidéo sont le fait du contrôleur DMA. La solution est alors toute trouvée : le contrôleur DMA accède directement en mémoire vidéo et en RAM système, sans passer par les caches du GPU. Il suffit d'invalider les caches du GPU après un transfert DMA, pour que le GPU lise les données directement en RAM et le tour est joué.
La situation est gérée différemment sur les GPU intégré, vu qu'ils utilisent la mémoire unifiée. Les données ne sont pas copiée de la RAM système à la mémoire vidéo, le GPU lit directement les données en RAM, il n'y a pas de contrôleur DMA qui contournerait les caches. Si le GPU est conçu pour, il n'y a pas besoin de marquer les données comme non-cacheables. Le cas le plus simple est celui où le CPU et le GPU partagent leur cache L3/L4. Dans ce cas, il n'y a qu'un seul cache L3/L4 qui ne contient qu'une seule copie valide, écrite par le CPU et lue par le GPU. Il faut juste garantir que la donnée soit lue par le GPU depuis le L3, mais c'est là une question d'inclusivité du cache, qui ne nous concerne pas ici. Si le CPU et le GPU ne partagent pas de cache, il suffit que le GPU puisse lire les caches du CPU. C'est la méthode utilisée sur l'APU Trinity d'AMD.
===La cohérence des caches entre processeurs de shaders===
Pour terminer, il faut voir la cohérence des caches entre processeurs de shaders. Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs auquel on aurait rajouté des ROPs, les circuits de la rastérisation et les unités de textures. Il n'est donc pas étonnant que les problèmes rencontrés sur les processeurs multicœurs soient aussi présents sur les GPU, la cohérence des caches ne fait pas exception.
Pour simplifier les explications, nous allons partir du principe que chaque processeur de shaders a son propre cache de données. Prenons deux processeur de shaders qui ont chacun une copie d'une donnée dans leur cache. Si un processeur de shaders modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches.
[[File:Cohérence des caches.png|centre|vignette|upright=2|Cohérence des caches]]
La réalité est cependant plus complexe, dans le sens où il n'y a souvent pas un cache par processeur de shaders, mais une hiérarchie de cache assez complexe, avec un cache L1 par processeur de shaders, un cache L2 partagé entre plusieurs processeur de shaders, des caches partagés entre tous les processeur de shaders, etc. Certains GPU partagent leur cache L1 d’instructions entre plusieurs processeur de shaders, d'autres non. Mais le principe reste valide, tant qu'un cache n'est pas partagé entre tous les processeurs de shaders : un cache peut contenir une donnée invalide, à savoir qu'elle a été modifiée dans le cache d'un autre processeur de shaders.
Pour corriger ce problème, les ingénieurs ont inventé des '''protocoles de cohérence des caches''' pour détecter les données périmées et les mettre à jour. Mais autant ces techniques sont faisables sur des CPU avec un nombre limité de cœurs, autant elles sont impraticables avec un GPU contenant une centaine de cœurs. Heureusement, la cohérence des caches est un problème bien moins important sur les GPU que sur les CPU. En effet, le rendu 3D implique un parallélisme de données : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches.
En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même. Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisés. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur insère une instruction qui invalide le cache, avant l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo et donc qu'il s'agit d'une donnée correcte.
C'est cette technique qui est utilisée pour les caches de textures, pour supporter les techniques de ''render-to-texture''. Dans le chapitre précédent, on a vu que lorsque d'une texture est modifiée par le GPU, les caches de texture sont réinitialisés. Les opérations de ''render-to-texture'' étant assez rares, il vaut mieux faire cela que de rendre les caches de texture accesibles en écriture. Ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches du GPU sont gérés avec le même principe. Pour les caches généralistes, certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.
{{NavChapitre | book=Les cartes graphiques
| prev=La hiérarchie mémoire d'un GPU
| prevText=La hiérarchie mémoire d'un GPU
| next=Le processeur de commandes
| netxText=Le processeur de commandes
}}{{autocat}}
f1p8cxsnfoxt5yc2xell6rwm06nc6zv
745315
745314
2025-06-24T20:41:47Z
Mewtow
31375
/* La cohérence des caches sur un GPU */
745315
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.
==La mémoire vidéo dédiée==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
==La cohérence des caches sur un GPU==
Pour terminer ce chapitre, nous allons parler de la '''cohérence des caches'''. La cohérence des caches est un problème qui se manifeste à plusieurs niveaux, quand on parle d'un GPU. Mais la situation la plus courante est celle où le processeur échange des données avec la carte graphique.
Un ordinateur contient un processeur et un GPU, chacun avec leurs caches séparés. Le processeur ou le GPU peuvent alors modifier la donnée chacun dans leur cache. Le résultat est que la donnée en RAM, celle dans le cache du GPU et celle dans le cache du CPU, seront totalement différentes. Il y a donc une incohérence : une lecture à une adresse ne donnera pas le même résultat selon que la lecture vienne du CPU, du GPU ou d'un périphérique DMA. Alors qu'il idéalement, on voudrait que les caches soient cohérents, à savoir qu'ils contiennent tous la même copie d'une donnée, ou presque.
[[File:Cohérence des caches entre CPU et GPU avec mémoire unifiée.png|centre|vignette|upright=2|Cohérence des caches entre CPU et GPU avec mémoire unifiée]]
Le problème se manifeste aussi bien avec la mémoire unifiée qu'avec une mémoire vidéo séparée. Le schéma précédent illustre ce qui se passe avec la mémoire unifiée. Avec une mémoire vidéo séparée, il faut prendre en compte les échanges entre RAM système et mémoire vidéo, avec des transferts DMA.
La majorité des échanges entre CPU et GPU se font du CPU vers le GPU. Le CPU prépare des textures, un tampon de sommet et diverses informations, qu'il envoie au GPU pour que celui-ci fasse le rendu 3D. Les échanges de données dans l'autre sens sont beaucoup plus rares. Sur un GPU dédié, le processeur envoie des données au GPU en trois temps :
* le CPU écrit des données en mémoire RAM ;
* les données sont copiées de la RAM vers la mémoire vidéo, via un transfert DMA ;
* les données sont lues par le GPU en mémoire vidéo.
La seconde étape disparait avec la mémoire unifiée. La cohérence des caches se manifeste entre l'étape 1 et 2, entre l'étape 2 et 3. Voyons d'abord le problème entre les deux dernières étapes, à savoir une fois que le transfert DMA est terminé et que le GPU veut lire les données.
===La cohérence des caches pour les lectures GPU===
Après un transfert DMA, une situation bien précise pose problème : quand un transfert DMA remplace des données par d'autres, à savoir qu'il écrase des données devenues inutiles, pour les remplacer par des données utiles. C'est très fréquent, les pilotes graphiques libèrent souvent de la mémoire vidéo pour la réallouer immédiatement après, afin de ne pas gaspiller de VRAM. Sans intervention du GPU, le remplacement des données aura été fait en mémoire vidéo, pas dans les caches du GPU. Et tout accès ultérieur au cache renverra la donnée écrasée.
[[File:Cache incoherence write.svg|centre|vignette|upright=2|Cohérence des caches avec DMA.]]
Pour éviter cela, le GPU invalide ses caches en cas de transfert DMA. Par invalider, on veut dire que le cache est réinitialisé, mis à zéro, il est rendu vierge de toute donnée. Ainsi, tout accès mémoire ultérieur se fera en mémoire RAM sans passer par le cache. Les données lues depuis la RAM seront ensuite copiées dans le cache, mais ce seront les données valides écrites après le transfert DMA. Le contenu du cache est alors reconstitué au fur et à mesure des accès mémoire. L'invalidation est automatique sur les anciens GPU, elle est réalisée par le processeur de commande. Sur les GPU modernes, elle est réalisée par le programmeur, comme on va le voir dans la section immédiatement suivante.
La même chose a lieu, mais avec la mémoire unifiée. La mémoire unifiée n'implique pas de transfert DMA entre CPU et GPU, vu qu'il n'y a qu'une seule RAM unifiée. Par contre, le processeur écrit directement en mémoire RAM, mais les écritures ne sont pas propagées dans les caches du GPU. Pour corriger cela, lorsque le processeur envoie des données au GPU, il force le GPU à invalider ses caches.
===La cohérence des caches au niveau du processeur===
Au niveau du processeur, le problème est l'inverse de ce qui se passe avec les transferts DMA. Avec les transferts DMA, la mémoire vidéo est modifiée, mais pas les caches. Avec le processeur, c'est l'inverse. Si le processeur veut envoyer des données au GPU, il doit écrire ces données dans la mémoire RAM, avant de faire le transfert DMA. Mais la présence de caches pose problème : les écritures peuvent être interceptées par le cache et ne pas être propagées en RAM.
Pour cela, le processeur est conçu pour contourner les caches lors de certaines écritures, à savoir que ces écritures se font en RAM directement, sans passer par le cache. C'est une fonctionnalité très importante pour communiquer avec les périphériques. Les processeurs modernes sont capables de marquer des blocs de mémoire entiers comme étant non-cacheables, à savoir que toute lecture/écriture dedans se fait sans passer par le cache. Il suffit au CPU de copier les données à envoyer au GPU dans une de ces zone non-cacheable.
Pour les GPU dédiés/soudés, cela a un lien avec la mémoire vidéo mappée en mémoire. Plus haut, nous avions dit que la mémoire vidéo est visible dans l'espace d'adressage du processeur, à savoir qu'un bloc de mémoire est détourné pour adresser non pas la RAM, mais la mémoire vidéo. Et bien ce bloc de mémoire entier est marqué comme étant non-cacheable. Du côté du GPU, les échanges entre RAM système et mémoire vidéo sont le fait du contrôleur DMA. La solution est alors toute trouvée : le contrôleur DMA accède directement en mémoire vidéo et en RAM système, sans passer par les caches du GPU. Il suffit d'invalider les caches du GPU après un transfert DMA, pour que le GPU lise les données directement en RAM et le tour est joué.
La situation est gérée différemment sur les GPU intégré, vu qu'ils utilisent la mémoire unifiée. Les données ne sont pas copiée de la RAM système à la mémoire vidéo, le GPU lit directement les données en RAM, il n'y a pas de contrôleur DMA qui contournerait les caches. Si le GPU est conçu pour, il n'y a pas besoin de marquer les données comme non-cacheables. Le cas le plus simple est celui où le CPU et le GPU partagent leur cache L3/L4. Dans ce cas, il n'y a qu'un seul cache L3/L4 qui ne contient qu'une seule copie valide, écrite par le CPU et lue par le GPU. Il faut juste garantir que la donnée soit lue par le GPU depuis le L3, mais c'est là une question d'inclusivité du cache, qui ne nous concerne pas ici. Si le CPU et le GPU ne partagent pas de cache, il suffit que le GPU puisse lire les caches du CPU. C'est la méthode utilisée sur l'APU Trinity d'AMD.
===La cohérence des caches entre processeurs de shaders===
Pour terminer, il faut voir la cohérence des caches entre processeurs de shaders. Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs auquel on aurait rajouté des ROPs, les circuits de la rastérisation et les unités de textures. Il n'est donc pas étonnant que les problèmes rencontrés sur les processeurs multicœurs soient aussi présents sur les GPU, la cohérence des caches ne fait pas exception.
Pour simplifier les explications, nous allons partir du principe que chaque processeur de shaders a son propre cache de données. Prenons deux processeur de shaders qui ont chacun une copie d'une donnée dans leur cache. Si un processeur de shaders modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches.
[[File:Cohérence des caches.png|centre|vignette|upright=2|Cohérence des caches]]
La réalité est cependant plus complexe, dans le sens où il n'y a souvent pas un cache par processeur de shaders, mais une hiérarchie de cache assez complexe, avec un cache L1 par processeur de shaders, un cache L2 partagé entre plusieurs processeur de shaders, des caches partagés entre tous les processeur de shaders, etc. Certains GPU partagent leur cache L1 d’instructions entre plusieurs processeur de shaders, d'autres non. Mais le principe reste valide, tant qu'un cache n'est pas partagé entre tous les processeurs de shaders : un cache peut contenir une donnée invalide, à savoir qu'elle a été modifiée dans le cache d'un autre processeur de shaders.
Pour corriger ce problème, les ingénieurs ont inventé des '''protocoles de cohérence des caches''' pour détecter les données périmées et les mettre à jour. Mais autant ces techniques sont faisables sur des CPU avec un nombre limité de cœurs, autant elles sont impraticables avec un GPU contenant une centaine de cœurs. Heureusement, la cohérence des caches est un problème bien moins important sur les GPU que sur les CPU. En effet, le rendu 3D implique un parallélisme de données : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches.
En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même. Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisés. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur insère une instruction qui invalide le cache, avant l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo et donc qu'il s'agit d'une donnée correcte.
C'est cette technique qui est utilisée pour les caches de textures, pour supporter les techniques de ''render-to-texture''. Dans le chapitre précédent, on a vu que lorsque d'une texture est modifiée par le GPU, les caches de texture sont réinitialisés. Les opérations de ''render-to-texture'' étant assez rares, il vaut mieux faire cela que de rendre les caches de texture accesibles en écriture. Ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches du GPU sont gérés avec le même principe. Pour les caches généralistes, certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.
{{NavChapitre | book=Les cartes graphiques
| prev=La hiérarchie mémoire d'un GPU
| prevText=La hiérarchie mémoire d'un GPU
| next=Le processeur de commandes
| netxText=Le processeur de commandes
}}{{autocat}}
cza0bi36u4ms6piu8ewv4618k4b04ed
745316
745315
2025-06-24T20:42:32Z
Mewtow
31375
/* La cohérence des caches au niveau du processeur */
745316
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.
==La mémoire vidéo dédiée==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
==La cohérence des caches sur un GPU==
Pour terminer ce chapitre, nous allons parler de la '''cohérence des caches'''. La cohérence des caches est un problème qui se manifeste à plusieurs niveaux, quand on parle d'un GPU. Mais la situation la plus courante est celle où le processeur échange des données avec la carte graphique.
Un ordinateur contient un processeur et un GPU, chacun avec leurs caches séparés. Le processeur ou le GPU peuvent alors modifier la donnée chacun dans leur cache. Le résultat est que la donnée en RAM, celle dans le cache du GPU et celle dans le cache du CPU, seront totalement différentes. Il y a donc une incohérence : une lecture à une adresse ne donnera pas le même résultat selon que la lecture vienne du CPU, du GPU ou d'un périphérique DMA. Alors qu'il idéalement, on voudrait que les caches soient cohérents, à savoir qu'ils contiennent tous la même copie d'une donnée, ou presque.
[[File:Cohérence des caches entre CPU et GPU avec mémoire unifiée.png|centre|vignette|upright=2|Cohérence des caches entre CPU et GPU avec mémoire unifiée]]
Le problème se manifeste aussi bien avec la mémoire unifiée qu'avec une mémoire vidéo séparée. Le schéma précédent illustre ce qui se passe avec la mémoire unifiée. Avec une mémoire vidéo séparée, il faut prendre en compte les échanges entre RAM système et mémoire vidéo, avec des transferts DMA.
La majorité des échanges entre CPU et GPU se font du CPU vers le GPU. Le CPU prépare des textures, un tampon de sommet et diverses informations, qu'il envoie au GPU pour que celui-ci fasse le rendu 3D. Les échanges de données dans l'autre sens sont beaucoup plus rares. Sur un GPU dédié, le processeur envoie des données au GPU en trois temps :
* le CPU écrit des données en mémoire RAM ;
* les données sont copiées de la RAM vers la mémoire vidéo, via un transfert DMA ;
* les données sont lues par le GPU en mémoire vidéo.
La seconde étape disparait avec la mémoire unifiée. La cohérence des caches se manifeste entre l'étape 1 et 2, entre l'étape 2 et 3. Voyons d'abord le problème entre les deux dernières étapes, à savoir une fois que le transfert DMA est terminé et que le GPU veut lire les données.
===La cohérence des caches pour les lectures GPU===
Après un transfert DMA, une situation bien précise pose problème : quand un transfert DMA remplace des données par d'autres, à savoir qu'il écrase des données devenues inutiles, pour les remplacer par des données utiles. C'est très fréquent, les pilotes graphiques libèrent souvent de la mémoire vidéo pour la réallouer immédiatement après, afin de ne pas gaspiller de VRAM. Sans intervention du GPU, le remplacement des données aura été fait en mémoire vidéo, pas dans les caches du GPU. Et tout accès ultérieur au cache renverra la donnée écrasée.
[[File:Cache incoherence write.svg|centre|vignette|upright=2|Cohérence des caches avec DMA.]]
Pour éviter cela, le GPU invalide ses caches en cas de transfert DMA. Par invalider, on veut dire que le cache est réinitialisé, mis à zéro, il est rendu vierge de toute donnée. Ainsi, tout accès mémoire ultérieur se fera en mémoire RAM sans passer par le cache. Les données lues depuis la RAM seront ensuite copiées dans le cache, mais ce seront les données valides écrites après le transfert DMA. Le contenu du cache est alors reconstitué au fur et à mesure des accès mémoire. L'invalidation est automatique sur les anciens GPU, elle est réalisée par le processeur de commande. Sur les GPU modernes, elle est réalisée par le programmeur, comme on va le voir dans la section immédiatement suivante.
La même chose a lieu, mais avec la mémoire unifiée. La mémoire unifiée n'implique pas de transfert DMA entre CPU et GPU, vu qu'il n'y a qu'une seule RAM unifiée. Par contre, le processeur écrit directement en mémoire RAM, mais les écritures ne sont pas propagées dans les caches du GPU. Pour corriger cela, lorsque le processeur envoie des données au GPU, il force le GPU à invalider ses caches.
===La cohérence des caches pour les écritures/lectures du processeur===
Au niveau du processeur, le problème est l'inverse de ce qui se passe avec les transferts DMA. Avec les transferts DMA, la mémoire vidéo est modifiée, mais pas les caches. Avec le processeur, c'est l'inverse. Si le processeur veut envoyer des données au GPU, il doit écrire ces données dans la mémoire RAM, avant de faire le transfert DMA. Mais la présence de caches pose problème : les écritures peuvent être interceptées par le cache et ne pas être propagées en RAM.
Pour cela, le processeur est conçu pour contourner les caches lors de certaines écritures, à savoir que ces écritures se font en RAM directement, sans passer par le cache. C'est une fonctionnalité très importante pour communiquer avec les périphériques. Les processeurs modernes sont capables de marquer des blocs de mémoire entiers comme étant non-cacheables, à savoir que toute lecture/écriture dedans se fait sans passer par le cache. Il suffit au CPU de copier les données à envoyer au GPU dans une de ces zone non-cacheable.
Pour les GPU dédiés/soudés, cela a un lien avec la mémoire vidéo mappée en mémoire. Plus haut, nous avions dit que la mémoire vidéo est visible dans l'espace d'adressage du processeur, à savoir qu'un bloc de mémoire est détourné pour adresser non pas la RAM, mais la mémoire vidéo. Et bien ce bloc de mémoire entier est marqué comme étant non-cacheable. Du côté du GPU, les échanges entre RAM système et mémoire vidéo sont le fait du contrôleur DMA. La solution est alors toute trouvée : le contrôleur DMA accède directement en mémoire vidéo et en RAM système, sans passer par les caches du GPU. Il suffit d'invalider les caches du GPU après un transfert DMA, pour que le GPU lise les données directement en RAM et le tour est joué.
La situation est gérée différemment sur les GPU intégré, vu qu'ils utilisent la mémoire unifiée. Les données ne sont pas copiée de la RAM système à la mémoire vidéo, le GPU lit directement les données en RAM, il n'y a pas de contrôleur DMA qui contournerait les caches. Si le GPU est conçu pour, il n'y a pas besoin de marquer les données comme non-cacheables. Le cas le plus simple est celui où le CPU et le GPU partagent leur cache L3/L4. Dans ce cas, il n'y a qu'un seul cache L3/L4 qui ne contient qu'une seule copie valide, écrite par le CPU et lue par le GPU. Il faut juste garantir que la donnée soit lue par le GPU depuis le L3, mais c'est là une question d'inclusivité du cache, qui ne nous concerne pas ici. Si le CPU et le GPU ne partagent pas de cache, il suffit que le GPU puisse lire les caches du CPU. C'est la méthode utilisée sur l'APU Trinity d'AMD.
===La cohérence des caches entre processeurs de shaders===
Pour terminer, il faut voir la cohérence des caches entre processeurs de shaders. Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs auquel on aurait rajouté des ROPs, les circuits de la rastérisation et les unités de textures. Il n'est donc pas étonnant que les problèmes rencontrés sur les processeurs multicœurs soient aussi présents sur les GPU, la cohérence des caches ne fait pas exception.
Pour simplifier les explications, nous allons partir du principe que chaque processeur de shaders a son propre cache de données. Prenons deux processeur de shaders qui ont chacun une copie d'une donnée dans leur cache. Si un processeur de shaders modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches.
[[File:Cohérence des caches.png|centre|vignette|upright=2|Cohérence des caches]]
La réalité est cependant plus complexe, dans le sens où il n'y a souvent pas un cache par processeur de shaders, mais une hiérarchie de cache assez complexe, avec un cache L1 par processeur de shaders, un cache L2 partagé entre plusieurs processeur de shaders, des caches partagés entre tous les processeur de shaders, etc. Certains GPU partagent leur cache L1 d’instructions entre plusieurs processeur de shaders, d'autres non. Mais le principe reste valide, tant qu'un cache n'est pas partagé entre tous les processeurs de shaders : un cache peut contenir une donnée invalide, à savoir qu'elle a été modifiée dans le cache d'un autre processeur de shaders.
Pour corriger ce problème, les ingénieurs ont inventé des '''protocoles de cohérence des caches''' pour détecter les données périmées et les mettre à jour. Mais autant ces techniques sont faisables sur des CPU avec un nombre limité de cœurs, autant elles sont impraticables avec un GPU contenant une centaine de cœurs. Heureusement, la cohérence des caches est un problème bien moins important sur les GPU que sur les CPU. En effet, le rendu 3D implique un parallélisme de données : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches.
En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même. Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisés. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur insère une instruction qui invalide le cache, avant l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo et donc qu'il s'agit d'une donnée correcte.
C'est cette technique qui est utilisée pour les caches de textures, pour supporter les techniques de ''render-to-texture''. Dans le chapitre précédent, on a vu que lorsque d'une texture est modifiée par le GPU, les caches de texture sont réinitialisés. Les opérations de ''render-to-texture'' étant assez rares, il vaut mieux faire cela que de rendre les caches de texture accesibles en écriture. Ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches du GPU sont gérés avec le même principe. Pour les caches généralistes, certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.
{{NavChapitre | book=Les cartes graphiques
| prev=La hiérarchie mémoire d'un GPU
| prevText=La hiérarchie mémoire d'un GPU
| next=Le processeur de commandes
| netxText=Le processeur de commandes
}}{{autocat}}
musxcm9o66be0m2w11idwioam3mky9g
745317
745316
2025-06-24T20:43:12Z
Mewtow
31375
/* La cohérence des caches pour les lectures GPU */
745317
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.
==La mémoire vidéo dédiée==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
==La cohérence des caches sur un GPU==
Pour terminer ce chapitre, nous allons parler de la '''cohérence des caches'''. La cohérence des caches est un problème qui se manifeste à plusieurs niveaux, quand on parle d'un GPU. Mais la situation la plus courante est celle où le processeur échange des données avec la carte graphique.
Un ordinateur contient un processeur et un GPU, chacun avec leurs caches séparés. Le processeur ou le GPU peuvent alors modifier la donnée chacun dans leur cache. Le résultat est que la donnée en RAM, celle dans le cache du GPU et celle dans le cache du CPU, seront totalement différentes. Il y a donc une incohérence : une lecture à une adresse ne donnera pas le même résultat selon que la lecture vienne du CPU, du GPU ou d'un périphérique DMA. Alors qu'il idéalement, on voudrait que les caches soient cohérents, à savoir qu'ils contiennent tous la même copie d'une donnée, ou presque.
[[File:Cohérence des caches entre CPU et GPU avec mémoire unifiée.png|centre|vignette|upright=2|Cohérence des caches entre CPU et GPU avec mémoire unifiée]]
Le problème se manifeste aussi bien avec la mémoire unifiée qu'avec une mémoire vidéo séparée. Le schéma précédent illustre ce qui se passe avec la mémoire unifiée. Avec une mémoire vidéo séparée, il faut prendre en compte les échanges entre RAM système et mémoire vidéo, avec des transferts DMA.
La majorité des échanges entre CPU et GPU se font du CPU vers le GPU. Le CPU prépare des textures, un tampon de sommet et diverses informations, qu'il envoie au GPU pour que celui-ci fasse le rendu 3D. Les échanges de données dans l'autre sens sont beaucoup plus rares. Sur un GPU dédié, le processeur envoie des données au GPU en trois temps :
* le CPU écrit des données en mémoire RAM ;
* les données sont copiées de la RAM vers la mémoire vidéo, via un transfert DMA ;
* les données sont lues par le GPU en mémoire vidéo.
La seconde étape disparait avec la mémoire unifiée. La cohérence des caches se manifeste entre l'étape 1 et 2, entre l'étape 2 et 3. Voyons d'abord le problème entre les deux dernières étapes, à savoir une fois que le transfert DMA est terminé et que le GPU veut lire les données.
===La cohérence des caches pour les lectures GPU===
Après un transfert DMA, une situation bien précise pose problème : quand un transfert DMA remplace des données par d'autres, à savoir qu'il écrase des données devenues inutiles, pour les remplacer par des données utiles. C'est très fréquent, les pilotes graphiques libèrent souvent de la mémoire vidéo pour la réallouer immédiatement après, afin de ne pas gaspiller de VRAM. Sans intervention du GPU, le remplacement des données aura été fait en mémoire vidéo, pas dans les caches du GPU. Et tout accès ultérieur au cache renverra la donnée écrasée.
[[File:Cache incoherence write.svg|centre|vignette|upright=2|Cohérence des caches avec DMA.]]
Pour éviter cela, le GPU invalide ses caches en cas de transfert DMA. Par invalider, on veut dire que le cache est réinitialisé, mis à zéro, il est rendu vierge de toute donnée. Ainsi, tout accès mémoire ultérieur se fera en mémoire RAM sans passer par le cache. Les données lues depuis la RAM seront ensuite copiées dans le cache, mais ce seront les données valides écrites après le transfert DMA. Le contenu du cache est alors reconstitué au fur et à mesure des accès mémoire. L'invalidation est automatique sur les anciens GPU, elle est réalisée par le processeur de commande. Sur les GPU modernes, elle est réalisée par le programmeur, comme on va le voir dans la section immédiatement suivante.
La même chose a lieu, mais avec la mémoire unifiée. La mémoire unifiée n'implique pas de transfert DMA entre CPU et GPU, vu qu'il n'y a qu'une seule RAM unifiée. Par contre, le processeur écrit directement en mémoire RAM, mais les écritures ne sont pas propagées dans les caches du GPU. Pour corriger cela, lorsque le processeur envoie des données au GPU, il force le GPU à invalider ses caches. Il envoie une commande dédiée pour, qui précède les commandes liées au rendu 2D/3D/autres.
===La cohérence des caches pour les écritures/lectures du processeur===
Au niveau du processeur, le problème est l'inverse de ce qui se passe avec les transferts DMA. Avec les transferts DMA, la mémoire vidéo est modifiée, mais pas les caches. Avec le processeur, c'est l'inverse. Si le processeur veut envoyer des données au GPU, il doit écrire ces données dans la mémoire RAM, avant de faire le transfert DMA. Mais la présence de caches pose problème : les écritures peuvent être interceptées par le cache et ne pas être propagées en RAM.
Pour cela, le processeur est conçu pour contourner les caches lors de certaines écritures, à savoir que ces écritures se font en RAM directement, sans passer par le cache. C'est une fonctionnalité très importante pour communiquer avec les périphériques. Les processeurs modernes sont capables de marquer des blocs de mémoire entiers comme étant non-cacheables, à savoir que toute lecture/écriture dedans se fait sans passer par le cache. Il suffit au CPU de copier les données à envoyer au GPU dans une de ces zone non-cacheable.
Pour les GPU dédiés/soudés, cela a un lien avec la mémoire vidéo mappée en mémoire. Plus haut, nous avions dit que la mémoire vidéo est visible dans l'espace d'adressage du processeur, à savoir qu'un bloc de mémoire est détourné pour adresser non pas la RAM, mais la mémoire vidéo. Et bien ce bloc de mémoire entier est marqué comme étant non-cacheable. Du côté du GPU, les échanges entre RAM système et mémoire vidéo sont le fait du contrôleur DMA. La solution est alors toute trouvée : le contrôleur DMA accède directement en mémoire vidéo et en RAM système, sans passer par les caches du GPU. Il suffit d'invalider les caches du GPU après un transfert DMA, pour que le GPU lise les données directement en RAM et le tour est joué.
La situation est gérée différemment sur les GPU intégré, vu qu'ils utilisent la mémoire unifiée. Les données ne sont pas copiée de la RAM système à la mémoire vidéo, le GPU lit directement les données en RAM, il n'y a pas de contrôleur DMA qui contournerait les caches. Si le GPU est conçu pour, il n'y a pas besoin de marquer les données comme non-cacheables. Le cas le plus simple est celui où le CPU et le GPU partagent leur cache L3/L4. Dans ce cas, il n'y a qu'un seul cache L3/L4 qui ne contient qu'une seule copie valide, écrite par le CPU et lue par le GPU. Il faut juste garantir que la donnée soit lue par le GPU depuis le L3, mais c'est là une question d'inclusivité du cache, qui ne nous concerne pas ici. Si le CPU et le GPU ne partagent pas de cache, il suffit que le GPU puisse lire les caches du CPU. C'est la méthode utilisée sur l'APU Trinity d'AMD.
===La cohérence des caches entre processeurs de shaders===
Pour terminer, il faut voir la cohérence des caches entre processeurs de shaders. Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs auquel on aurait rajouté des ROPs, les circuits de la rastérisation et les unités de textures. Il n'est donc pas étonnant que les problèmes rencontrés sur les processeurs multicœurs soient aussi présents sur les GPU, la cohérence des caches ne fait pas exception.
Pour simplifier les explications, nous allons partir du principe que chaque processeur de shaders a son propre cache de données. Prenons deux processeur de shaders qui ont chacun une copie d'une donnée dans leur cache. Si un processeur de shaders modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches.
[[File:Cohérence des caches.png|centre|vignette|upright=2|Cohérence des caches]]
La réalité est cependant plus complexe, dans le sens où il n'y a souvent pas un cache par processeur de shaders, mais une hiérarchie de cache assez complexe, avec un cache L1 par processeur de shaders, un cache L2 partagé entre plusieurs processeur de shaders, des caches partagés entre tous les processeur de shaders, etc. Certains GPU partagent leur cache L1 d’instructions entre plusieurs processeur de shaders, d'autres non. Mais le principe reste valide, tant qu'un cache n'est pas partagé entre tous les processeurs de shaders : un cache peut contenir une donnée invalide, à savoir qu'elle a été modifiée dans le cache d'un autre processeur de shaders.
Pour corriger ce problème, les ingénieurs ont inventé des '''protocoles de cohérence des caches''' pour détecter les données périmées et les mettre à jour. Mais autant ces techniques sont faisables sur des CPU avec un nombre limité de cœurs, autant elles sont impraticables avec un GPU contenant une centaine de cœurs. Heureusement, la cohérence des caches est un problème bien moins important sur les GPU que sur les CPU. En effet, le rendu 3D implique un parallélisme de données : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches.
En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même. Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisés. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur insère une instruction qui invalide le cache, avant l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo et donc qu'il s'agit d'une donnée correcte.
C'est cette technique qui est utilisée pour les caches de textures, pour supporter les techniques de ''render-to-texture''. Dans le chapitre précédent, on a vu que lorsque d'une texture est modifiée par le GPU, les caches de texture sont réinitialisés. Les opérations de ''render-to-texture'' étant assez rares, il vaut mieux faire cela que de rendre les caches de texture accesibles en écriture. Ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches du GPU sont gérés avec le même principe. Pour les caches généralistes, certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.
{{NavChapitre | book=Les cartes graphiques
| prev=La hiérarchie mémoire d'un GPU
| prevText=La hiérarchie mémoire d'un GPU
| next=Le processeur de commandes
| netxText=Le processeur de commandes
}}{{autocat}}
n6hjy6onh325gzflgzx1ucl309lmbe9
745318
745317
2025-06-24T20:44:04Z
Mewtow
31375
/* La cohérence des caches pour les écritures/lectures du processeur */
745318
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.
==La mémoire vidéo dédiée==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
==La cohérence des caches sur un GPU==
Pour terminer ce chapitre, nous allons parler de la '''cohérence des caches'''. La cohérence des caches est un problème qui se manifeste à plusieurs niveaux, quand on parle d'un GPU. Mais la situation la plus courante est celle où le processeur échange des données avec la carte graphique.
Un ordinateur contient un processeur et un GPU, chacun avec leurs caches séparés. Le processeur ou le GPU peuvent alors modifier la donnée chacun dans leur cache. Le résultat est que la donnée en RAM, celle dans le cache du GPU et celle dans le cache du CPU, seront totalement différentes. Il y a donc une incohérence : une lecture à une adresse ne donnera pas le même résultat selon que la lecture vienne du CPU, du GPU ou d'un périphérique DMA. Alors qu'il idéalement, on voudrait que les caches soient cohérents, à savoir qu'ils contiennent tous la même copie d'une donnée, ou presque.
[[File:Cohérence des caches entre CPU et GPU avec mémoire unifiée.png|centre|vignette|upright=2|Cohérence des caches entre CPU et GPU avec mémoire unifiée]]
Le problème se manifeste aussi bien avec la mémoire unifiée qu'avec une mémoire vidéo séparée. Le schéma précédent illustre ce qui se passe avec la mémoire unifiée. Avec une mémoire vidéo séparée, il faut prendre en compte les échanges entre RAM système et mémoire vidéo, avec des transferts DMA.
La majorité des échanges entre CPU et GPU se font du CPU vers le GPU. Le CPU prépare des textures, un tampon de sommet et diverses informations, qu'il envoie au GPU pour que celui-ci fasse le rendu 3D. Les échanges de données dans l'autre sens sont beaucoup plus rares. Sur un GPU dédié, le processeur envoie des données au GPU en trois temps :
* le CPU écrit des données en mémoire RAM ;
* les données sont copiées de la RAM vers la mémoire vidéo, via un transfert DMA ;
* les données sont lues par le GPU en mémoire vidéo.
La seconde étape disparait avec la mémoire unifiée. La cohérence des caches se manifeste entre l'étape 1 et 2, entre l'étape 2 et 3. Voyons d'abord le problème entre les deux dernières étapes, à savoir une fois que le transfert DMA est terminé et que le GPU veut lire les données.
===La cohérence des caches pour les lectures GPU===
Après un transfert DMA, une situation bien précise pose problème : quand un transfert DMA remplace des données par d'autres, à savoir qu'il écrase des données devenues inutiles, pour les remplacer par des données utiles. C'est très fréquent, les pilotes graphiques libèrent souvent de la mémoire vidéo pour la réallouer immédiatement après, afin de ne pas gaspiller de VRAM. Sans intervention du GPU, le remplacement des données aura été fait en mémoire vidéo, pas dans les caches du GPU. Et tout accès ultérieur au cache renverra la donnée écrasée.
[[File:Cache incoherence write.svg|centre|vignette|upright=2|Cohérence des caches avec DMA.]]
Pour éviter cela, le GPU invalide ses caches en cas de transfert DMA. Par invalider, on veut dire que le cache est réinitialisé, mis à zéro, il est rendu vierge de toute donnée. Ainsi, tout accès mémoire ultérieur se fera en mémoire RAM sans passer par le cache. Les données lues depuis la RAM seront ensuite copiées dans le cache, mais ce seront les données valides écrites après le transfert DMA. Le contenu du cache est alors reconstitué au fur et à mesure des accès mémoire. L'invalidation est automatique sur les anciens GPU, elle est réalisée par le processeur de commande. Sur les GPU modernes, elle est réalisée par le programmeur, comme on va le voir dans la section immédiatement suivante.
La même chose a lieu, mais avec la mémoire unifiée. La mémoire unifiée n'implique pas de transfert DMA entre CPU et GPU, vu qu'il n'y a qu'une seule RAM unifiée. Par contre, le processeur écrit directement en mémoire RAM, mais les écritures ne sont pas propagées dans les caches du GPU. Pour corriger cela, lorsque le processeur envoie des données au GPU, il force le GPU à invalider ses caches. Il envoie une commande dédiée pour, qui précède les commandes liées au rendu 2D/3D/autres.
===La cohérence des caches pour les écritures/lectures du processeur===
Au niveau du processeur, le problème est l'inverse de ce qui se passe avec les transferts DMA. Avec les transferts DMA, la mémoire vidéo est modifiée, mais pas les caches. Avec le processeur, c'est l'inverse. Si le processeur veut envoyer des données au GPU, il doit écrire ces données dans la mémoire RAM, avant de faire le transfert DMA. Mais la présence de caches pose problème : les écritures peuvent être interceptées par le cache et ne pas être propagées en RAM.
Pour cela, le processeur est conçu pour contourner les caches lors de certaines écritures, à savoir que ces écritures se font en RAM directement, sans passer par le cache. C'est une fonctionnalité très importante pour communiquer avec les périphériques. Les processeurs modernes sont capables de marquer des blocs de mémoire entiers comme étant non-cacheables, à savoir que toute lecture/écriture dedans se fait sans passer par le cache. Il suffit au CPU de copier les données à envoyer au GPU dans une de ces zone non-cacheable.
Pour les GPU dédiés/soudés, cela a un lien avec la mémoire vidéo mappée en mémoire. Plus haut, nous avions dit que la mémoire vidéo est visible dans l'espace d'adressage du processeur, à savoir qu'un bloc de mémoire est détourné pour adresser non pas la RAM, mais la mémoire vidéo. Et bien ce bloc de mémoire entier est marqué comme étant non-cacheable.
La situation est gérée différemment sur les GPU intégré, vu qu'ils utilisent la mémoire unifiée. Les données ne sont pas copiée de la RAM système à la mémoire vidéo, le GPU lit directement les données en RAM, il n'y a pas de contrôleur DMA qui contournerait les caches. Si le GPU est conçu pour, il n'y a pas besoin de marquer les données comme non-cacheables. Le cas le plus simple est celui où le CPU et le GPU partagent leur cache L3/L4. Dans ce cas, il n'y a qu'un seul cache L3/L4 qui ne contient qu'une seule copie valide, écrite par le CPU et lue par le GPU. Il faut juste garantir que la donnée soit lue par le GPU depuis le L3, mais c'est là une question d'inclusivité du cache, qui ne nous concerne pas ici. Si le CPU et le GPU ne partagent pas de cache, il suffit que le GPU puisse lire les caches du CPU. C'est la méthode utilisée sur l'APU Trinity d'AMD.
===La cohérence des caches entre processeurs de shaders===
Pour terminer, il faut voir la cohérence des caches entre processeurs de shaders. Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs auquel on aurait rajouté des ROPs, les circuits de la rastérisation et les unités de textures. Il n'est donc pas étonnant que les problèmes rencontrés sur les processeurs multicœurs soient aussi présents sur les GPU, la cohérence des caches ne fait pas exception.
Pour simplifier les explications, nous allons partir du principe que chaque processeur de shaders a son propre cache de données. Prenons deux processeur de shaders qui ont chacun une copie d'une donnée dans leur cache. Si un processeur de shaders modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches.
[[File:Cohérence des caches.png|centre|vignette|upright=2|Cohérence des caches]]
La réalité est cependant plus complexe, dans le sens où il n'y a souvent pas un cache par processeur de shaders, mais une hiérarchie de cache assez complexe, avec un cache L1 par processeur de shaders, un cache L2 partagé entre plusieurs processeur de shaders, des caches partagés entre tous les processeur de shaders, etc. Certains GPU partagent leur cache L1 d’instructions entre plusieurs processeur de shaders, d'autres non. Mais le principe reste valide, tant qu'un cache n'est pas partagé entre tous les processeurs de shaders : un cache peut contenir une donnée invalide, à savoir qu'elle a été modifiée dans le cache d'un autre processeur de shaders.
Pour corriger ce problème, les ingénieurs ont inventé des '''protocoles de cohérence des caches''' pour détecter les données périmées et les mettre à jour. Mais autant ces techniques sont faisables sur des CPU avec un nombre limité de cœurs, autant elles sont impraticables avec un GPU contenant une centaine de cœurs. Heureusement, la cohérence des caches est un problème bien moins important sur les GPU que sur les CPU. En effet, le rendu 3D implique un parallélisme de données : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches.
En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même. Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisés. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur insère une instruction qui invalide le cache, avant l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo et donc qu'il s'agit d'une donnée correcte.
C'est cette technique qui est utilisée pour les caches de textures, pour supporter les techniques de ''render-to-texture''. Dans le chapitre précédent, on a vu que lorsque d'une texture est modifiée par le GPU, les caches de texture sont réinitialisés. Les opérations de ''render-to-texture'' étant assez rares, il vaut mieux faire cela que de rendre les caches de texture accesibles en écriture. Ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches du GPU sont gérés avec le même principe. Pour les caches généralistes, certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.
{{NavChapitre | book=Les cartes graphiques
| prev=La hiérarchie mémoire d'un GPU
| prevText=La hiérarchie mémoire d'un GPU
| next=Le processeur de commandes
| netxText=Le processeur de commandes
}}{{autocat}}
4m5lgpqd9b2t7n2ibs9tw532f2vcemc
745319
745318
2025-06-24T20:44:56Z
Mewtow
31375
/* La cohérence des caches pour les écritures/lectures du processeur */
745319
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.
==La mémoire vidéo dédiée==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
==La cohérence des caches sur un GPU==
Pour terminer ce chapitre, nous allons parler de la '''cohérence des caches'''. La cohérence des caches est un problème qui se manifeste à plusieurs niveaux, quand on parle d'un GPU. Mais la situation la plus courante est celle où le processeur échange des données avec la carte graphique.
Un ordinateur contient un processeur et un GPU, chacun avec leurs caches séparés. Le processeur ou le GPU peuvent alors modifier la donnée chacun dans leur cache. Le résultat est que la donnée en RAM, celle dans le cache du GPU et celle dans le cache du CPU, seront totalement différentes. Il y a donc une incohérence : une lecture à une adresse ne donnera pas le même résultat selon que la lecture vienne du CPU, du GPU ou d'un périphérique DMA. Alors qu'il idéalement, on voudrait que les caches soient cohérents, à savoir qu'ils contiennent tous la même copie d'une donnée, ou presque.
[[File:Cohérence des caches entre CPU et GPU avec mémoire unifiée.png|centre|vignette|upright=2|Cohérence des caches entre CPU et GPU avec mémoire unifiée]]
Le problème se manifeste aussi bien avec la mémoire unifiée qu'avec une mémoire vidéo séparée. Le schéma précédent illustre ce qui se passe avec la mémoire unifiée. Avec une mémoire vidéo séparée, il faut prendre en compte les échanges entre RAM système et mémoire vidéo, avec des transferts DMA.
La majorité des échanges entre CPU et GPU se font du CPU vers le GPU. Le CPU prépare des textures, un tampon de sommet et diverses informations, qu'il envoie au GPU pour que celui-ci fasse le rendu 3D. Les échanges de données dans l'autre sens sont beaucoup plus rares. Sur un GPU dédié, le processeur envoie des données au GPU en trois temps :
* le CPU écrit des données en mémoire RAM ;
* les données sont copiées de la RAM vers la mémoire vidéo, via un transfert DMA ;
* les données sont lues par le GPU en mémoire vidéo.
La seconde étape disparait avec la mémoire unifiée. La cohérence des caches se manifeste entre l'étape 1 et 2, entre l'étape 2 et 3. Voyons d'abord le problème entre les deux dernières étapes, à savoir une fois que le transfert DMA est terminé et que le GPU veut lire les données.
===La cohérence des caches pour les lectures GPU===
Après un transfert DMA, une situation bien précise pose problème : quand un transfert DMA remplace des données par d'autres, à savoir qu'il écrase des données devenues inutiles, pour les remplacer par des données utiles. C'est très fréquent, les pilotes graphiques libèrent souvent de la mémoire vidéo pour la réallouer immédiatement après, afin de ne pas gaspiller de VRAM. Sans intervention du GPU, le remplacement des données aura été fait en mémoire vidéo, pas dans les caches du GPU. Et tout accès ultérieur au cache renverra la donnée écrasée.
[[File:Cache incoherence write.svg|centre|vignette|upright=2|Cohérence des caches avec DMA.]]
Pour éviter cela, le GPU invalide ses caches en cas de transfert DMA. Par invalider, on veut dire que le cache est réinitialisé, mis à zéro, il est rendu vierge de toute donnée. Ainsi, tout accès mémoire ultérieur se fera en mémoire RAM sans passer par le cache. Les données lues depuis la RAM seront ensuite copiées dans le cache, mais ce seront les données valides écrites après le transfert DMA. Le contenu du cache est alors reconstitué au fur et à mesure des accès mémoire. L'invalidation est automatique sur les anciens GPU, elle est réalisée par le processeur de commande. Sur les GPU modernes, elle est réalisée par le programmeur, comme on va le voir dans la section immédiatement suivante.
La même chose a lieu, mais avec la mémoire unifiée. La mémoire unifiée n'implique pas de transfert DMA entre CPU et GPU, vu qu'il n'y a qu'une seule RAM unifiée. Par contre, le processeur écrit directement en mémoire RAM, mais les écritures ne sont pas propagées dans les caches du GPU. Pour corriger cela, lorsque le processeur envoie des données au GPU, il force le GPU à invalider ses caches. Il envoie une commande dédiée pour, qui précède les commandes liées au rendu 2D/3D/autres.
===La cohérence des caches pour les écritures/lectures du processeur===
Au niveau du processeur, le problème est l'inverse de ce qui se passe avec les transferts DMA. Avec les transferts DMA, la mémoire vidéo est modifiée, mais pas les caches. Avec le processeur, c'est l'inverse. Si le processeur veut envoyer des données au GPU, il doit écrire ces données dans la mémoire RAM, avant de faire le transfert DMA. Mais la présence de caches pose problème : les écritures peuvent être interceptées par le cache et ne pas être propagées en RAM.
Les processeurs modernes sont capables de marquer des blocs de mémoire entiers comme étant non-cacheables, à savoir que toute lecture/écriture dedans se fait sans passer par le cache. C'est une fonctionnalité très importante pour communiquer avec les périphériques. Pour les GPU dédiés/soudés, cela a un lien avec la mémoire vidéo mappée en mémoire. Plus haut, nous avions dit que la mémoire vidéo est visible dans l'espace d'adressage du processeur, à savoir qu'un bloc de mémoire est détourné pour adresser non pas la RAM, mais la mémoire vidéo. Et bien ce bloc de mémoire entier est marqué comme étant non-cacheable.
La situation est gérée différemment sur les GPU intégré, vu qu'ils utilisent la mémoire unifiée. Les données ne sont pas copiée de la RAM système à la mémoire vidéo, le GPU lit directement les données en RAM, il n'y a pas de contrôleur DMA qui contournerait les caches. Si le GPU est conçu pour, il n'y a pas besoin de marquer les données comme non-cacheables. Le cas le plus simple est celui où le CPU et le GPU partagent leur cache L3/L4. Dans ce cas, il n'y a qu'un seul cache L3/L4 qui ne contient qu'une seule copie valide, écrite par le CPU et lue par le GPU. Il faut juste garantir que la donnée soit lue par le GPU depuis le L3, mais c'est là une question d'inclusivité du cache, qui ne nous concerne pas ici. Si le CPU et le GPU ne partagent pas de cache, il suffit que le GPU puisse lire les caches du CPU. C'est la méthode utilisée sur l'APU Trinity d'AMD.
===La cohérence des caches entre processeurs de shaders===
Pour terminer, il faut voir la cohérence des caches entre processeurs de shaders. Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs auquel on aurait rajouté des ROPs, les circuits de la rastérisation et les unités de textures. Il n'est donc pas étonnant que les problèmes rencontrés sur les processeurs multicœurs soient aussi présents sur les GPU, la cohérence des caches ne fait pas exception.
Pour simplifier les explications, nous allons partir du principe que chaque processeur de shaders a son propre cache de données. Prenons deux processeur de shaders qui ont chacun une copie d'une donnée dans leur cache. Si un processeur de shaders modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches.
[[File:Cohérence des caches.png|centre|vignette|upright=2|Cohérence des caches]]
La réalité est cependant plus complexe, dans le sens où il n'y a souvent pas un cache par processeur de shaders, mais une hiérarchie de cache assez complexe, avec un cache L1 par processeur de shaders, un cache L2 partagé entre plusieurs processeur de shaders, des caches partagés entre tous les processeur de shaders, etc. Certains GPU partagent leur cache L1 d’instructions entre plusieurs processeur de shaders, d'autres non. Mais le principe reste valide, tant qu'un cache n'est pas partagé entre tous les processeurs de shaders : un cache peut contenir une donnée invalide, à savoir qu'elle a été modifiée dans le cache d'un autre processeur de shaders.
Pour corriger ce problème, les ingénieurs ont inventé des '''protocoles de cohérence des caches''' pour détecter les données périmées et les mettre à jour. Mais autant ces techniques sont faisables sur des CPU avec un nombre limité de cœurs, autant elles sont impraticables avec un GPU contenant une centaine de cœurs. Heureusement, la cohérence des caches est un problème bien moins important sur les GPU que sur les CPU. En effet, le rendu 3D implique un parallélisme de données : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches.
En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même. Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisés. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur insère une instruction qui invalide le cache, avant l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo et donc qu'il s'agit d'une donnée correcte.
C'est cette technique qui est utilisée pour les caches de textures, pour supporter les techniques de ''render-to-texture''. Dans le chapitre précédent, on a vu que lorsque d'une texture est modifiée par le GPU, les caches de texture sont réinitialisés. Les opérations de ''render-to-texture'' étant assez rares, il vaut mieux faire cela que de rendre les caches de texture accesibles en écriture. Ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches du GPU sont gérés avec le même principe. Pour les caches généralistes, certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.
{{NavChapitre | book=Les cartes graphiques
| prev=La hiérarchie mémoire d'un GPU
| prevText=La hiérarchie mémoire d'un GPU
| next=Le processeur de commandes
| netxText=Le processeur de commandes
}}{{autocat}}
rvfjqusjc9y75ybcpsml4cf0jklby0x
745320
745319
2025-06-24T20:45:42Z
Mewtow
31375
/* La cohérence des caches pour les écritures/lectures du processeur */
745320
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.
==La mémoire vidéo dédiée==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
==La cohérence des caches sur un GPU==
Pour terminer ce chapitre, nous allons parler de la '''cohérence des caches'''. La cohérence des caches est un problème qui se manifeste à plusieurs niveaux, quand on parle d'un GPU. Mais la situation la plus courante est celle où le processeur échange des données avec la carte graphique.
Un ordinateur contient un processeur et un GPU, chacun avec leurs caches séparés. Le processeur ou le GPU peuvent alors modifier la donnée chacun dans leur cache. Le résultat est que la donnée en RAM, celle dans le cache du GPU et celle dans le cache du CPU, seront totalement différentes. Il y a donc une incohérence : une lecture à une adresse ne donnera pas le même résultat selon que la lecture vienne du CPU, du GPU ou d'un périphérique DMA. Alors qu'il idéalement, on voudrait que les caches soient cohérents, à savoir qu'ils contiennent tous la même copie d'une donnée, ou presque.
[[File:Cohérence des caches entre CPU et GPU avec mémoire unifiée.png|centre|vignette|upright=2|Cohérence des caches entre CPU et GPU avec mémoire unifiée]]
Le problème se manifeste aussi bien avec la mémoire unifiée qu'avec une mémoire vidéo séparée. Le schéma précédent illustre ce qui se passe avec la mémoire unifiée. Avec une mémoire vidéo séparée, il faut prendre en compte les échanges entre RAM système et mémoire vidéo, avec des transferts DMA.
La majorité des échanges entre CPU et GPU se font du CPU vers le GPU. Le CPU prépare des textures, un tampon de sommet et diverses informations, qu'il envoie au GPU pour que celui-ci fasse le rendu 3D. Les échanges de données dans l'autre sens sont beaucoup plus rares. Sur un GPU dédié, le processeur envoie des données au GPU en trois temps :
* le CPU écrit des données en mémoire RAM ;
* les données sont copiées de la RAM vers la mémoire vidéo, via un transfert DMA ;
* les données sont lues par le GPU en mémoire vidéo.
La seconde étape disparait avec la mémoire unifiée. La cohérence des caches se manifeste entre l'étape 1 et 2, entre l'étape 2 et 3. Voyons d'abord le problème entre les deux dernières étapes, à savoir une fois que le transfert DMA est terminé et que le GPU veut lire les données.
===La cohérence des caches pour les lectures GPU===
Après un transfert DMA, une situation bien précise pose problème : quand un transfert DMA remplace des données par d'autres, à savoir qu'il écrase des données devenues inutiles, pour les remplacer par des données utiles. C'est très fréquent, les pilotes graphiques libèrent souvent de la mémoire vidéo pour la réallouer immédiatement après, afin de ne pas gaspiller de VRAM. Sans intervention du GPU, le remplacement des données aura été fait en mémoire vidéo, pas dans les caches du GPU. Et tout accès ultérieur au cache renverra la donnée écrasée.
[[File:Cache incoherence write.svg|centre|vignette|upright=2|Cohérence des caches avec DMA.]]
Pour éviter cela, le GPU invalide ses caches en cas de transfert DMA. Par invalider, on veut dire que le cache est réinitialisé, mis à zéro, il est rendu vierge de toute donnée. Ainsi, tout accès mémoire ultérieur se fera en mémoire RAM sans passer par le cache. Les données lues depuis la RAM seront ensuite copiées dans le cache, mais ce seront les données valides écrites après le transfert DMA. Le contenu du cache est alors reconstitué au fur et à mesure des accès mémoire. L'invalidation est automatique sur les anciens GPU, elle est réalisée par le processeur de commande. Sur les GPU modernes, elle est réalisée par le programmeur, comme on va le voir dans la section immédiatement suivante.
La même chose a lieu, mais avec la mémoire unifiée. La mémoire unifiée n'implique pas de transfert DMA entre CPU et GPU, vu qu'il n'y a qu'une seule RAM unifiée. Par contre, le processeur écrit directement en mémoire RAM, mais les écritures ne sont pas propagées dans les caches du GPU. Pour corriger cela, lorsque le processeur envoie des données au GPU, il force le GPU à invalider ses caches. Il envoie une commande dédiée pour, qui précède les commandes liées au rendu 2D/3D/autres.
===La cohérence des caches pour les écritures/lectures du processeur===
Au niveau du processeur, le problème est l'inverse de ce qui se passe avec les transferts DMA. Avec les transferts DMA, la mémoire vidéo est modifiée, mais pas les caches. Avec le processeur, c'est l'inverse. Si le processeur veut envoyer des données au GPU, il doit écrire ces données dans la mémoire RAM, avant de faire le transfert DMA. Mais la présence de caches pose problème : les écritures peuvent être interceptées par le cache et ne pas être propagées en RAM.
Les processeurs modernes sont capables de marquer des blocs de mémoire entiers comme étant non-cacheables, à savoir que toute lecture/écriture dedans se fait sans passer par le cache. C'est une fonctionnalité très importante pour communiquer avec les périphériques. Pour les GPU dédiés/soudés, cela a un lien avec la mémoire vidéo mappée en mémoire. Plus haut, nous avions dit que la mémoire vidéo est visible dans l'espace d'adressage du processeur, à savoir qu'un bloc de mémoire est détourné pour adresser non pas la RAM, mais la mémoire vidéo. Et bien ce bloc de mémoire entier est marqué comme étant non-cacheable.
La situation est gérée différemment sur les GPU intégrés, vu qu'ils utilisent la mémoire unifiée. Si le GPU est conçu pour, il n'y a pas besoin de marquer les données comme non-cacheables. Le cas le plus simple est celui où le CPU et le GPU partagent leur cache L3/L4. Dans ce cas, il n'y a qu'un seul cache L3/L4 qui ne contient qu'une seule copie valide, écrite par le CPU et lue par le GPU. Il faut juste garantir que la donnée soit lue par le GPU depuis le L3, mais c'est là une question d'inclusivité du cache, qui ne nous concerne pas ici. Si le CPU et le GPU ne partagent pas de cache, il suffit que le GPU puisse lire les caches du CPU. C'est la méthode utilisée sur l'APU Trinity d'AMD.
===La cohérence des caches entre processeurs de shaders===
Pour terminer, il faut voir la cohérence des caches entre processeurs de shaders. Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs auquel on aurait rajouté des ROPs, les circuits de la rastérisation et les unités de textures. Il n'est donc pas étonnant que les problèmes rencontrés sur les processeurs multicœurs soient aussi présents sur les GPU, la cohérence des caches ne fait pas exception.
Pour simplifier les explications, nous allons partir du principe que chaque processeur de shaders a son propre cache de données. Prenons deux processeur de shaders qui ont chacun une copie d'une donnée dans leur cache. Si un processeur de shaders modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches.
[[File:Cohérence des caches.png|centre|vignette|upright=2|Cohérence des caches]]
La réalité est cependant plus complexe, dans le sens où il n'y a souvent pas un cache par processeur de shaders, mais une hiérarchie de cache assez complexe, avec un cache L1 par processeur de shaders, un cache L2 partagé entre plusieurs processeur de shaders, des caches partagés entre tous les processeur de shaders, etc. Certains GPU partagent leur cache L1 d’instructions entre plusieurs processeur de shaders, d'autres non. Mais le principe reste valide, tant qu'un cache n'est pas partagé entre tous les processeurs de shaders : un cache peut contenir une donnée invalide, à savoir qu'elle a été modifiée dans le cache d'un autre processeur de shaders.
Pour corriger ce problème, les ingénieurs ont inventé des '''protocoles de cohérence des caches''' pour détecter les données périmées et les mettre à jour. Mais autant ces techniques sont faisables sur des CPU avec un nombre limité de cœurs, autant elles sont impraticables avec un GPU contenant une centaine de cœurs. Heureusement, la cohérence des caches est un problème bien moins important sur les GPU que sur les CPU. En effet, le rendu 3D implique un parallélisme de données : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches.
En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même. Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisés. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur insère une instruction qui invalide le cache, avant l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo et donc qu'il s'agit d'une donnée correcte.
C'est cette technique qui est utilisée pour les caches de textures, pour supporter les techniques de ''render-to-texture''. Dans le chapitre précédent, on a vu que lorsque d'une texture est modifiée par le GPU, les caches de texture sont réinitialisés. Les opérations de ''render-to-texture'' étant assez rares, il vaut mieux faire cela que de rendre les caches de texture accesibles en écriture. Ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches du GPU sont gérés avec le même principe. Pour les caches généralistes, certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.
{{NavChapitre | book=Les cartes graphiques
| prev=La hiérarchie mémoire d'un GPU
| prevText=La hiérarchie mémoire d'un GPU
| next=Le processeur de commandes
| netxText=Le processeur de commandes
}}{{autocat}}
9nxkn30rhk72dz73kqjjyk60gb1eun1
745321
745320
2025-06-24T20:46:49Z
Mewtow
31375
/* La cohérence des caches pour les écritures/lectures du processeur */
745321
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.
==La mémoire vidéo dédiée==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
==La cohérence des caches sur un GPU==
Pour terminer ce chapitre, nous allons parler de la '''cohérence des caches'''. La cohérence des caches est un problème qui se manifeste à plusieurs niveaux, quand on parle d'un GPU. Mais la situation la plus courante est celle où le processeur échange des données avec la carte graphique.
Un ordinateur contient un processeur et un GPU, chacun avec leurs caches séparés. Le processeur ou le GPU peuvent alors modifier la donnée chacun dans leur cache. Le résultat est que la donnée en RAM, celle dans le cache du GPU et celle dans le cache du CPU, seront totalement différentes. Il y a donc une incohérence : une lecture à une adresse ne donnera pas le même résultat selon que la lecture vienne du CPU, du GPU ou d'un périphérique DMA. Alors qu'il idéalement, on voudrait que les caches soient cohérents, à savoir qu'ils contiennent tous la même copie d'une donnée, ou presque.
[[File:Cohérence des caches entre CPU et GPU avec mémoire unifiée.png|centre|vignette|upright=2|Cohérence des caches entre CPU et GPU avec mémoire unifiée]]
Le problème se manifeste aussi bien avec la mémoire unifiée qu'avec une mémoire vidéo séparée. Le schéma précédent illustre ce qui se passe avec la mémoire unifiée. Avec une mémoire vidéo séparée, il faut prendre en compte les échanges entre RAM système et mémoire vidéo, avec des transferts DMA.
La majorité des échanges entre CPU et GPU se font du CPU vers le GPU. Le CPU prépare des textures, un tampon de sommet et diverses informations, qu'il envoie au GPU pour que celui-ci fasse le rendu 3D. Les échanges de données dans l'autre sens sont beaucoup plus rares. Sur un GPU dédié, le processeur envoie des données au GPU en trois temps :
* le CPU écrit des données en mémoire RAM ;
* les données sont copiées de la RAM vers la mémoire vidéo, via un transfert DMA ;
* les données sont lues par le GPU en mémoire vidéo.
La seconde étape disparait avec la mémoire unifiée. La cohérence des caches se manifeste entre l'étape 1 et 2, entre l'étape 2 et 3. Voyons d'abord le problème entre les deux dernières étapes, à savoir une fois que le transfert DMA est terminé et que le GPU veut lire les données.
===La cohérence des caches pour les lectures GPU===
Après un transfert DMA, une situation bien précise pose problème : quand un transfert DMA remplace des données par d'autres, à savoir qu'il écrase des données devenues inutiles, pour les remplacer par des données utiles. C'est très fréquent, les pilotes graphiques libèrent souvent de la mémoire vidéo pour la réallouer immédiatement après, afin de ne pas gaspiller de VRAM. Sans intervention du GPU, le remplacement des données aura été fait en mémoire vidéo, pas dans les caches du GPU. Et tout accès ultérieur au cache renverra la donnée écrasée.
[[File:Cache incoherence write.svg|centre|vignette|upright=2|Cohérence des caches avec DMA.]]
Pour éviter cela, le GPU invalide ses caches en cas de transfert DMA. Par invalider, on veut dire que le cache est réinitialisé, mis à zéro, il est rendu vierge de toute donnée. Ainsi, tout accès mémoire ultérieur se fera en mémoire RAM sans passer par le cache. Les données lues depuis la RAM seront ensuite copiées dans le cache, mais ce seront les données valides écrites après le transfert DMA. Le contenu du cache est alors reconstitué au fur et à mesure des accès mémoire. L'invalidation est automatique sur les anciens GPU, elle est réalisée par le processeur de commande. Sur les GPU modernes, elle est réalisée par le programmeur, comme on va le voir dans la section immédiatement suivante.
La même chose a lieu, mais avec la mémoire unifiée. La mémoire unifiée n'implique pas de transfert DMA entre CPU et GPU, vu qu'il n'y a qu'une seule RAM unifiée. Par contre, le processeur écrit directement en mémoire RAM, mais les écritures ne sont pas propagées dans les caches du GPU. Pour corriger cela, lorsque le processeur envoie des données au GPU, il force le GPU à invalider ses caches. Il envoie une commande dédiée pour, qui précède les commandes liées au rendu 2D/3D/autres.
===La cohérence des caches pour les écritures/lectures du processeur===
Au niveau du processeur, le problème est l'inverse de ce qui se passe avec les transferts DMA. Le processeur doit écrire les données dans la mémoire RAM, avant de faire le transfert DMA. Mais la présence de caches pose problème : les écritures peuvent être interceptées par le cache et ne pas être propagées en RAM.
Pour éviter cela, les processeurs modernes sont capables de marquer des blocs de mémoire entiers comme étant non-cacheables, à savoir que toute lecture/écriture dedans se fait sans passer par le cache. C'est une fonctionnalité très importante pour communiquer avec les périphériques. Pour les GPU dédiés/soudés, cela a un lien avec la mémoire vidéo mappée en mémoire. Plus haut, nous avions dit que la mémoire vidéo est visible dans l'espace d'adressage du processeur, à savoir qu'un bloc de mémoire est détourné pour adresser non pas la RAM, mais la mémoire vidéo. Et bien ce bloc de mémoire entier est marqué comme étant non-cacheable.
La situation est gérée différemment sur les GPU intégrés, vu qu'ils utilisent la mémoire unifiée. Si le GPU est conçu pour, il n'y a pas besoin de marquer les données comme non-cacheables. Le cas le plus simple est celui où le CPU et le GPU partagent leur cache L3/L4. Dans ce cas, il n'y a qu'un seul cache L3/L4 qui ne contient qu'une seule copie valide, écrite par le CPU et lue par le GPU. Il faut juste garantir que la donnée soit lue par le GPU depuis le L3, mais c'est là une question d'inclusivité du cache, qui ne nous concerne pas ici. Si le CPU et le GPU ne partagent pas de cache, il suffit que le GPU puisse lire les caches du CPU. C'est la méthode utilisée sur l'APU Trinity d'AMD.
===La cohérence des caches entre processeurs de shaders===
Pour terminer, il faut voir la cohérence des caches entre processeurs de shaders. Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs auquel on aurait rajouté des ROPs, les circuits de la rastérisation et les unités de textures. Il n'est donc pas étonnant que les problèmes rencontrés sur les processeurs multicœurs soient aussi présents sur les GPU, la cohérence des caches ne fait pas exception.
Pour simplifier les explications, nous allons partir du principe que chaque processeur de shaders a son propre cache de données. Prenons deux processeur de shaders qui ont chacun une copie d'une donnée dans leur cache. Si un processeur de shaders modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches.
[[File:Cohérence des caches.png|centre|vignette|upright=2|Cohérence des caches]]
La réalité est cependant plus complexe, dans le sens où il n'y a souvent pas un cache par processeur de shaders, mais une hiérarchie de cache assez complexe, avec un cache L1 par processeur de shaders, un cache L2 partagé entre plusieurs processeur de shaders, des caches partagés entre tous les processeur de shaders, etc. Certains GPU partagent leur cache L1 d’instructions entre plusieurs processeur de shaders, d'autres non. Mais le principe reste valide, tant qu'un cache n'est pas partagé entre tous les processeurs de shaders : un cache peut contenir une donnée invalide, à savoir qu'elle a été modifiée dans le cache d'un autre processeur de shaders.
Pour corriger ce problème, les ingénieurs ont inventé des '''protocoles de cohérence des caches''' pour détecter les données périmées et les mettre à jour. Mais autant ces techniques sont faisables sur des CPU avec un nombre limité de cœurs, autant elles sont impraticables avec un GPU contenant une centaine de cœurs. Heureusement, la cohérence des caches est un problème bien moins important sur les GPU que sur les CPU. En effet, le rendu 3D implique un parallélisme de données : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches.
En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même. Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisés. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur insère une instruction qui invalide le cache, avant l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo et donc qu'il s'agit d'une donnée correcte.
C'est cette technique qui est utilisée pour les caches de textures, pour supporter les techniques de ''render-to-texture''. Dans le chapitre précédent, on a vu que lorsque d'une texture est modifiée par le GPU, les caches de texture sont réinitialisés. Les opérations de ''render-to-texture'' étant assez rares, il vaut mieux faire cela que de rendre les caches de texture accesibles en écriture. Ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches du GPU sont gérés avec le même principe. Pour les caches généralistes, certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.
{{NavChapitre | book=Les cartes graphiques
| prev=La hiérarchie mémoire d'un GPU
| prevText=La hiérarchie mémoire d'un GPU
| next=Le processeur de commandes
| netxText=Le processeur de commandes
}}{{autocat}}
mmpt50ws1v9cgg1j4fgrq54tqf62nt0
745322
745321
2025-06-24T20:50:57Z
Mewtow
31375
/* La cohérence des caches pour les écritures/lectures du processeur */
745322
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.
==La mémoire vidéo dédiée==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
==La cohérence des caches sur un GPU==
Pour terminer ce chapitre, nous allons parler de la '''cohérence des caches'''. La cohérence des caches est un problème qui se manifeste à plusieurs niveaux, quand on parle d'un GPU. Mais la situation la plus courante est celle où le processeur échange des données avec la carte graphique.
Un ordinateur contient un processeur et un GPU, chacun avec leurs caches séparés. Le processeur ou le GPU peuvent alors modifier la donnée chacun dans leur cache. Le résultat est que la donnée en RAM, celle dans le cache du GPU et celle dans le cache du CPU, seront totalement différentes. Il y a donc une incohérence : une lecture à une adresse ne donnera pas le même résultat selon que la lecture vienne du CPU, du GPU ou d'un périphérique DMA. Alors qu'il idéalement, on voudrait que les caches soient cohérents, à savoir qu'ils contiennent tous la même copie d'une donnée, ou presque.
[[File:Cohérence des caches entre CPU et GPU avec mémoire unifiée.png|centre|vignette|upright=2|Cohérence des caches entre CPU et GPU avec mémoire unifiée]]
Le problème se manifeste aussi bien avec la mémoire unifiée qu'avec une mémoire vidéo séparée. Le schéma précédent illustre ce qui se passe avec la mémoire unifiée. Avec une mémoire vidéo séparée, il faut prendre en compte les échanges entre RAM système et mémoire vidéo, avec des transferts DMA.
La majorité des échanges entre CPU et GPU se font du CPU vers le GPU. Le CPU prépare des textures, un tampon de sommet et diverses informations, qu'il envoie au GPU pour que celui-ci fasse le rendu 3D. Les échanges de données dans l'autre sens sont beaucoup plus rares. Sur un GPU dédié, le processeur envoie des données au GPU en trois temps :
* le CPU écrit des données en mémoire RAM ;
* les données sont copiées de la RAM vers la mémoire vidéo, via un transfert DMA ;
* les données sont lues par le GPU en mémoire vidéo.
La seconde étape disparait avec la mémoire unifiée. La cohérence des caches se manifeste entre l'étape 1 et 2, entre l'étape 2 et 3. Voyons d'abord le problème entre les deux dernières étapes, à savoir une fois que le transfert DMA est terminé et que le GPU veut lire les données.
===La cohérence des caches pour les lectures GPU===
Après un transfert DMA, une situation bien précise pose problème : quand un transfert DMA remplace des données par d'autres, à savoir qu'il écrase des données devenues inutiles, pour les remplacer par des données utiles. C'est très fréquent, les pilotes graphiques libèrent souvent de la mémoire vidéo pour la réallouer immédiatement après, afin de ne pas gaspiller de VRAM. Sans intervention du GPU, le remplacement des données aura été fait en mémoire vidéo, pas dans les caches du GPU. Et tout accès ultérieur au cache renverra la donnée écrasée.
[[File:Cache incoherence write.svg|centre|vignette|upright=2|Cohérence des caches avec DMA.]]
Pour éviter cela, le GPU invalide ses caches en cas de transfert DMA. Par invalider, on veut dire que le cache est réinitialisé, mis à zéro, il est rendu vierge de toute donnée. Ainsi, tout accès mémoire ultérieur se fera en mémoire RAM sans passer par le cache. Les données lues depuis la RAM seront ensuite copiées dans le cache, mais ce seront les données valides écrites après le transfert DMA. Le contenu du cache est alors reconstitué au fur et à mesure des accès mémoire. L'invalidation est automatique sur les anciens GPU, elle est réalisée par le processeur de commande. Sur les GPU modernes, elle est réalisée par le programmeur, comme on va le voir dans la section immédiatement suivante.
La même chose a lieu, mais avec la mémoire unifiée. La mémoire unifiée n'implique pas de transfert DMA entre CPU et GPU, vu qu'il n'y a qu'une seule RAM unifiée. Par contre, le processeur écrit directement en mémoire RAM, mais les écritures ne sont pas propagées dans les caches du GPU. Pour corriger cela, lorsque le processeur envoie des données au GPU, il force le GPU à invalider ses caches. Il envoie une commande dédiée pour, qui précède les commandes liées au rendu 2D/3D/autres.
===La cohérence des caches pour les écritures/lectures du processeur===
Au niveau du processeur, le problème est l'inverse de ce qui se passe avec les transferts DMA. Le processeur doit écrire les données dans la mémoire RAM, avant de faire le transfert DMA. Mais la présence de caches pose problème : les écritures peuvent être interceptées par le cache et ne pas être propagées en RAM.
Pour éviter cela, les processeurs modernes sont capables de marquer des blocs de mémoire entiers comme étant non-cacheables, à savoir que toute lecture/écriture dedans se fait sans passer par le cache. C'est une fonctionnalité très importante pour communiquer avec les périphériques. Pour les GPU dédiés/soudés, cela a un lien avec la mémoire vidéo mappée en mémoire. Plus haut, nous avions dit que la mémoire vidéo est visible dans l'espace d'adressage du processeur, à savoir qu'un bloc de mémoire est détourné pour adresser non pas la RAM, mais la mémoire vidéo. Et bien ce bloc de mémoire entier est marqué comme étant non-cacheable.
[[File:Cohérence des caches entre CPU et GPU avec mémoire unifiée, mécanismes.png|centre|vignette|upright=2|Cohérence des caches entre CPU et GPU avec mémoire unifiée, mécanismes]]
La situation peut être optimisée sur les GPU intégrés. Si le GPU est conçu pour, il n'y a pas besoin de marquer les données comme non-cacheables. Le cas le plus simple est celui où le CPU et le GPU partagent leur cache L3/L4. Dans ce cas, il n'y a qu'un seul cache L3/L4 qui ne contient qu'une seule copie valide, écrite par le CPU et lue par le GPU. Il faut juste garantir que la donnée soit lue par le GPU depuis le L3, mais c'est là une question d'inclusivité du cache, qui ne nous concerne pas ici. Si le CPU et le GPU ne partagent pas de cache, il suffit que le GPU puisse lire les caches du CPU. C'est la méthode utilisée sur l'APU Trinity d'AMD.
===La cohérence des caches entre processeurs de shaders===
Pour terminer, il faut voir la cohérence des caches entre processeurs de shaders. Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs auquel on aurait rajouté des ROPs, les circuits de la rastérisation et les unités de textures. Il n'est donc pas étonnant que les problèmes rencontrés sur les processeurs multicœurs soient aussi présents sur les GPU, la cohérence des caches ne fait pas exception.
Pour simplifier les explications, nous allons partir du principe que chaque processeur de shaders a son propre cache de données. Prenons deux processeur de shaders qui ont chacun une copie d'une donnée dans leur cache. Si un processeur de shaders modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches.
[[File:Cohérence des caches.png|centre|vignette|upright=2|Cohérence des caches]]
La réalité est cependant plus complexe, dans le sens où il n'y a souvent pas un cache par processeur de shaders, mais une hiérarchie de cache assez complexe, avec un cache L1 par processeur de shaders, un cache L2 partagé entre plusieurs processeur de shaders, des caches partagés entre tous les processeur de shaders, etc. Certains GPU partagent leur cache L1 d’instructions entre plusieurs processeur de shaders, d'autres non. Mais le principe reste valide, tant qu'un cache n'est pas partagé entre tous les processeurs de shaders : un cache peut contenir une donnée invalide, à savoir qu'elle a été modifiée dans le cache d'un autre processeur de shaders.
Pour corriger ce problème, les ingénieurs ont inventé des '''protocoles de cohérence des caches''' pour détecter les données périmées et les mettre à jour. Mais autant ces techniques sont faisables sur des CPU avec un nombre limité de cœurs, autant elles sont impraticables avec un GPU contenant une centaine de cœurs. Heureusement, la cohérence des caches est un problème bien moins important sur les GPU que sur les CPU. En effet, le rendu 3D implique un parallélisme de données : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches.
En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même. Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisés. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur insère une instruction qui invalide le cache, avant l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo et donc qu'il s'agit d'une donnée correcte.
C'est cette technique qui est utilisée pour les caches de textures, pour supporter les techniques de ''render-to-texture''. Dans le chapitre précédent, on a vu que lorsque d'une texture est modifiée par le GPU, les caches de texture sont réinitialisés. Les opérations de ''render-to-texture'' étant assez rares, il vaut mieux faire cela que de rendre les caches de texture accesibles en écriture. Ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches du GPU sont gérés avec le même principe. Pour les caches généralistes, certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.
{{NavChapitre | book=Les cartes graphiques
| prev=La hiérarchie mémoire d'un GPU
| prevText=La hiérarchie mémoire d'un GPU
| next=Le processeur de commandes
| netxText=Le processeur de commandes
}}{{autocat}}
3n04xuatfbfmynxejbmv5lq8wwzcmz8
745323
745322
2025-06-24T20:51:27Z
Mewtow
31375
/* La cohérence des caches sur un GPU */
745323
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.
==La mémoire vidéo dédiée==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
==La cohérence des caches sur un GPU==
Pour terminer ce chapitre, nous allons parler de la '''cohérence des caches'''. La cohérence des caches est un problème qui se manifeste à plusieurs niveaux, quand on parle d'un GPU. Mais la situation la plus courante est celle où le processeur échange des données avec la carte graphique.
===La cohérence des caches entre CPU et GPU===
Un ordinateur contient un processeur et un GPU, chacun avec leurs caches séparés. Le processeur ou le GPU peuvent alors modifier la donnée chacun dans leur cache. Le résultat est que la donnée en RAM, celle dans le cache du GPU et celle dans le cache du CPU, seront totalement différentes. Il y a donc une incohérence : une lecture à une adresse ne donnera pas le même résultat selon que la lecture vienne du CPU, du GPU ou d'un périphérique DMA. Alors qu'il idéalement, on voudrait que les caches soient cohérents, à savoir qu'ils contiennent tous la même copie d'une donnée, ou presque.
[[File:Cohérence des caches entre CPU et GPU avec mémoire unifiée.png|centre|vignette|upright=2|Cohérence des caches entre CPU et GPU avec mémoire unifiée]]
Le problème se manifeste aussi bien avec la mémoire unifiée qu'avec une mémoire vidéo séparée. Le schéma précédent illustre ce qui se passe avec la mémoire unifiée. Avec une mémoire vidéo séparée, il faut prendre en compte les échanges entre RAM système et mémoire vidéo, avec des transferts DMA.
La majorité des échanges entre CPU et GPU se font du CPU vers le GPU. Le CPU prépare des textures, un tampon de sommet et diverses informations, qu'il envoie au GPU pour que celui-ci fasse le rendu 3D. Les échanges de données dans l'autre sens sont beaucoup plus rares. Sur un GPU dédié, le processeur envoie des données au GPU en trois temps :
* le CPU écrit des données en mémoire RAM ;
* les données sont copiées de la RAM vers la mémoire vidéo, via un transfert DMA ;
* les données sont lues par le GPU en mémoire vidéo.
La seconde étape disparait avec la mémoire unifiée. La cohérence des caches se manifeste entre l'étape 1 et 2, entre l'étape 2 et 3. Voyons d'abord le problème entre les deux dernières étapes, à savoir une fois que le transfert DMA est terminé et que le GPU veut lire les données.
====La cohérence des caches pour les lectures GPU====
Après un transfert DMA, une situation bien précise pose problème : quand un transfert DMA remplace des données par d'autres, à savoir qu'il écrase des données devenues inutiles, pour les remplacer par des données utiles. C'est très fréquent, les pilotes graphiques libèrent souvent de la mémoire vidéo pour la réallouer immédiatement après, afin de ne pas gaspiller de VRAM. Sans intervention du GPU, le remplacement des données aura été fait en mémoire vidéo, pas dans les caches du GPU. Et tout accès ultérieur au cache renverra la donnée écrasée.
[[File:Cache incoherence write.svg|centre|vignette|upright=2|Cohérence des caches avec DMA.]]
Pour éviter cela, le GPU invalide ses caches en cas de transfert DMA. Par invalider, on veut dire que le cache est réinitialisé, mis à zéro, il est rendu vierge de toute donnée. Ainsi, tout accès mémoire ultérieur se fera en mémoire RAM sans passer par le cache. Les données lues depuis la RAM seront ensuite copiées dans le cache, mais ce seront les données valides écrites après le transfert DMA. Le contenu du cache est alors reconstitué au fur et à mesure des accès mémoire. L'invalidation est automatique sur les anciens GPU, elle est réalisée par le processeur de commande. Sur les GPU modernes, elle est réalisée par le programmeur, comme on va le voir dans la section immédiatement suivante.
La même chose a lieu, mais avec la mémoire unifiée. La mémoire unifiée n'implique pas de transfert DMA entre CPU et GPU, vu qu'il n'y a qu'une seule RAM unifiée. Par contre, le processeur écrit directement en mémoire RAM, mais les écritures ne sont pas propagées dans les caches du GPU. Pour corriger cela, lorsque le processeur envoie des données au GPU, il force le GPU à invalider ses caches. Il envoie une commande dédiée pour, qui précède les commandes liées au rendu 2D/3D/autres.
====La cohérence des caches pour les écritures/lectures du processeur====
Au niveau du processeur, le problème est l'inverse de ce qui se passe avec les transferts DMA. Le processeur doit écrire les données dans la mémoire RAM, avant de faire le transfert DMA. Mais la présence de caches pose problème : les écritures peuvent être interceptées par le cache et ne pas être propagées en RAM.
Pour éviter cela, les processeurs modernes sont capables de marquer des blocs de mémoire entiers comme étant non-cacheables, à savoir que toute lecture/écriture dedans se fait sans passer par le cache. C'est une fonctionnalité très importante pour communiquer avec les périphériques. Pour les GPU dédiés/soudés, cela a un lien avec la mémoire vidéo mappée en mémoire. Plus haut, nous avions dit que la mémoire vidéo est visible dans l'espace d'adressage du processeur, à savoir qu'un bloc de mémoire est détourné pour adresser non pas la RAM, mais la mémoire vidéo. Et bien ce bloc de mémoire entier est marqué comme étant non-cacheable.
[[File:Cohérence des caches entre CPU et GPU avec mémoire unifiée, mécanismes.png|centre|vignette|upright=2|Cohérence des caches entre CPU et GPU avec mémoire unifiée, mécanismes]]
La situation peut être optimisée sur les GPU intégrés. Si le GPU est conçu pour, il n'y a pas besoin de marquer les données comme non-cacheables. Le cas le plus simple est celui où le CPU et le GPU partagent leur cache L3/L4. Dans ce cas, il n'y a qu'un seul cache L3/L4 qui ne contient qu'une seule copie valide, écrite par le CPU et lue par le GPU. Il faut juste garantir que la donnée soit lue par le GPU depuis le L3, mais c'est là une question d'inclusivité du cache, qui ne nous concerne pas ici. Si le CPU et le GPU ne partagent pas de cache, il suffit que le GPU puisse lire les caches du CPU. C'est la méthode utilisée sur l'APU Trinity d'AMD.
===La cohérence des caches entre processeurs de shaders===
Pour terminer, il faut voir la cohérence des caches entre processeurs de shaders. Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs auquel on aurait rajouté des ROPs, les circuits de la rastérisation et les unités de textures. Il n'est donc pas étonnant que les problèmes rencontrés sur les processeurs multicœurs soient aussi présents sur les GPU, la cohérence des caches ne fait pas exception.
Pour simplifier les explications, nous allons partir du principe que chaque processeur de shaders a son propre cache de données. Prenons deux processeur de shaders qui ont chacun une copie d'une donnée dans leur cache. Si un processeur de shaders modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches.
[[File:Cohérence des caches.png|centre|vignette|upright=2|Cohérence des caches]]
La réalité est cependant plus complexe, dans le sens où il n'y a souvent pas un cache par processeur de shaders, mais une hiérarchie de cache assez complexe, avec un cache L1 par processeur de shaders, un cache L2 partagé entre plusieurs processeur de shaders, des caches partagés entre tous les processeur de shaders, etc. Certains GPU partagent leur cache L1 d’instructions entre plusieurs processeur de shaders, d'autres non. Mais le principe reste valide, tant qu'un cache n'est pas partagé entre tous les processeurs de shaders : un cache peut contenir une donnée invalide, à savoir qu'elle a été modifiée dans le cache d'un autre processeur de shaders.
Pour corriger ce problème, les ingénieurs ont inventé des '''protocoles de cohérence des caches''' pour détecter les données périmées et les mettre à jour. Mais autant ces techniques sont faisables sur des CPU avec un nombre limité de cœurs, autant elles sont impraticables avec un GPU contenant une centaine de cœurs. Heureusement, la cohérence des caches est un problème bien moins important sur les GPU que sur les CPU. En effet, le rendu 3D implique un parallélisme de données : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches.
En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même. Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisés. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur insère une instruction qui invalide le cache, avant l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo et donc qu'il s'agit d'une donnée correcte.
C'est cette technique qui est utilisée pour les caches de textures, pour supporter les techniques de ''render-to-texture''. Dans le chapitre précédent, on a vu que lorsque d'une texture est modifiée par le GPU, les caches de texture sont réinitialisés. Les opérations de ''render-to-texture'' étant assez rares, il vaut mieux faire cela que de rendre les caches de texture accesibles en écriture. Ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches du GPU sont gérés avec le même principe. Pour les caches généralistes, certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.
{{NavChapitre | book=Les cartes graphiques
| prev=La hiérarchie mémoire d'un GPU
| prevText=La hiérarchie mémoire d'un GPU
| next=Le processeur de commandes
| netxText=Le processeur de commandes
}}{{autocat}}
1r9og17cbw7iellef842493q5o1dp6v
745324
745323
2025-06-24T20:52:03Z
Mewtow
31375
/* La cohérence des caches pour les lectures GPU */
745324
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.
==La mémoire vidéo dédiée==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
==La cohérence des caches sur un GPU==
Pour terminer ce chapitre, nous allons parler de la '''cohérence des caches'''. La cohérence des caches est un problème qui se manifeste à plusieurs niveaux, quand on parle d'un GPU. Mais la situation la plus courante est celle où le processeur échange des données avec la carte graphique.
===La cohérence des caches entre CPU et GPU===
Un ordinateur contient un processeur et un GPU, chacun avec leurs caches séparés. Le processeur ou le GPU peuvent alors modifier la donnée chacun dans leur cache. Le résultat est que la donnée en RAM, celle dans le cache du GPU et celle dans le cache du CPU, seront totalement différentes. Il y a donc une incohérence : une lecture à une adresse ne donnera pas le même résultat selon que la lecture vienne du CPU, du GPU ou d'un périphérique DMA. Alors qu'il idéalement, on voudrait que les caches soient cohérents, à savoir qu'ils contiennent tous la même copie d'une donnée, ou presque.
[[File:Cohérence des caches entre CPU et GPU avec mémoire unifiée.png|centre|vignette|upright=2|Cohérence des caches entre CPU et GPU avec mémoire unifiée]]
Le problème se manifeste aussi bien avec la mémoire unifiée qu'avec une mémoire vidéo séparée. Le schéma précédent illustre ce qui se passe avec la mémoire unifiée. Avec une mémoire vidéo séparée, il faut prendre en compte les échanges entre RAM système et mémoire vidéo, avec des transferts DMA.
La majorité des échanges entre CPU et GPU se font du CPU vers le GPU. Le CPU prépare des textures, un tampon de sommet et diverses informations, qu'il envoie au GPU pour que celui-ci fasse le rendu 3D. Les échanges de données dans l'autre sens sont beaucoup plus rares. Sur un GPU dédié, le processeur envoie des données au GPU en trois temps :
* le CPU écrit des données en mémoire RAM ;
* les données sont copiées de la RAM vers la mémoire vidéo, via un transfert DMA ;
* les données sont lues par le GPU en mémoire vidéo.
La seconde étape disparait avec la mémoire unifiée. La cohérence des caches se manifeste entre l'étape 1 et 2, entre l'étape 2 et 3. Voyons d'abord le problème entre les deux dernières étapes, à savoir une fois que le transfert DMA est terminé et que le GPU veut lire les données.
Après un transfert DMA, une situation bien précise pose problème : quand un transfert DMA remplace des données par d'autres, à savoir qu'il écrase des données devenues inutiles, pour les remplacer par des données utiles. C'est très fréquent, les pilotes graphiques libèrent souvent de la mémoire vidéo pour la réallouer immédiatement après, afin de ne pas gaspiller de VRAM. Sans intervention du GPU, le remplacement des données aura été fait en mémoire vidéo, pas dans les caches du GPU. Et tout accès ultérieur au cache renverra la donnée écrasée.
[[File:Cache incoherence write.svg|centre|vignette|upright=2|Cohérence des caches avec DMA.]]
Pour éviter cela, le GPU invalide ses caches en cas de transfert DMA. Par invalider, on veut dire que le cache est réinitialisé, mis à zéro, il est rendu vierge de toute donnée. Ainsi, tout accès mémoire ultérieur se fera en mémoire RAM sans passer par le cache. Les données lues depuis la RAM seront ensuite copiées dans le cache, mais ce seront les données valides écrites après le transfert DMA. Le contenu du cache est alors reconstitué au fur et à mesure des accès mémoire. L'invalidation est automatique sur les anciens GPU, elle est réalisée par le processeur de commande. Sur les GPU modernes, elle est réalisée par le programmeur, comme on va le voir dans la section immédiatement suivante.
La même chose a lieu, mais avec la mémoire unifiée. La mémoire unifiée n'implique pas de transfert DMA entre CPU et GPU, vu qu'il n'y a qu'une seule RAM unifiée. Par contre, le processeur écrit directement en mémoire RAM, mais les écritures ne sont pas propagées dans les caches du GPU. Pour corriger cela, lorsque le processeur envoie des données au GPU, il force le GPU à invalider ses caches. Il envoie une commande dédiée pour, qui précède les commandes liées au rendu 2D/3D/autres.
====La cohérence des caches pour les écritures/lectures du processeur====
Au niveau du processeur, le problème est l'inverse de ce qui se passe avec les transferts DMA. Le processeur doit écrire les données dans la mémoire RAM, avant de faire le transfert DMA. Mais la présence de caches pose problème : les écritures peuvent être interceptées par le cache et ne pas être propagées en RAM.
Pour éviter cela, les processeurs modernes sont capables de marquer des blocs de mémoire entiers comme étant non-cacheables, à savoir que toute lecture/écriture dedans se fait sans passer par le cache. C'est une fonctionnalité très importante pour communiquer avec les périphériques. Pour les GPU dédiés/soudés, cela a un lien avec la mémoire vidéo mappée en mémoire. Plus haut, nous avions dit que la mémoire vidéo est visible dans l'espace d'adressage du processeur, à savoir qu'un bloc de mémoire est détourné pour adresser non pas la RAM, mais la mémoire vidéo. Et bien ce bloc de mémoire entier est marqué comme étant non-cacheable.
[[File:Cohérence des caches entre CPU et GPU avec mémoire unifiée, mécanismes.png|centre|vignette|upright=2|Cohérence des caches entre CPU et GPU avec mémoire unifiée, mécanismes]]
La situation peut être optimisée sur les GPU intégrés. Si le GPU est conçu pour, il n'y a pas besoin de marquer les données comme non-cacheables. Le cas le plus simple est celui où le CPU et le GPU partagent leur cache L3/L4. Dans ce cas, il n'y a qu'un seul cache L3/L4 qui ne contient qu'une seule copie valide, écrite par le CPU et lue par le GPU. Il faut juste garantir que la donnée soit lue par le GPU depuis le L3, mais c'est là une question d'inclusivité du cache, qui ne nous concerne pas ici. Si le CPU et le GPU ne partagent pas de cache, il suffit que le GPU puisse lire les caches du CPU. C'est la méthode utilisée sur l'APU Trinity d'AMD.
===La cohérence des caches entre processeurs de shaders===
Pour terminer, il faut voir la cohérence des caches entre processeurs de shaders. Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs auquel on aurait rajouté des ROPs, les circuits de la rastérisation et les unités de textures. Il n'est donc pas étonnant que les problèmes rencontrés sur les processeurs multicœurs soient aussi présents sur les GPU, la cohérence des caches ne fait pas exception.
Pour simplifier les explications, nous allons partir du principe que chaque processeur de shaders a son propre cache de données. Prenons deux processeur de shaders qui ont chacun une copie d'une donnée dans leur cache. Si un processeur de shaders modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches.
[[File:Cohérence des caches.png|centre|vignette|upright=2|Cohérence des caches]]
La réalité est cependant plus complexe, dans le sens où il n'y a souvent pas un cache par processeur de shaders, mais une hiérarchie de cache assez complexe, avec un cache L1 par processeur de shaders, un cache L2 partagé entre plusieurs processeur de shaders, des caches partagés entre tous les processeur de shaders, etc. Certains GPU partagent leur cache L1 d’instructions entre plusieurs processeur de shaders, d'autres non. Mais le principe reste valide, tant qu'un cache n'est pas partagé entre tous les processeurs de shaders : un cache peut contenir une donnée invalide, à savoir qu'elle a été modifiée dans le cache d'un autre processeur de shaders.
Pour corriger ce problème, les ingénieurs ont inventé des '''protocoles de cohérence des caches''' pour détecter les données périmées et les mettre à jour. Mais autant ces techniques sont faisables sur des CPU avec un nombre limité de cœurs, autant elles sont impraticables avec un GPU contenant une centaine de cœurs. Heureusement, la cohérence des caches est un problème bien moins important sur les GPU que sur les CPU. En effet, le rendu 3D implique un parallélisme de données : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches.
En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même. Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisés. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur insère une instruction qui invalide le cache, avant l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo et donc qu'il s'agit d'une donnée correcte.
C'est cette technique qui est utilisée pour les caches de textures, pour supporter les techniques de ''render-to-texture''. Dans le chapitre précédent, on a vu que lorsque d'une texture est modifiée par le GPU, les caches de texture sont réinitialisés. Les opérations de ''render-to-texture'' étant assez rares, il vaut mieux faire cela que de rendre les caches de texture accesibles en écriture. Ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches du GPU sont gérés avec le même principe. Pour les caches généralistes, certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.
{{NavChapitre | book=Les cartes graphiques
| prev=La hiérarchie mémoire d'un GPU
| prevText=La hiérarchie mémoire d'un GPU
| next=Le processeur de commandes
| netxText=Le processeur de commandes
}}{{autocat}}
naudpk258gejrqxij9gs7tlssrrt75c
745325
745324
2025-06-24T20:52:10Z
Mewtow
31375
/* La cohérence des caches pour les écritures/lectures du processeur */
745325
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.
==La mémoire vidéo dédiée==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
==La cohérence des caches sur un GPU==
Pour terminer ce chapitre, nous allons parler de la '''cohérence des caches'''. La cohérence des caches est un problème qui se manifeste à plusieurs niveaux, quand on parle d'un GPU. Mais la situation la plus courante est celle où le processeur échange des données avec la carte graphique.
===La cohérence des caches entre CPU et GPU===
Un ordinateur contient un processeur et un GPU, chacun avec leurs caches séparés. Le processeur ou le GPU peuvent alors modifier la donnée chacun dans leur cache. Le résultat est que la donnée en RAM, celle dans le cache du GPU et celle dans le cache du CPU, seront totalement différentes. Il y a donc une incohérence : une lecture à une adresse ne donnera pas le même résultat selon que la lecture vienne du CPU, du GPU ou d'un périphérique DMA. Alors qu'il idéalement, on voudrait que les caches soient cohérents, à savoir qu'ils contiennent tous la même copie d'une donnée, ou presque.
[[File:Cohérence des caches entre CPU et GPU avec mémoire unifiée.png|centre|vignette|upright=2|Cohérence des caches entre CPU et GPU avec mémoire unifiée]]
Le problème se manifeste aussi bien avec la mémoire unifiée qu'avec une mémoire vidéo séparée. Le schéma précédent illustre ce qui se passe avec la mémoire unifiée. Avec une mémoire vidéo séparée, il faut prendre en compte les échanges entre RAM système et mémoire vidéo, avec des transferts DMA.
La majorité des échanges entre CPU et GPU se font du CPU vers le GPU. Le CPU prépare des textures, un tampon de sommet et diverses informations, qu'il envoie au GPU pour que celui-ci fasse le rendu 3D. Les échanges de données dans l'autre sens sont beaucoup plus rares. Sur un GPU dédié, le processeur envoie des données au GPU en trois temps :
* le CPU écrit des données en mémoire RAM ;
* les données sont copiées de la RAM vers la mémoire vidéo, via un transfert DMA ;
* les données sont lues par le GPU en mémoire vidéo.
La seconde étape disparait avec la mémoire unifiée. La cohérence des caches se manifeste entre l'étape 1 et 2, entre l'étape 2 et 3. Voyons d'abord le problème entre les deux dernières étapes, à savoir une fois que le transfert DMA est terminé et que le GPU veut lire les données.
Après un transfert DMA, une situation bien précise pose problème : quand un transfert DMA remplace des données par d'autres, à savoir qu'il écrase des données devenues inutiles, pour les remplacer par des données utiles. C'est très fréquent, les pilotes graphiques libèrent souvent de la mémoire vidéo pour la réallouer immédiatement après, afin de ne pas gaspiller de VRAM. Sans intervention du GPU, le remplacement des données aura été fait en mémoire vidéo, pas dans les caches du GPU. Et tout accès ultérieur au cache renverra la donnée écrasée.
[[File:Cache incoherence write.svg|centre|vignette|upright=2|Cohérence des caches avec DMA.]]
Pour éviter cela, le GPU invalide ses caches en cas de transfert DMA. Par invalider, on veut dire que le cache est réinitialisé, mis à zéro, il est rendu vierge de toute donnée. Ainsi, tout accès mémoire ultérieur se fera en mémoire RAM sans passer par le cache. Les données lues depuis la RAM seront ensuite copiées dans le cache, mais ce seront les données valides écrites après le transfert DMA. Le contenu du cache est alors reconstitué au fur et à mesure des accès mémoire. L'invalidation est automatique sur les anciens GPU, elle est réalisée par le processeur de commande. Sur les GPU modernes, elle est réalisée par le programmeur, comme on va le voir dans la section immédiatement suivante.
La même chose a lieu, mais avec la mémoire unifiée. La mémoire unifiée n'implique pas de transfert DMA entre CPU et GPU, vu qu'il n'y a qu'une seule RAM unifiée. Par contre, le processeur écrit directement en mémoire RAM, mais les écritures ne sont pas propagées dans les caches du GPU. Pour corriger cela, lorsque le processeur envoie des données au GPU, il force le GPU à invalider ses caches. Il envoie une commande dédiée pour, qui précède les commandes liées au rendu 2D/3D/autres.
Au niveau du processeur, le problème est l'inverse de ce qui se passe avec les transferts DMA. Le processeur doit écrire les données dans la mémoire RAM, avant de faire le transfert DMA. Mais la présence de caches pose problème : les écritures peuvent être interceptées par le cache et ne pas être propagées en RAM.
Pour éviter cela, les processeurs modernes sont capables de marquer des blocs de mémoire entiers comme étant non-cacheables, à savoir que toute lecture/écriture dedans se fait sans passer par le cache. C'est une fonctionnalité très importante pour communiquer avec les périphériques. Pour les GPU dédiés/soudés, cela a un lien avec la mémoire vidéo mappée en mémoire. Plus haut, nous avions dit que la mémoire vidéo est visible dans l'espace d'adressage du processeur, à savoir qu'un bloc de mémoire est détourné pour adresser non pas la RAM, mais la mémoire vidéo. Et bien ce bloc de mémoire entier est marqué comme étant non-cacheable.
[[File:Cohérence des caches entre CPU et GPU avec mémoire unifiée, mécanismes.png|centre|vignette|upright=2|Cohérence des caches entre CPU et GPU avec mémoire unifiée, mécanismes]]
La situation peut être optimisée sur les GPU intégrés. Si le GPU est conçu pour, il n'y a pas besoin de marquer les données comme non-cacheables. Le cas le plus simple est celui où le CPU et le GPU partagent leur cache L3/L4. Dans ce cas, il n'y a qu'un seul cache L3/L4 qui ne contient qu'une seule copie valide, écrite par le CPU et lue par le GPU. Il faut juste garantir que la donnée soit lue par le GPU depuis le L3, mais c'est là une question d'inclusivité du cache, qui ne nous concerne pas ici. Si le CPU et le GPU ne partagent pas de cache, il suffit que le GPU puisse lire les caches du CPU. C'est la méthode utilisée sur l'APU Trinity d'AMD.
===La cohérence des caches entre processeurs de shaders===
Pour terminer, il faut voir la cohérence des caches entre processeurs de shaders. Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs auquel on aurait rajouté des ROPs, les circuits de la rastérisation et les unités de textures. Il n'est donc pas étonnant que les problèmes rencontrés sur les processeurs multicœurs soient aussi présents sur les GPU, la cohérence des caches ne fait pas exception.
Pour simplifier les explications, nous allons partir du principe que chaque processeur de shaders a son propre cache de données. Prenons deux processeur de shaders qui ont chacun une copie d'une donnée dans leur cache. Si un processeur de shaders modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches.
[[File:Cohérence des caches.png|centre|vignette|upright=2|Cohérence des caches]]
La réalité est cependant plus complexe, dans le sens où il n'y a souvent pas un cache par processeur de shaders, mais une hiérarchie de cache assez complexe, avec un cache L1 par processeur de shaders, un cache L2 partagé entre plusieurs processeur de shaders, des caches partagés entre tous les processeur de shaders, etc. Certains GPU partagent leur cache L1 d’instructions entre plusieurs processeur de shaders, d'autres non. Mais le principe reste valide, tant qu'un cache n'est pas partagé entre tous les processeurs de shaders : un cache peut contenir une donnée invalide, à savoir qu'elle a été modifiée dans le cache d'un autre processeur de shaders.
Pour corriger ce problème, les ingénieurs ont inventé des '''protocoles de cohérence des caches''' pour détecter les données périmées et les mettre à jour. Mais autant ces techniques sont faisables sur des CPU avec un nombre limité de cœurs, autant elles sont impraticables avec un GPU contenant une centaine de cœurs. Heureusement, la cohérence des caches est un problème bien moins important sur les GPU que sur les CPU. En effet, le rendu 3D implique un parallélisme de données : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches.
En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même. Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisés. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur insère une instruction qui invalide le cache, avant l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo et donc qu'il s'agit d'une donnée correcte.
C'est cette technique qui est utilisée pour les caches de textures, pour supporter les techniques de ''render-to-texture''. Dans le chapitre précédent, on a vu que lorsque d'une texture est modifiée par le GPU, les caches de texture sont réinitialisés. Les opérations de ''render-to-texture'' étant assez rares, il vaut mieux faire cela que de rendre les caches de texture accesibles en écriture. Ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches du GPU sont gérés avec le même principe. Pour les caches généralistes, certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.
{{NavChapitre | book=Les cartes graphiques
| prev=La hiérarchie mémoire d'un GPU
| prevText=La hiérarchie mémoire d'un GPU
| next=Le processeur de commandes
| netxText=Le processeur de commandes
}}{{autocat}}
qe9x4906eovuwc5g8fdgwjuxgkacrxo
745326
745325
2025-06-24T20:53:28Z
Mewtow
31375
/* La cohérence des caches entre CPU et GPU */
745326
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.
==La mémoire vidéo dédiée==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
==La cohérence des caches sur un GPU==
Pour terminer ce chapitre, nous allons parler de la '''cohérence des caches'''. La cohérence des caches est un problème qui se manifeste à plusieurs niveaux, quand on parle d'un GPU. Mais la situation la plus courante est celle où le processeur échange des données avec la carte graphique.
===La cohérence des caches entre CPU et GPU===
Un ordinateur contient un processeur et un GPU, chacun avec leurs caches séparés. Le processeur ou le GPU peuvent alors modifier la donnée chacun dans leur cache. Le résultat est que la donnée en RAM, celle dans le cache du GPU et celle dans le cache du CPU, seront totalement différentes. Il y a donc une incohérence : une lecture à une adresse ne donnera pas le même résultat selon que la lecture vienne du CPU, du GPU ou d'un périphérique DMA. Alors qu'il idéalement, on voudrait que les caches soient cohérents, à savoir qu'ils contiennent tous la même copie d'une donnée, ou presque.
[[File:Cohérence des caches entre CPU et GPU avec mémoire unifiée.png|centre|vignette|upright=2|Cohérence des caches entre CPU et GPU avec mémoire unifiée]]
Le problème se manifeste aussi bien avec la mémoire unifiée qu'avec une mémoire vidéo séparée. Le schéma précédent illustre ce qui se passe avec la mémoire unifiée. Avec une mémoire vidéo séparée, il faut prendre en compte les échanges entre RAM système et mémoire vidéo, avec des transferts DMA.
La majorité des échanges entre CPU et GPU se font du CPU vers le GPU. Le CPU prépare des textures, un tampon de sommet et diverses informations, qu'il envoie au GPU pour que celui-ci fasse le rendu 3D. Les échanges de données dans l'autre sens sont beaucoup plus rares. Sur un GPU dédié, le processeur envoie des données au GPU en trois temps :
* le CPU écrit des données en mémoire RAM ;
* les données sont copiées de la RAM vers la mémoire vidéo, via un transfert DMA ;
* les données sont lues par le GPU en mémoire vidéo.
La seconde étape disparait avec la mémoire unifiée. La cohérence des caches se manifeste entre l'étape 1 et 2, entre l'étape 2 et 3. Voyons d'abord le problème entre les deux dernières étapes, à savoir une fois que le transfert DMA est terminé et que le GPU veut lire les données.
Après un transfert DMA, une situation bien précise pose problème : quand un transfert DMA remplace des données par d'autres, à savoir qu'il écrase des données devenues inutiles, pour les remplacer par des données utiles. C'est très fréquent, les pilotes graphiques libèrent souvent de la mémoire vidéo pour la réallouer immédiatement après, afin de ne pas gaspiller de VRAM. Sans intervention du GPU, le remplacement des données aura été fait en mémoire vidéo, pas dans les caches du GPU. Et tout accès ultérieur au cache renverra la donnée écrasée.
[[File:Cache incoherence write.svg|centre|vignette|upright=2|Cohérence des caches avec DMA.]]
Pour éviter cela, le GPU invalide ses caches en cas de transfert DMA. Par invalider, on veut dire que le cache est réinitialisé, mis à zéro, il est rendu vierge de toute donnée. Ainsi, tout accès mémoire ultérieur se fera en mémoire RAM sans passer par le cache. Les données lues depuis la RAM seront ensuite copiées dans le cache, mais ce seront les données valides écrites après le transfert DMA. Le contenu du cache est alors reconstitué au fur et à mesure des accès mémoire. L'invalidation est automatique sur les anciens GPU, elle est réalisée par le processeur de commande. Sur les GPU modernes, elle est réalisée par le programmeur, comme on va le voir dans la section immédiatement suivante.
La même chose a lieu, mais avec la mémoire unifiée. La mémoire unifiée n'implique pas de transfert DMA entre CPU et GPU, vu qu'il n'y a qu'une seule RAM unifiée. Par contre, le processeur écrit directement en mémoire RAM, mais les écritures ne sont pas propagées dans les caches du GPU. Pour corriger cela, lorsque le processeur envoie des données au GPU, il force le GPU à invalider ses caches. Il envoie une commande dédiée pour, qui précède les commandes liées au rendu 2D/3D/autres.
Au niveau du processeur, le processeur doit écrire les données dans la mémoire RAM, avant de faire le transfert DMA. Mais la présence de caches pose problème : les écritures peuvent être interceptées par le cache et ne pas être propagées en RAM. Pour éviter cela, les processeurs modernes marquent des blocs de mémoire comme "non-cacheables", à savoir que toute lecture/écriture dedans se fait sans passer par le cache. C'est une fonctionnalité très importante pour communiquer avec les périphériques. Pour les GPU dédiés/soudés, cela a un lien avec la mémoire vidéo mappée en mémoire. Plus haut, nous avions dit que la mémoire vidéo est visible dans l'espace d'adressage du processeur, à savoir qu'un bloc de mémoire est détourné pour adresser non pas la RAM, mais la mémoire vidéo. Et bien ce bloc de mémoire entier est marqué comme étant non-cacheable.
[[File:Cohérence des caches entre CPU et GPU avec mémoire unifiée, mécanismes.png|centre|vignette|upright=2|Cohérence des caches entre CPU et GPU avec mémoire unifiée, mécanismes]]
La situation peut être optimisée sur les GPU intégrés. Si le GPU est conçu pour, il n'y a pas besoin de marquer les données comme non-cacheables. Le cas le plus simple est celui où le CPU et le GPU partagent leur cache L3/L4. Dans ce cas, il n'y a qu'un seul cache L3/L4 qui ne contient qu'une seule copie valide, écrite par le CPU et lue par le GPU. Il faut juste garantir que la donnée soit lue par le GPU depuis le L3, mais c'est là une question d'inclusivité du cache, qui ne nous concerne pas ici. Si le CPU et le GPU ne partagent pas de cache, il suffit que le GPU puisse lire les caches du CPU. C'est la méthode utilisée sur l'APU Trinity d'AMD.
===La cohérence des caches entre processeurs de shaders===
Pour terminer, il faut voir la cohérence des caches entre processeurs de shaders. Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs auquel on aurait rajouté des ROPs, les circuits de la rastérisation et les unités de textures. Il n'est donc pas étonnant que les problèmes rencontrés sur les processeurs multicœurs soient aussi présents sur les GPU, la cohérence des caches ne fait pas exception.
Pour simplifier les explications, nous allons partir du principe que chaque processeur de shaders a son propre cache de données. Prenons deux processeur de shaders qui ont chacun une copie d'une donnée dans leur cache. Si un processeur de shaders modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches.
[[File:Cohérence des caches.png|centre|vignette|upright=2|Cohérence des caches]]
La réalité est cependant plus complexe, dans le sens où il n'y a souvent pas un cache par processeur de shaders, mais une hiérarchie de cache assez complexe, avec un cache L1 par processeur de shaders, un cache L2 partagé entre plusieurs processeur de shaders, des caches partagés entre tous les processeur de shaders, etc. Certains GPU partagent leur cache L1 d’instructions entre plusieurs processeur de shaders, d'autres non. Mais le principe reste valide, tant qu'un cache n'est pas partagé entre tous les processeurs de shaders : un cache peut contenir une donnée invalide, à savoir qu'elle a été modifiée dans le cache d'un autre processeur de shaders.
Pour corriger ce problème, les ingénieurs ont inventé des '''protocoles de cohérence des caches''' pour détecter les données périmées et les mettre à jour. Mais autant ces techniques sont faisables sur des CPU avec un nombre limité de cœurs, autant elles sont impraticables avec un GPU contenant une centaine de cœurs. Heureusement, la cohérence des caches est un problème bien moins important sur les GPU que sur les CPU. En effet, le rendu 3D implique un parallélisme de données : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches.
En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même. Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisés. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur insère une instruction qui invalide le cache, avant l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo et donc qu'il s'agit d'une donnée correcte.
C'est cette technique qui est utilisée pour les caches de textures, pour supporter les techniques de ''render-to-texture''. Dans le chapitre précédent, on a vu que lorsque d'une texture est modifiée par le GPU, les caches de texture sont réinitialisés. Les opérations de ''render-to-texture'' étant assez rares, il vaut mieux faire cela que de rendre les caches de texture accesibles en écriture. Ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches du GPU sont gérés avec le même principe. Pour les caches généralistes, certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.
{{NavChapitre | book=Les cartes graphiques
| prev=La hiérarchie mémoire d'un GPU
| prevText=La hiérarchie mémoire d'un GPU
| next=Le processeur de commandes
| netxText=Le processeur de commandes
}}{{autocat}}
dwnqqnk119yjygpnm7bcf03n89tuv86
745327
745326
2025-06-24T20:58:05Z
Mewtow
31375
/* La cohérence des caches entre CPU et GPU */
745327
wikitext
text/x-wiki
Pour rappel, il existe deux types de cartes graphiques : les cartes dédiées et les cartes intégrées. Les '''cartes graphiques dédiées''' sont des cartes graphiques branchées sur des connecteurs/ports de la carte mère. A l'opposé, tous les processeurs modernes intègrent une carte graphique, appelée '''carte graphique intégrée''', ou encore '''IGP''' (''Integrated Graphic Processor''). En somme, les cartes dédiées sont opposées à celles intégrées dans les processeurs modernes.
Les cartes graphiques dédiées ont de la mémoire vidéo intégrée à la carte graphique, sauf pour quelques exceptions dont on parlera plus tard. Par contre, les IGP n'ont pas de mémoire vidéo dédiée, vu qu'on ne peut pas intégrer beaucoup de mémoire dans un processeur. Et cela permet de classer les cartes graphiques en deux types :
* Les cartes graphiques à '''mémoire vidéo dédiée''', à savoir que la carte graphique dispose de sa propre mémoire rien qu'à elle, séparée de la mémoire RAM de l'ordinateur. On fait alors la distinction entre ''RAM système'' et ''RAM vidéo''.
* Les cartes graphiques à '''mémoire unifiée''', où la mémoire RAM est partagée entre le processeur et la carte graphique. Le terme "unifiée" sous-entend que l'on a unifié la mémoire vidéo et la mémoire système (la RAM).
[[File:Répartition de la mémoire entre RAM système et carte graphique.png|centre|vignette|upright=2.5|Répartition de la mémoire entre RAM système et carte graphique]]
Dans la grosse majorité des cas, les cartes vidéos dédiées ont une mémoire dédiée, alors que les IGP utilisent la mémoire unifiée. Mais il existe des cas où une carte vidéo dédiée est associée à de la mémoire unifiée. Par exemple, la toute première carte graphique AGP, l'Intel 740, ne possédait pas de mémoire vidéo proprement dite, juste un simple ''framebuffer''. Tout le reste, texture comme géométrie, était placé en mémoire système ! Les performances sont généralement ridicules, pour des raisons très diverses, mais les cartes de ce type sont peu chères, ce qui explique que l'Intel 740 a eu un petit succès sur les ordinateurs d'entrée de gamme.
Outre les cartes dédiées et intégrées, il faut aussi citer les cartes graphiques soudées sur la carte mère. Elles étaient utilisées sur les consoles de jeu vidéos assez anciennes, elles sont encore utilisées sur certains PC portables puissants, destinés aux ''gamers''. Pour ces dernières, il est possible d'utiliser aussi bien de la mémoire dédiée que de la mémoire unifiée. D'anciennes consoles de jeu avaient une carte graphique soudée sur la carte mère, qu'on peut facilement repérer à l’œil nu, avec une mémoire unifiée. C'est notamment le cas sur la Nintendo 64, pour ne citer qu'elle. D'autres avaient leur propre mémoire vidéo dédiée.
{|class="wikitable"
|-
!
! Mémoire unifiée
! Mémoire dédiée
|-
! GPU dédié
| Quelques rares GPU d'entrée de gamme || Tous les GPU, sauf de rares exceptions
|-
! GPU intégré
| Systématique ||
|-
! GPU soudé
| Anciennes consoles de jeu || Ordinateurs portables modernes
|}
==La mémoire unifiée==
Avec la mémoire unifiée, la quantité d'adresses détournées est généralement réglable avec un réglage dans le BIOS. On peut ainsi choisir d'allouer 64, 128 ou 256 mégaoctets de mémoire système pour la carte vidéo, sur un ordinateur avec 4 gigaoctets de RAM. Les GPU modernes sont plus souples et fournissent deux réglages : une quantité de RAM vidéo minimale et une quantité de RAM maximale que le GPU ne peut pas dépasser. Par exemple, il est possible de régler le GPU de manière à ce qu'il ait 64 mégaoctets rien que pour lui, mais qu'il puisse avoir accès à maximum 1 gigaoctet s'il en a besoin. Cela fait au total 960 mégaoctets (1024-64) qui peut être alloués au choix à la carte graphique ou au reste des programmes en cours d’exécution, selon les besoins.
[[File:Partage de la mémoire unifiée entre CPU et GPU.png|centre|vignette|upright=2|Répartition de la mémoire entre RAM système et carte graphique]]
===Les GPU dédiés avec mémoire unifiée===
Un défaut survient sur les cartes dédiées à mémoire unifiée, par exemple l'Intel 740. Pour lire en mémoire RAM, elles doivent passer par l'intermédiaire du bus AGP, PCI ou PCI-Express. Et ce bus est très lent, bien plus que ne le serait une mémoire vidéo normale. Aussi, les performances sont exécrables. J'insiste sur le fait que l'on parle des cartes graphiques dédiées, mais pas des cartes graphiques soudées des consoles de jeu.
D'ailleurs, de telles cartes dédiées incorporent un ''framebuffer'' directement dans la carte graphique. Il n'y a pas le choix, le VDC de la carte graphique doit accéder à une mémoire suffisamment rapide pour alimenter l'écran. Ils ne peuvent pas prendre le risque d'aller lire la RAM, dont le temps de latence est élevé, et qui peut potentiellement être réservée par le processeur pendant l’affichage d'une image à l'écran.
===Les GPU intégrés===
Les GPU dédiés à mémoire unifiée doivent passer par le bus AGP ou PCI Express pour lire des données en RAM système. Mais avec les GPU intégrés ou soudés, ce n'est pas le cas. Leur accès à la RAM est plus direct, il passe par les mêmes voies que le CPU. Les GPU et le processeur sont reliés à la RAM par un bus dédié appelé le '''bus mémoire'''. Le bus mémoire existe même sans GPU intégré : tous les processeurs sont reliés à la RAM par un tel bus. Mais si le CPU intègre un IGPU, il greffe l'IGPU sur ce bus mémoire existant.
Un défaut de cette approche est que le débit du bus système est partagé entre la carte graphique et le processeur. En clair, si le bus mémoire peut transférer 20 gigas de données par secondes, il faudra partager ces 20 gigas/seconde entre CPU et GPU. Par exemple, le CPU aura droit à 15 gigas par secondes, le GPU seulement 5 gigas. Divers circuits d'arbitrage s'occupent de répartir équitablement le bus système, selon les besoins, mais ça reste un compromis imparfait.
[[File:Echanges de données entre CPU et GPU avec une mémoire unifiée.png|centre|vignette|upright=2|Connexion du bus mémoire au CPU et à un GPU soudé sur la carte mère.]]
Le processeur et le GPU intègrent des mémoires caches, afin de réduire l'usage du bus mémoire. Et l'intégration des caches avec un GPU intégré est assez intéressante. Le CPU et l'IGPU peuvent avoir des caches séparés, ou partager certains caches. Par exemple, les processeurs Skylake et Sandy Bridge d'Intel partageaient le cache L3 entre CPU et GPU, mais CPU et GPU avaient leurs propres caches L2 et L1. En général, sur les processeurs qui font ainsi, le partage ne touche que le cache de dernier niveau, le cache le plus gros et le plus proche de la mémoire, à savoir le cache L3 ou L4. Il est partagé entre tous les cœurs du CPU et le GPU intégré.
Mais faire ainsi pose des problèmes assez complexes à expliquer ici, ce qui fait que les processeurs modernes séparent les caches du GPU et ceux du CPU. Le GPU et le CPU ont chacun leurs propres caches L1 et L2, comme avant, mais aussi leurs propres caches L3 et L4. La taille des caches L3/L4 est souvent assez différente, avec des caches L3/L4 plus gros pour le processeur que pour le GPU. De plus, le design des caches L3/L4 est alors différent : ils vont à des fréquences différentes, sont alimentés par des tensions différentes, etc. De plus, le cache du CPU est optimisé pour un temps d'accès faible, alors que celui du GPU est optimisé pour un fort débit mémoire.
[[File:Caches sur un iGPU.png|centre|vignette|upright=2.5|Caches sur un iGPU]]
N'allez cependant pas croire que les GPU intégrés n'ont que des désavantages. Ils disposent d'un avantage bien spécifique : la '''''zero-overhead copy''''', terme barbare mais qui cache une réalité très simple.
Avec une mémoire dédiée, le processeur doit copier les données de la RAM système dans la RAM vidéo. Et ces copies ont un cout en performance. Les copies en question se font souvent avant de démarrer le rendu 3D, par exemple lors du chargement d'un niveau dans un jeu vidéo. C'est en partie elles qui se cachent derrière les temps de chargement des jeux vidéo modernes. Copier plusieurs gigas de données dans la VRAM prend un peu de temps. Mais elles peuvent se faire lors du rendu, bien que ce soit plus rare. Lors du rendu 2D, les copies lors du rendu sont cependant plus fréquentes.
Par contre, avec la mémoire unifié, les échanges de données entre CPU et GPU se font en écrivant/lisant des données dans la RAM partagée entre CPU et GPU. La carte vidéo peut lire les informations envoyées directement dans la RAM, via DMA (''Direct Memory Access''). Pas besoin de faire de copie d'une mémoire à une autre : le processeur envoie juste une commande du GPU, et celle-ci intègre l'adresse des données en RAM. Le chargement des textures, du tampon de commandes ou d'autres données du genre, est donc très rapide, presque instantané.
Les copies de données posent problème si le CPU et le GPU utilisent des caches séparés. Le problème se manifeste si le processeur écrit une donnée en RAM, qui est ensuite lue par le GPU. En clair, lors des fameuses copies de données mentionnées plus haut. Dans ce cas, le processeur va écrire dans son cache et non en mémoire RAM. Le cache contiendra les données les plus récentes, pas la RAM. Le GPU lira alors les données en mémoire RAM, qui ne sont pas les données valides. Pour éviter cela, il existe diverses mécanismes de cohérence des caches qui sont intégrés au processeur, mais ils ne sont pas utilisés pour le GPU. A la place, des mécanismes alternatifs, spécifique au GPU, sont utilisés.
==La mémoire vidéo dédiée==
Après avoir vu la mémoire unifiée, voyons maintenant la mémoire dédiée. Les GPU doivent souvent échanger des données avec le processeur, des données doivent être copiées de la mémoire RAM vers la mémoire vidéo. Cependant, le CPU peut aussi adresser directement la mémoire vidéo. Pour expliquer comment, il faut faire quelques rappels sur l'espace d'adressage du processeur.
===La mémoire vidéo est mappée dans l'espace d'adressage du CPU===
L''''espace d'adressage''' du processeur est l'ensemble des adresses utilisables par le processeur. Par exemple, un processeur 16 bits peut adresser 2^16 = 65536 adresses, l'ensemble de ces adresses forme son espace d'adressage. L'espace d'adressage n'est pas toujours égal à la mémoire réellement installée. S'il n'y a pas assez de RAM installée, des adresses seront inoccupées. De plus, une partie de l'espace d'adressage peut être détourné pour communiquer avec les périphériques, grâce à la technique des '''entrées-sorties mappées en mémoire'''.
Et c'est ce qui est fait pour le GPU : une partie des adresses est détournée vers le GPU. Typiquement un bloc d'adresse de la même taille que la mémoire vidéo est détournée : elles n'adressent plus de la RAM, mais la directement la mémoire vidéo de la carte graphique. En clair, le processeur voit la mémoire vidéo et peut lire ou écrire dedans directement.
[[File:Espace d'adressage classique avec entrées-sorties mappées en mémoire.png|centre|vignette|upright=2|Espace d'adressage classique avec entrées-sorties mappées en mémoire]]
Intuitivement, on se dit que toute la mémoire vidéo est visible par le CPU, mais le bus PCI, AGP ou PCI Express ont leur mot à dire. Le bus PCI permettait au CPU d'adresser une fenêtre de 256 mégaoctets de VRAM maximum, en raison d’une sombre histoire de configuration des ''Base Address Registers'' (BARs). Les registres BAR étaient utilisés pour gérer les transferts DMA, mais aussi pour l'adressage direct.
Le PCI Express était aussi dans ce cas avant 2008. La gestion de la mémoire vidéo était alors difficile, mais on pouvait adresser plus de 256 mégaoctets, en déplaçant la fenêtre de 256 mégaoctets dans la mémoire vidéo. Après 2008, la spécification du PCI-Express ajouta un support de la technologie ''resizable bar'', qui permet au processeur d’accéder directement à plus de 256 mégaoctets de mémoire vidéo, voire à la totalité de la mémoire vidéo.
===La mémoire virtuelle des GPUs dédiés===
Intuitivement, on se dit que la carte graphique n'a accès qu'à la mémoire vidéo dédiée et ne peut pas lire de données dans la mémoire système, la RAM de l'ordinateur. Les cartes graphiques intègrent presque toutes des technologies pour lire directement des données en RAM, sans forcément les copier en mémoire vidéo. Les technologies en question permettent à la carte graphique d'adresser plus de RAM qu'en a la mémoire vidéo. Par exemple, si la carte vidéo a 4 giga-octets de RAM, la carte graphique peut être capable d'en adresser 8 : 4 gigas en RAM vidéo, et 4 autres gigas en RAM système.
Les technologies de ce genre ressemblent beaucoup à la mémoire virtuelle des CPU, avec cependant quelques différences. La mémoire virtuelle permet à un processeur d'utiliser plus de RAM qu'il n'y en a d'installée dans l'ordinateur. Par exemple, elle permet au CPU de gérer 4 gigas de RAM sur un ordinateur qui n'en contient que trois, le gigaoctet de trop étant en réalité simulé par un fichier sur le disque dur. La technique est utilisée par tous les processeurs modernes. La mémoire virtuelle des GPUs dédiés fait la même chose, sauf que le surplus d'adresses n'est pas stockés sur le disque dur dans un fichier pagefile, mais est dans la RAM système. Pour le dire autrement, ces cartes dédiées peuvent utiliser la mémoire système si jamais la mémoire vidéo est pleine.
[[File:Mémoire virtuelle des cartes graphiques dédiées.png|centre|vignette|upright=2|Mémoire virtuelle des cartes graphiques dédiées]]
Pour que la carte graphique ait accès à la mémoire système, elle intègre un circuit appelé la '''''Graphics address remapping table''''', abrévié en GART. Cela vaut aussi bien pour les cartes graphiques utilisant le bus AGP que pour celles en PCI-Express. La GART est techniquement une une ''Memory Management Unit'' (MMU), à savoir un circuit spécialisé qui prend en charge la mémoire virtuelle. La dite MMU étant intégrée dans un périphérique d'entrée-sortie (IO), ici la carte graphique, elle est appelée une IO-MMU (''Input Output-MMU'').
Le GPU utilise la technique dite de la pagination, à savoir que la RAM est découpée en pages de taille fixe, généralement 4 kilo-octets. La taille eds pages n'est pas un problème, car cela n'a pas d'intérêt pour un GPU de lire ou écrire des petites données en RAM système, 4 kilo-octets est une bonne taille. La mémoire système comme la mémoire vidéo sont toutes deux découpées en pages de 4 kiloctets et les deux échangent des pages. Quand le GPU veut accéder à une page en RMA système, la page est copiée en mémoire vidéo. S'il n'y a pas assez de place en mémoire vidéo, une page en mémoire vidéo est rapatriée en RAM système. La page rapatriée en RAM système est choisie par un algorithme spécialisé. Le GPU échange donc des pages entre RAM système et mémoire vidéo.
La traduction des adresses virtuelles en adresses physique se fait au niveau de la page. Une adresse est coupée en deux parts : un numéro de page, et la position de la donnée dans la page. La position dans la page ne change pas lors de la traduction d'adresse, mais le numéro de page est lui traduit. Le numéro de page virtuel est remplacé par un numéro de page physique lors de la traduction.
Pour remplacer le numéro de page virtuel en numéro physique, il faut utiliser une table de translation, appelée la '''table des pages''', qui associe un numéro de page logique à un numéro de page physique. Le système d'exploitation dispose de sa table des pages, qui n'est pas accesible au GPU. Par contre, le GPU dispose d'une sorte de mini-table des pages, qui contient les associations page virtuelle-physique utiles pour traiter les commandes GPU, et rien d'autre. En clair, une sorte de sous-ensemble de la table des pages de l'OS, mais spécifique au GPU. La mini-table des pages est gérée par le pilote de périphérique, qui remplit la mini-table des pages. La mini-table des pages est mémorisée dans une mémoire intégrée au GPU, et précisément dans la MMU.
===Les transferts ''Direct Memory Access''===
Pour échanger des données entre la RAM et la mémoire vidéo, les GPU utilisent la technologie ''Direct Memory Access'', aussi appelée DMA. Elle permet à un périphérique de lire/écrire un bloc de mémoire RAM, sans intervention du processeur, par l'intermédiaire du bus PCI Express.
Pour cela, le GPU intègre un circuit dédié à la gestion des transferts DMA, appelé le '''contrôleur DMA''', qui lit des données en RAM système pour les copier en RAM vidéo (ou inversement, mais c'est plus rare). Précisément, le contrôleur DMA copie un bloc de mémoire, de données consécutives en mémoire, par exemple un bloc de 128 mégaoctets, un bloc de 64 kiloctets, ou autre. Le processeur configure le contrôleur DMA en lui indiquant l'adresse de départ du bloc de mémoire, sa taille, et quelques informations annexes. Le contrôleur DMA lit alors les données une par une, et les écrit dans la mémoire vidéo.
Le contrôleur DMA est souvent fusionné avec l'IO-MMU. En effet, il s'occupe de la copie des pages entre mémoire vidéo et RAM système. Le contrôleur DMA reçoit des adresses provenant du pilote de périphérique, qui sont des adresses virtuelles. Mais vu qu'il accède à la RAM système, il doit faire la traduction entre adresses virtuelles qu'il reçoit et adresses physiques qu'il émet, ce qui implique qu'il sert d'IO-MMU. D'ailleurs, le controleur DMA est celui qui s'occupe de copier les pages mémoire de 4 kilo-octets.
La technologie existait déjà sur certaines cartes graphiques au format PCI, mais la documentation est assez rare. La carte graphique NV1 de NVIDIA, leur toute première carte graphique, disposait d'un contrôleur DMA. Le ''driver'' de la carte graphique programmait le contrôleur DMA en utilisant les adresses fournies par les applications/logiciels. Et il s'agissait d'adresses virtuelles, non d'adresses physiques en mémoire RAM. Pour résoudre ce problème, le contrôleur DMA intégrait une MMU, une unité de traduction d'adresse, qui traduisait les adresses virtuelles fournies par les applications en adresse physique en mémoire système.
: Le fonctionnement de cette IOMMU est décrite dans le brevet "US5758182A : DMA controller translates virtual I/O device address received directly from application program command to physical i/o device address of I/O device on device bus", des inventeurs David S. H. Rosenthal et Curtis Priem.
[[File:Microarchitecture du GPU NV1 de NVIDIA.png|centre|vignette|upright=2|Microarchitecture du GPU NV1 de NVIDIA]]
La technologie s'est démocratisée avec le bus AGP, dont la fonctionnalité dite d'''AGP texturing'' permettait de lire ou écrire directement dans la mémoire RAM, sans passer par le processeur. D'ailleurs, la carte graphique Intel i740 n'avait pas de mémoire vidéo et se débrouillait uniquement avec la mémoire système. C'est l'AGP qui a introduit le GART, la fameuse IO-MMU mentionnée plus haut.
L'arrivée du bus PCI-Express ne changea pas la donne, si ce n'est que le bus était plus rapide, ce qui améliorait les performances. Au début, seules les cartes graphiques PCI-Express d'entrée de gamme pouvaient accéder à certaines portions de la mémoire RAM grâce à des technologies adaptées, comme le TurboCache de NVIDIA ou l'HyperMemory d'AMD. Mais la technologie s'est aujourd'hui étendue. De nos jours, toutes les cartes vidéos modernes utilisent la RAM système en plus de la mémoire vidéo, mais seulement en dernier recours, soit quand la mémoire vidéo est quasiment pleine, soit pour faciliter les échanges de données avec le processeur. C'est typiquement le pilote de la carte graphique qui décide ce qui va dans la mémoire vidéo et la mémoire système, et il fait au mieux de manière à avoir les performances optimales.
==La cohérence des caches sur un GPU==
Pour terminer ce chapitre, nous allons parler de la '''cohérence des caches'''. La cohérence des caches est un problème qui se manifeste à plusieurs niveaux, quand on parle d'un GPU. Mais la situation la plus courante est celle où le processeur échange des données avec la carte graphique.
===La cohérence des caches entre CPU et GPU===
Un ordinateur contient un processeur et un GPU, chacun avec leurs caches séparés. Le processeur ou le GPU peuvent alors modifier la donnée chacun dans leur cache. Le résultat est que la donnée en RAM, celle dans le cache du GPU et celle dans le cache du CPU, seront totalement différentes. Il y a donc une incohérence : une lecture à une adresse ne donnera pas le même résultat selon que la lecture vienne du CPU, du GPU ou d'un périphérique DMA. Alors qu'il idéalement, on voudrait que les caches soient cohérents, à savoir qu'ils contiennent tous la même copie d'une donnée, ou presque.
[[File:Cohérence des caches entre CPU et GPU avec mémoire unifiée.png|centre|vignette|upright=2|Cohérence des caches entre CPU et GPU avec mémoire unifiée]]
Le problème se manifeste aussi bien avec la mémoire unifiée qu'avec une mémoire vidéo séparée. Le schéma précédent illustre ce qui se passe avec la mémoire unifiée. Avec une mémoire vidéo séparée, il faut prendre en compte les échanges entre RAM système et mémoire vidéo, avec des transferts DMA.
La majorité des échanges entre CPU et GPU se font du CPU vers le GPU. Le CPU prépare des textures, un tampon de sommet et diverses informations, qu'il envoie au GPU pour que celui-ci fasse le rendu 3D. Les échanges de données dans l'autre sens sont beaucoup plus rares. Sur un GPU dédié, le processeur envoie des données au GPU en trois temps :
* le CPU écrit des données en mémoire RAM ;
* les données sont copiées de la RAM vers la mémoire vidéo, via un transfert DMA ;
* les données sont lues par le GPU en mémoire vidéo.
La seconde étape disparait avec la mémoire unifiée. La cohérence des caches se manifeste entre l'étape 1 et 2, entre l'étape 2 et 3. Voyons d'abord le problème entre les deux dernières étapes, à savoir une fois que le transfert DMA est terminé et que le GPU veut lire les données.
Après un transfert DMA, une situation bien précise pose problème : quand un transfert DMA remplace des données par d'autres, à savoir qu'il écrase des données devenues inutiles, pour les remplacer par des données utiles. C'est très fréquent, les pilotes graphiques libèrent souvent de la mémoire vidéo pour la réallouer immédiatement après, afin de ne pas gaspiller de VRAM. Sans intervention du GPU, le remplacement des données aura été fait en mémoire vidéo, pas dans les caches du GPU. Et tout accès ultérieur au cache renverra la donnée écrasée.
[[File:Cache incoherence write.svg|centre|vignette|upright=2|Cohérence des caches avec DMA.]]
Pour éviter cela, le GPU invalide ses caches en cas de transfert DMA. Par invalider, on veut dire que le cache est réinitialisé, mis à zéro, il est rendu vierge de toute donnée. Ainsi, tout accès mémoire ultérieur se fera en mémoire RAM sans passer par le cache. Les données lues depuis la RAM seront ensuite copiées dans le cache, mais ce seront les données valides écrites après le transfert DMA. Le contenu du cache est alors reconstitué au fur et à mesure des accès mémoire. L'invalidation est automatique sur les anciens GPU, elle est réalisée par le processeur de commande. Sur les GPU modernes, elle est réalisée par le programmeur, comme on va le voir dans la section immédiatement suivante.
La même chose a lieu, mais avec la mémoire unifiée. La mémoire unifiée n'implique pas de transfert DMA entre CPU et GPU, vu qu'il n'y a qu'une seule RAM unifiée. Par contre, le processeur écrit directement en mémoire RAM, mais les écritures ne sont pas propagées dans les caches du GPU. Pour corriger cela, lorsque le processeur envoie des données au GPU, il force le GPU à invalider ses caches. Il envoie une commande dédiée pour, qui précède les commandes liées au rendu 2D/3D/autres.
Au niveau du processeur, le processeur doit écrire les données dans la mémoire RAM, avant de faire le transfert DMA. Mais la présence de caches pose problème : les écritures peuvent être interceptées par le cache et ne pas être propagées en RAM. Pour éviter cela, les processeurs modernes marquent des blocs de mémoire comme "non-cacheables", à savoir que toute lecture/écriture dedans se fait sans passer par le cache. C'est une fonctionnalité très importante pour communiquer avec les périphériques. Pour les GPU dédiés/soudés, cela a un lien avec la mémoire vidéo mappée en mémoire. Plus haut, nous avions dit que la mémoire vidéo est visible dans l'espace d'adressage du processeur, à savoir qu'un bloc de mémoire est détourné pour adresser non pas la RAM, mais la mémoire vidéo. Et bien ce bloc de mémoire entier est marqué comme étant non-cacheable.
[[File:Cohérence des caches entre CPU et GPU avec mémoire unifiée, mécanismes.png|centre|vignette|upright=2|Cohérence des caches entre CPU et GPU avec mémoire unifiée, mécanismes]]
La situation peut être optimisée sur les GPU intégrés. Si le GPU est conçu pour, il n'y a pas besoin de marquer les données comme non-cacheables. Le cas le plus simple est celui où le CPU et le GPU partagent leur cache L3/L4. Dans ce cas, il n'y a qu'un seul cache L3/L4 qui ne contient qu'une seule copie valide, écrite par le CPU et lue par le GPU. Il faut juste garantir que la donnée soit lue par le GPU depuis le L3, mais c'est là une question d'inclusivité du cache, qui ne nous concerne pas ici. Si le CPU et le GPU ne partagent pas de cache, il suffit que le GPU puisse lire les caches du CPU. C'est la méthode utilisée sur l'APU Trinity d'AMD.
[[File:Cohérence des caches entre CPU et GPU intégré.png|centre|vignette|upright=2|Cohérence des caches entre CPU et GPU intégré]]
===La cohérence des caches entre processeurs de shaders===
Pour terminer, il faut voir la cohérence des caches entre processeurs de shaders. Une carte graphique moderne est, pour simplifier, un gros processeur multicœurs auquel on aurait rajouté des ROPs, les circuits de la rastérisation et les unités de textures. Il n'est donc pas étonnant que les problèmes rencontrés sur les processeurs multicœurs soient aussi présents sur les GPU, la cohérence des caches ne fait pas exception.
Pour simplifier les explications, nous allons partir du principe que chaque processeur de shaders a son propre cache de données. Prenons deux processeur de shaders qui ont chacun une copie d'une donnée dans leur cache. Si un processeur de shaders modifie sa copie de la donnée, l'autre ne sera pas mise à jour. L'autre processeur manipule donc une donnée périmée : il n'y a pas cohérence des caches.
[[File:Cohérence des caches.png|centre|vignette|upright=2|Cohérence des caches]]
La réalité est cependant plus complexe, dans le sens où il n'y a souvent pas un cache par processeur de shaders, mais une hiérarchie de cache assez complexe, avec un cache L1 par processeur de shaders, un cache L2 partagé entre plusieurs processeur de shaders, des caches partagés entre tous les processeur de shaders, etc. Certains GPU partagent leur cache L1 d’instructions entre plusieurs processeur de shaders, d'autres non. Mais le principe reste valide, tant qu'un cache n'est pas partagé entre tous les processeurs de shaders : un cache peut contenir une donnée invalide, à savoir qu'elle a été modifiée dans le cache d'un autre processeur de shaders.
Pour corriger ce problème, les ingénieurs ont inventé des '''protocoles de cohérence des caches''' pour détecter les données périmées et les mettre à jour. Mais autant ces techniques sont faisables sur des CPU avec un nombre limité de cœurs, autant elles sont impraticables avec un GPU contenant une centaine de cœurs. Heureusement, la cohérence des caches est un problème bien moins important sur les GPU que sur les CPU. En effet, le rendu 3D implique un parallélisme de données : des processeurs/cœurs différents sont censés travailler sur des données différentes. Il est donc rare qu'une donnée soit traitée en parallèle par plusieurs cœurs, et donc qu'elle soit copiée dans plusieurs caches.
En conséquence, les GPU se contentent d'une cohérence des caches assez light, gérée par le programmeur. Si jamais une opération peut mener à un problème de cohérence des caches, le programmeur doit gérer cette situation de lui-même. Pour cela, les GPU supportent des instructions machines spécialisées, qui vident les caches. Par vider les caches, on veut dire que leur contenu est rapatrié en mémoire RAM, et qu'ils sont réinitialisés. Les accès mémoire qui suivront l'invalidation trouveront un cache vide, et devront recharger leurs données depuis la RAM. Ainsi, si une lecture/écriture peut mener à un défaut de cohérence problématique, le programmeur insère une instruction qui invalide le cache, avant l'accès mémoire potentiellement problématique. Ainsi, on garantit que la donnée chargée/écrite est lue depuis la mémoire vidéo et donc qu'il s'agit d'une donnée correcte.
C'est cette technique qui est utilisée pour les caches de textures, pour supporter les techniques de ''render-to-texture''. Dans le chapitre précédent, on a vu que lorsque d'une texture est modifiée par le GPU, les caches de texture sont réinitialisés. Les opérations de ''render-to-texture'' étant assez rares, il vaut mieux faire cela que de rendre les caches de texture accesibles en écriture. Ce modèle d'invalidation du cache au besoin est parfaitement adapté. Les autres caches du GPU sont gérés avec le même principe. Pour les caches généralistes, certains GPU modernes commencent à implémenter des méthodes plus élaborées de cohérence des caches.
{{NavChapitre | book=Les cartes graphiques
| prev=La hiérarchie mémoire d'un GPU
| prevText=La hiérarchie mémoire d'un GPU
| next=Le processeur de commandes
| netxText=Le processeur de commandes
}}{{autocat}}
cqrkgig9ho0kcc5n3g8xc3td8t1p7gw
Les cartes graphiques/La microarchitecture des processeurs de shaders
0
81538
745288
744012
2025-06-24T13:50:47Z
Mewtow
31375
/* Les hybrides SIMD/VLIW et les instructions à co-issue */
745288
wikitext
text/x-wiki
La conception interne (aussi appelée microarchitecture) des processeurs de ''shaders'' possède quelques particularités idiosyncratiques. La microarchitecture des processeurs de ''shaders'' est particulièrement simple. On n'y retrouve pas les fioritures des CPU modernes, tant utiles pour du calcul séquentiel : pas d’exécution dans le désordre, de renommage de registres, et autres techniques avancées. En conséquence, les unités de décodage et/ou de contrôle sont relativement simples, peu complexes. La majeure partie du processeur est dédié aux unités de calcul.
[[File:Cpu-gpu.svg|centre|vignette|upright=2.0|Comparaison entre l'architecture d'un processeur généraliste et d'un processeur de shaders.]]
==Les unités de calcul d'un processeur de shader SIMD==
Pour rappel, un processeur de shader supporte plusieurs types d'instructions. Au minimum, il supporte des ''instructions SIMD''. Mais il peut aussi gérer des ''instructions scalaires'', à savoir qu'elles travaillent sur des entiers/flottants isolés, non-regroupés dans un vecteur SIMD. Typiquement, il y a plusieurs types d'instructions scalaires : les calculs entiers, les calculs flottants simples, les calculs flottants complexes dit transcendantaux (calculs trigonométriques, des exponentielles, des logarithmes, des racines carrées ou racines carrées inverse).
Et ces différentes instructions ont toutes des unités de calcul dédiées, ce qui fait qu'un processeur de shader contient un grand nombre d'unités de calcul très différentes. Le cœur est une unité de calcul SIMD, qui se charge des instructions SIMD. Mais d'autres unités de calcul sont présentes, surtout sur les architectures modernes. Il n'est pas rare de trouver une unité de calcul entière, ou des unités de calcul flottantes spécialisées pour les opérations transcendantales.
===Les unités de calcul SIMD===
[[File:SIMD2.svg|vignette|Une unité de calcul SIMD.]]
Un processeur de shader incorpore une unité de calcul SIMD, qui effectue plusieurs calculs en parallèle. Elle regroupe plusieurs ALU flottantes regroupées ensemble et avec quelques circuits pour gérer les débordements et d'autres situations. En théorie, une unité de calcul SIMD regroupe autant d'unité de calcul qu'il y a d'entiers/flottants dans un vecteur. Par exemple, pour additionner deux vecteurs contenant chacun 16 flottants, il faut utilise 16 additionneurs flottants. Ce qui fait qu'une opération sur un vecteur est traité en une seule fois, en un cycle d'horloge. Une contrainte importante est que toutes les sous-ALU effectuent la même opération : ce ne sont pas des ALU séparées qu'on peut commander indépendamment, mais une seule ALU regroupant des circuits de calcul distinct.
Le cout en circuit est d'autant plus grand que les vecteurs sont longs et le cout est approximativement proportionnel à la taille des vecteurs. Entre des vecteurs de 128 et 256 bits, l'unité de calcul utilisera globalement deux fois plus de circuits avec 256 bits qu'avec 128. Même chose pour les registres, mais c'est là un cout commun à toutes les architectures.
Il y a quelques unités de calcul SIMD où le calcul se fait en deux fois, car on n'a que la moitié des unités de calcul. Par exemple, pour un vecteur de 32 flottants, on peut utiliser 16 unités de calcul, mais le temps de calcul se fait en deux cycles d'horloge. Les opérations sur les vecteurs sont donc faites en deux fois : une première passe pour les 16 premiers éléments, une seconde passe pour les 16 restants. L'implémentation demande cependant qu'une instruction de calcul soit décodée en deux micro-opérations. Par exemple, une instruction SIMD sur des vecteurs de 32 éléments est exécutée par deux micro-instructions travaillant sur des vecteurs de 16 éléments. On économise ainsi pas mal de circuits, mais cela se fait au détriment de la performance globale.
L'avantage est que cela se marie bien avec l'abandon des opérations pour lesquelles masques dont tous les bits sont à 0. Par exemple, prenons une instruction travaillant sur 16 flottants, exécutée en deux fois sur 8 flottants. Si le masque dit que les 8 premières opérations ne sont pas à exécuter, alors l'ALU ne fera que le calcul des 8 derniers flottants. Pour cela, le décodeur doit lire le registre de masque lors du décodage pour éliminer une micro-instruction si besoin, voire les deux si le masque est coopératif.
===Plusieurs unités SIMD, liées au format des données===
Il faut préciser qu'il y a une séparation entre unités SIMD flottantes simple et double précision. Pour le dire plus clairement, il y a des unités SIMD pour les flottants 32 bits, d'autres pour les flottants 64 bits, et même d'autres pour les flottants 16 bits. Les flottants 64 bits sont utilisés dans les applications GPGPU, les flottants 16 et 32 bits le sont dans le rendu 3D, et les flottants 16 bits pour tout ce qui lié à l'IA. Malheureusement, on doit utiliser des ALU flottantes séparées pour chaque taille, le format des flottants n'aidant pas.
Depuis plus d'une décennie, les cartes graphiques ont des unités SIMD à la fois pour les calculs entiers et flottants. Elles sont censées être séparées. Pour NVIDIA, avant l'architecture Turing, les unités SIMD entières et flottantes sont décrites comme séparées dans leurs ''white papers'', avec des unités INT32 et FLOAT32 séparées, combinées à d'autres unités de calcul. L'architecture Volta a notamment des unités INT32, FLOAT32 et FLOAT64 séparées. A partir de l'architecture Ampere, il semblerait que les unités SIMD soient devenues capables de faire à la fois des calculs flottants et entiers, pour la moitié d'entre elles. Mais il se pourrait simplement qu'elles soient physiquement séparées, mais reliées aux reste du processeur de manière à ne pas être utilisables en même temps.
Il est cependant possible que sur d'anciennes architectures, les unités entières et flottantes partagent des circuits, notamment pour ce qui est de la multiplication. En effet, une unité de calcul flottante contient des circuits pour faire des calculs entiers pour additionner/multiplier les mantisses et les exposants. Il est possible d'utiliser ce circuit Un exemple est le cas de l'architecture GT200 d'NVIDIA, sur laquelle les "pseudo-ALU" entières SIMD étaient limitées à des multiplications d'opérandes 24 bits, ce qui correspond à la taille d'une mantisse d'un flottant 32 bits. Le design exact des ALU n'est pas connu.
===Les unités de calcul scalaires===
Les GPU modernes incorporent une '''unité de calcul entière scalaire''', séparée de l'unité de calcul SIMD. Elle gère des calculs scalaires, à savoir qu'elle ne travaille pas sur des vecteurs. Elle gère divers calculs, comme des additions, soustractions, comparaisons, opérations bit à bit, etc. Elle exécute les instructions de calcul entière sur des nombres entiers isolés, de plus en plus fréquentes dans les shaders.
Elle est parfois accompagnée d'une unité de calcul pour les branchements. Par branchements, on veut parler des vrais branchements similaires à ceux des CPU, qui effectuent des tests sur des entiers et effectuent des branchements conditionnels. Ils n'ont rien à voir avec les instructions à prédicat qui elles sont spécifiques à l'unité de calcul vectorielles. Ce sont des instructions séparées, totalement distinctes
Les processeurs de shaders incorporent aussi une '''unité de calcul flottante scalaire''', utilisée pour faire des calculs sur des flottants isolés. L'unité de calcul gère généralement des calculs simples, comme les additions, soustractions, multiplications et divisions. Il s'agit typiquement d'une unité de calcul spécialisée dans l’opération ''Multiply-And-Add'' (une multiplication suivie d'une addition, opération très courante en 3D, notamment dans le calcul de produits scalaires), qui ne gère pas la division.
L'unité de calcul flottante est souvent accompagnée d'une unité de calcul spécialisée qui gère les calculs transcendantaux, avec une gestion des calculs trigonométriques, de produits scalaires ou d'autres opérations. Elle porte le nom d''''unité de calcul spéciale''' (''Special Function Unit''), ou encore d'unité de calcul transcendantale, et elle a d'autres appellations. Elle est composée de circuits de calculs accompagnés par une table contenant des constantes nécessaires pour faire les calculs.
Rappelons que les registres SIMD et les registres scalaires sont séparés et ne sont pas adressés par les mêmes instructions. Les registres scalaires sont placés dans un banc de registre physiquement séparé du banc de registres SIMD. Le banc de registre scalaire est relié à sa propre ALU scalaire, il y a vraiment une séparation physique entre registres scalaires et SIMD. Il existe cependant un système d'interconnexion qui permet d'envoyer un scalaire aux unités SIMD, ce qui est utile pour les opérations de produits scalaire ou autres.
===L'unité de texture/lecture/écriture===
L'unité d'accès mémoire s'occupe des lectures et écriture en général, et elle prend en charge les accès aux textures, le filtrage de textures et tout un tas de fonctionnalités liées aux textures. La seule différence entre un accès aux textures et une lecture/écriture en mémoire est que les circuits de filtrage de texture sont contournés dans une lecture/écriture normale. Dans ce qui suit, nous allons l'appeler l'unité de texture par souci de simplification.
==La gestion des dépendances de données==
Un processeur de ''shaders'' SIMD contient donc beaucoup d'unité de calcul, qui peuvent en théorie fonctionner en parallèle. Il est en théorie possible d'exécuter des instructions séparés dans des unités de calcul séparées. Par exemple, reprenons l'exemple de l'unité de vertices de la Geforce 6800, mentionné au-dessus. Elle dispose d'une unité de calcul SIMD MAD, et d'une unité de texture, ainsi que d'une unité de calcul scalaire transcendantale. Il en en théorie possible de faire, en même temps, un calcul dans chaque ALU. Pendant que l'unité transcendantale fait un calcul trigonométrique quelconque, l'autre ALU effectue des calculs SIMD sur d'autres données.
Pour cela, une possibilité est d'utiliser des instructions à ''co-issue''. Le problème est que ces instructions sont surtout utiles pour exécuter des instructions scalaires en parallèle d'instruction SIMD, mais guère plus. Aussi, nous allons mettre la ''co-issue'' de côté. Dans ce qui suit, nous allons partir du principe que le processeur peut démarrer une nouvelle instruction par cycle d'horloge. Et cela permet malgré tout d’exécuter plusieurs instructions en même temps dans des unités de calcul séparées. La raison est que la plupart des instructions prend plusieurs cycles d'horloge à s’exécuter. Ainsi, pendant qu'une instruction en est à son second ou troisième cycle dans une ALU, il est possible de démarrer une nouvelle instruction dans une ALU inoccupée, s'il y en a une. La seule contrainte est que les deux instructions soient indépendantes.
Les processeurs de ''shaders'' en sont capables, tout comme les CPU. Mais les CPU utilisent au mieux cette possibilité. Ils intègrent des circuits d'exécution dans le désordre, de renommage de registre, et bien d'autres. Mais leur implémentation demande un budget en transistors conséquent, que les GPU ne peuvent pas se permettre. A la place, ils utilisent une technique appelée le '''''multithreading'' matériel''', une technique qui vient du monde des processeurs, où cette technique a été appliquée à de nombreuses reprises. Vous connaissez sans doute l'''hyperthreading'' d'Intel ? C'est une version basique du ''multithreading'' matériel.
L'idée est d'exécuter plusieurs programmes en même temps sur le même processeur, le processeur commutant de l'un à l'autre suivant les besoins. Par exemple, si un ''thread'' est bloqué par un accès mémoire, d'autres ''threads'' exécutent des calculs dans l'unité de calcul en parallèle de l'accès mémoire. Les programmes en question sont appelés des ''threads'', l'équivalent sur un GPU est un ''warp'', ni plus ni moins. Un processeur de ''shader'' commute donc régulièrement d'un ''warp'' à l'autre, suivant les besoins. Dans ce qui va suivre, nous allons voir dans quelles situations un processeur de 'shader'' change de ''thread''/''warp'' en cours d'exécution. Suivant le GPU, les situations ne sont pas les mêmes.
===Les lectures non-bloquantes avec ''multithreading'' matériel===
Les processeurs de ''shader'' sont connectés à une mémoire vidéo très lente, avec un temps d'accès élevé, qui se rattrape avec un débit binaire important. La conséquence est qu'un accès à une texture, c'est long : si celle-ci est lue depuis la mémoire vidéo, le temps d'attente est d'une bonne centaine de cycles d'horloges. Pour limiter la casse, les unités de texture incorporent un cache de texture, mais cela ne suffit pas toujours à alimenter les processeurs de ''shaders'' en données. Et cs derniers ne peuvent pas recourir à des techniques avancées communes sur les CPU, comme l’exécution dans le désordre : le cout en circuit serait trop important. Fort heureusement, les processeurs de ''shaders'' disposent d'optimisations pour remplir ces temps d'attente avec des calculs indépendants de la texture lue.
Les anciennes cartes graphiques d'avant les années 2000 géraient une forme particulière de '''lectures non-bloquantes'''. L'idée est simple : pendant qu'on effectue une lecture dans l'unité de texture, on peut faire des calculs en parallèle dans les ALU de calcul. Mais la technique n'est possible que si les instructions de calcul n'ont pas comme opérande un texel en cours de lecture. Il ne faut pas qu'il y a ai une dépendance entre les instructions exécutées dans l'ALU et un texel pas encore lu. Pour cela, la carte graphique fait des vérifications sur les registres. La lecture charge le texel dans un registre, appelé le registre de destination (sous entendu, de destination de la lecture). Les instructions qui n'utilisent pas ce registre peuvent s'exécuter sans problèmes : elles sont indépendantes de la lecture. Mais les autres ont une dépendance et ne sont pas exécutées.
Détecter les dépendances demande juste de mémoriser le registre de destination dans un registre temporaire, et de faire des comparaisons. Pour cela, le processeur de ''shader'' incorpore un circuit situé juste après le décodeur d'instruction, appelé l'unité d'émission. Elle compare les registres utilisés par l'instruction avec le registre de destination. Si il y a correspondance, l'instruction est mise en attente. La mise en attente bloque alors toutes les instructions suivantes. En clair, dès qu'on tombe sur une instruction dépendante, le processeur cesse d'émettre des instructions dans l'ALU, et attend la fin de la lecture. Il n'a pas vraiment le choix. Faire autrement serait possible, mais demanderait d'implémenter des techniques d'exécution dans le désordre très gourmandes en circuits.
Depuis l'époque de Direct X 9, les GPU combinent les lectures non-bloquantes avec le ''multithreading'' matériel. L'idée est que si un ''thread'' effectue un accès mémoire, le ''thread'' est mis en pause pendant l'accès mémoire et laisse la place à un autre. Le ''thread'' est réveillé une fois que l'accès mémoire est terminé. L'avantage est que si un ''thread'' est mis en pause par une lecture, les autres ''threads'' récupèrent les cycles d'horloge du ''thread'' mis en pause. Par exemple, sur un processeur qui gère 16 ''threads'' concurrents, si l'un d'eux est mis en pause, le processeur changera de ''thread'' tous les 15 cycles au lieu de 16. Ou encore, un ''thread'' aura droit à deux cycles consécutifs pour s’exécuter. Faire ainsi marche assez bien si on a beaucoup de ''threads'' dont peu d'entre eux font des lectures/écritures.
L'avantage est que si un thread est mis en pause par une lecture, les autres threads récupèrent les cycles d'horloge du thread mis en pause. Par exemple, sur un processeur qui gère 16 threads concurrents, si l'un d'eux est mis en pause, le processeur changera de thread tous les 15 cycles au lieu de 16. Ou encore, un thread aura droit à deux cycles consécutifs pour s’exécuter. Faire ainsi marche assez bien si on a beaucoup de threads dont peu d'entre eux font des lectures/écritures.
L'implémentation de cette technique est assez simple au premier abord. L'unité de chargement a plusieurs ''program counter'', un par ''thread''. Un circuit dédié sait quels ''threads'' sont mis en pause, et en envoie un aux unités de calculs. La technique impose cependant que les registres soient dupliqués, pour que chaque ''thread'' ait ses propres registres rien qu'à lui. Sans cela, impossible de passer rapidement d'un ''thread'' à l'autre à chaque cycle. Un processeur de ''shader'' peut exécuter entre 16 et 32 ''thrads''/''warps'', ce qui multiplie le nombre de registres par 16/32. Et il y a la même chose avec d'autres structures matérielles, comme les files de lecture/écriture de l'unité d'accès mémoire.
[[File:Aperçu de l'architecture d'un processeur multithreadé.png|centre|vignette|upright=2|Aperçu de l'architecture d'un processeur multithreadé]]
===Le ''scoreboard'' des GPU des années 2000-2010===
Les lectures non-bloquantes permettent de gérer le cas des instructions d'accès mémoire. Mais il y a d'autres instructions qui prennent plus d'un cycle d'horloge pour s'exécuter. Les instructions de calcul basiques d'un GPU prennent 2 à 3 cycles d'horloge dans le meilleur des cas, parfois plus d'une dizaine. De telles instructions sont autant d'opportunités pour exécuter plusieurs instructions en même temps. Pendant qu'une instruction multicycle occupe une ALU, les autres ALU peuvent accepter d'autres instructions.
Les processeurs de ''shaders'' SIMD peuvent démarrer une nouvelle instruction scalaire/SIMD par cycle, si les conditions sont réunies. Le problème est alors le suivant : deux instructions démarrées l'une après l'autre doivent occuper des ALU différentes, mais en plus elles doivent manipuler des données différentes.
Prenons un exemple simple : une instruction calcule un résultat, qui est utilisé comme opérande par une seconde instruction. La seconde ne doit pas démarrer tant que la première n'a pas enregistré son résultat dans les registres. Il s'agit d'une dépendance dite RAW (''Read After Write'') typique, que la carte graphique doit gérer automatiquement. Il existe aussi d'autres dépendances liées au fait que deux instructions utilisent les mêmes registres, mais laissons-les de côté pour le moment. Il faut dire qu'avec 4096 registres par ''shader'', elles sont plus rares. Les dépendances en question sont regroupées sous le terme de '''dépendances de données'''.
S'il y a une dépendance de données entre deux instructions, on ne peut pas les exécuter en parallèle dans des unités de calcul séparées. Sur les cartes graphiques des années 2000-2010, un circuit dédié appelé le '''''scoreboard''''' détectait ces dépendances. Pour cela, il vérifie si deux instructions utilisent les mêmes registres.
: Précisons que le grand nombre de registres et de ''threads'' fait qu'un ''scoreboard'' classique, tel que décrit dans les cours d'architecture des ordinateurs, devient rapidement impraticable. Aussi, une implémentation alternative est utilisée, bien que les détails ne soient pas connus à ce jour. Quelques brevets donnent des détails d'implémentation, mais on ne sait pas s'ils sont à jour. Les curieux peuvent tenter de lire le brevet américain numéro #7,634,621, nommé ''"Register File Allocation"'', déposé par NVIDIA durant décembre 2009.
A chaque cycle, le ''scoreboard'' reçoit une instruction provenant du décodeur et vérifie si elle peut être exécutée. Il vérifie les dépendances de données, mais aussi d'autres formes de dépendances. Notamment, il vérifie qu'il y a une unité de calcul libre pour exécuter l'instruction.S'il n'y a pas de dépendance, elle est envoyée à une unité de calcul inoccupée, s'il y en a une. Mais s'il y a une dépendance de donnée, le processeur de ''shader'' met en pause le ''thread''/''warp'' et bascule sur un autre ''thread''. Le ''thread'' est mis en pause tant que la dépendance n'est pas résolue. A chaque cycle, le processeur détecte les dépendances et choisit une instruction parmi celles qui peuvent s'exécuter, peu importe leur ''thread''.
L'implémentation sépare le processeur de ''shader'' en deux sections séparées par une mémoire tampon qui met en attente les instructions SIMD. Les instructions SIMD sont chargées et décodées, puis sont placées dans une ou plusieurs '''files d'instruction'''. Le cas le plus simple à comprendre utilise une file d'instruction par ''thread''. Les instructions attendent leur tour dans la file d'instruction. L'unité d'émission vérifie à chaque cycle quelles instructions sont prêtes à s'exécuter, dans chaque file d'instruction. L'unité d'émission choisit un ''thread'' et envoie l'instruction la plus ancienne dans la file d'instruction aux unités de calcul. Les instructions qui ont besoin d'un résultat pas encore calculé ou lu depuis la mémoire attendent juste.
[[File:FGMT sur un processeur de shaders.png|centre|vignette|upright=2|FGMT sur un processeur de shaders]]
Mais pour cela, il faut que ces derniers aient des instructions en attente dans la file d'instruction. Le processeur doit avoir chargé assez d'instructions en avance, il faut que chaque ''thread'' ait assez de réserve pour exécuter plusieurs instructions consécutives. Pour éviter ce problème, le processeur profite du fait que le chargement des instructions depuis la mémoire et leur exécution se font en même temps : le processeur charge des instructions pendant qu'il en exécute d'autres. Plus haut, nous avions dit que l'unité de chargement mémorise plusieurs ''program counter'', un par ''thread'', et choisit à chaque cycle l'un d'entre eux pour charger une instruction. Le choix en question est synchronisé avec l'émission des instructions. Si un ''thread'' émet une instruction, ce même ''thread'' charge une instruction au même moment. Sauf si la file d'instruction du ''thread'' est déjà pleine, auquel cas un autre ''thread'' est choisit.
===L'encodage explicite des dépendances sur les GPU post-2010===
Depuis environ 2010, les GPU n'utilisent plus de ''scoreboard'' proprement dit. A la place, les GPU modernes déportent la détection des dépendances de données à la compilation. L'idée est que chaque instruction contient quelques bits pour dire au processeur : tu peux lancer 1, 2, 3 instructions à la suite sans problème. La technique porte le nom d''''anticipation de dépendances explicite''' (''Explicit-Dependence lookahead''). Un exemple historique assez ancien est le processeur Tera MTA (''MultiThreaded Architecture''), qui utilisait cette technique.
Les GPU NVIDIA modernes utilisent plusieurs bits par instruction pour gérer les dépendances de données. Un premire mécanisme est utilisé pour bloquer l'émission d'une nouvelle instruction pendant x cycles. Il utilise un '''''stall counter''''', qui mémorise le nombre de cycles d'attente. Une instruction peut initialiser le ''stall counter'' avec une valeur de base, qui indique combien de cycles attendre. Le ''stall counter'' est décrémenté à chaque cycle d'horloge et une nouvelle instruction s'exécute seulement quand le compteur atteint 0. Il est utilisé quand une instruction productrice prenant X cycles est suivie par une instruction consommatrice. Le ''stall counter'' est alors initialisé à la valeur X.
La méthode précédente ne vaut cependant que pour les instructions dont le compilateur peut prédire la durée. En clair, les accès mémoire et certaines instructions spéciales ne sont pas prises en charge. Pour les gérer, les GPU modernes utilisent un autre mécanisme, basé sur des '''compteurs de dépendances'''. Il y en a plusieurs par ''thread'', entre 5 et 10 selon le GPU. Une instruction productrice réserve un de ces compteur, l'incrémente quand elle démarre son exécution/est émise, le décrémente quand la dépendance est résolue. Les instructions consommatrices doivent attendre que le compteur tombe à zéro pour s'exécuter. Elles précisent quels compteurs regarder avec un '''masque de compteurs de dépendance''', encodé directement dans l'instruction elle-même. Le masque est composé de bits à 0/1, un par compteur.
Précisément, chaque instruction productrice se réserve deux compteurs, pour gérer les trois types de dépendances de données (RAW, WAR et WAW). Le premier compteur est décrémenté quand le résultat est écrit dans les registres, ce qui gère naturellement les dépendances RAW et WAW. Le second est décrémenté quand l'instruction a lu ses opérandes, ce qui gère les dépendances WAR. Les autres instructions regarde l'un ou l'autre des compteurs selon leur situation.
Il arrive même que le switch de ''thread'' soit géré par des bits intégrés dans l'instruction. Par exemple, sur les GPU NVIDIA modernes, chaque instruction contient un bit '''''yield''''' qui indique qu'il faut changer de ''thread'' une fois l'instruction émise. En clair, il indique que l'instruction risque de durer longtemps, a des dépendances avec la mémoire ou autre chose qui fait qu'il est préférable de changer de ''thread''.
==Le banc de registres d'un processeur de ''shader''==
Les GPU disposent d'un grand nombre de registres. Les normes de DirectX et Open GL imposent que les shaders modernes gèrent au moins 4096 registres généraux par instance de shader, avec des registres spécialisés en plus. En soi, 4096 est énorme ! Mais au-delà de ces normes, le FGMT implique de dupliquer des registres par le nombre de ''threads hardware'' simultanés. Avec le FGMT, les registres devront être dupliqués pour que chaque ''thread'' ait ses propres registres rien qu'à lui. Sans cela, impossible de passer rapidement d'un ''thread'' à l'autre à chaque cycle.
Maintenant, faisons quelques calculs d'épiciers. Un processeur de ''shader'' peut exécuter entre 16 et 32 ''threads''/''warps'', ce qui multiplie le nombre de registres par 16/32. En multipliant par les 4096 registres nécessaires, cela fait 128 kilooctets de mémoire rien que pour les registres. Et c'est pour un seul cœur ! Si on multiplie par le nombre de cœurs, on trouve que les cartes graphiques modernes ayant plusieurs processeurs de ''shaders'' ont facilement entre 32 768 et 65 536 registres de 32 bits, ce qui est énorme ! Il y a plus de mémoire gaspillée dans les registres que dans le cache L1 ou L2 !
Et ce grand nombre de registres par cœur pose quelques problèmes. Les registres sont regroupés dans une petite mémoire SRAM, adressable, appelée le '''banc de registre'''. Et comme toutes les mémoires, plus ce banc de registres est grand, plus il est lent. En conséquence, lire un opérande dans les registres prend beaucoup de temps. Du moins, c'est le cas sans optimisations. Et les GPU implémentent de nombreuses parades pour limiter le nombre de registres réellement présents dans leur silicium.
===L'allocation dynamique/statique des registres par ''thread''===
Les GPU modernes n'implémentent pas le nombre maximal de registres demandés. Par exemple, si je prends les GPU AMD de type RDNA 4, il peuvent gérer 16 ''threads'' hardware simultanés, chacun ayant accès à 256 registres, registres faisant 128 octets chacun. On s'attend à avoir un banc de registre de 16 * 256 * 128 octets, soit 512 Kilo-octets. En somme, un banc de registre de la taille du cache L1. Sauf que les GPUs en question intègrent moins de registres que prévu ! Ils ont précisément une taille de 192 kilo-octets, soit 96 registres pour chacun des 16 ''threads''.
En effet, 256 registres est un nombre maximal, que la plupart des ''shaders'' n'utilise pas totalement. La plupart des ''shaders'' utilise entre 64 et 128 registres, rarement moins, rarement plus. Aussi, le GPU partitionne le banc de registres à la demande entre les ''threads'', en leur donnant seulement une partie des registres. Le partitionnement peut être pseudo-statique, à savoir que le banc de registre est découpé en parts égales pour chaque ''thread'', ou dynamique avec un nombre de registre variant d'un ''thread'' à l'autre, selon les besoins.
Prenons l'exemple d'un '''partitionnement pseudo-statique''', avec l'exemple des GPU AMD RDNA 1 et 2. Leur banc de registre fait 1024 registres, de 128 octets chacun, soit 128 KB au total. Le GPU gère 16 ''threads'' simultanés maximum. Avec un seul ''thread'' d'exécuté, le ''thread'' unique peut utiliser les 1024 registres du banc de registre pour lui tout seul. Avec deux ''threads'', chacun aura droit à 512 registres, soit la moitié du cas précédent. Avec 16 ''thread'' simultanés, chaque ''thread'' a accès à 64 registres, pas plus. Et ainsi de suite : le nombre de registre par ''thread'' est égal à la taille du banc de registre divisée par le nombre de ''threads''.
A l'heure où j'écris ces lignes, courant 2025, les GPU Intel se contentent d'un partitionnement statique très limité. Avant les GPU d'architecture Battlemage, il n'y avait pas de partitionnement du banc de registre, tous les ''threads'' avaient 128 registre à leur disposition. Les GPU Battlemage et ses successeurs ont introduit un partitionnement limité, avec deux modes : un mode sans partitionnement où tous les ''threads'' ont accès à 128 registres, un mode avec partitionnement qui divise le nombre de ''thread'' par deux et leur donne chacun 256 registres. Pas de possibilité de diviser plus le nombre de ''threads''.
Les GPU AMD et NVIDIA sont eux plus compétents niveau partitionnement statique. Par exemple, les GPU RDNA 4 supportent les partitionnements suivants :
* 16 ''threads'' avec 96 registres chacun ;
* 12 ''threads'' avec 120 registres chacun ;
* 10 ''threads'' avec 144 registres chacun ;
* 9 ''threads'' avec 168 registres chacun ;
* 8 ''threads'' avec 192 registres chacun ;
* 7 ''threads'' avec 216 registres chacun ;
* 6 ''threads'' avec 240 registres chacun ;
* 5 ''threads'' avec 256 registres chacun.
Le partitionnement pseudo-statique est simple à implémenter, il ne demande pas beaucoup de circuits pour fonctionner. Il est rapide et a de bonnes performances pour le rendu graphique en rastérisation. La raison est qu'en rastérisation, les différents ''threads'' sont souvent des copies/instances d'un même ''shader'' qui travaillent sur des données différentes. Leur donner le même nombre de registres colle bien avec cet état de fait.
Cependant, si les différents ''threads'' sont des ''shaders'' différents, les choses ne sont pas optimales. Un ''shader'' utilisera plus de registres que l'autre, leur donner le même nombre de registres n'est pas optimal. Par exemple, imaginons que l'on a deux shaders, nommés shaders 1 et 2, aux besoins différents, l'un étant gourmand en registres et l'autre très économe. Dans ce cas, il faudrait partitionner le banc de registre pour donner plus de registre au premier et moins au second. Il s'agit là d'un '''partitionnement dynamique'''.
Le partitionnement dynamique est plus optimal pour gérer des ''shaders'' déséquilibrés niveau registres, mais a une implémentation matérielle plus complexe. Il a été introduit assez tard, car il a fallu attendre que le rayctracing se démocratise. En effet, le partitionnement dynamique du banc de registre est surtout utile pour le raytracing. Exécuter simultanément des shaders déséquilibrés en registres est peu fréquent avec la rastérisation, beaucoup plus courant avec le raytracing.
NVIDIA et AMD ont des implémentations différentes du partitionnement dynamique. Les GPU AMD RDNA 4 allouent un nombre minimal de registre à chaque ''thread'', mais ils peuvent demander d'avoir accès à plus de registres si besoin. Quand un ''thread'' a besoin de plus de registres, il exécute une instruction dédiée, qui sert à demander plus de registres, ou au contraire à en libérer s'ils sont inutilisés. La demande d'allocation de nouveaux registres se fait par blocs de 16 à 32 registres, suivant comment est configuré le processeur. Précisons que l'instruction d'allocation n'est disponible que pour les ''compute shaders'' et pas les ''shaders'' graphiques.
L'instruction d'allocation de registre précédent peut échouer dans certains cas. Si assez de registres sont disponibles, à savoir inutilisés par d'autres ''threads'', l'instruction réussit. Dans le cas contraire, elle échoue et le ''shader'' est mis en pause avant de retenter cette demande plus tard. Le résultat de l'instruction, échec ou réussite, est mémorisé dans le registre d'état. La technique a pour défaut que certaines situations peuvent mener à un blocage complet du processeur, où chaque ''thread'' ne peut plus poursuivre son exécution, faute de registres disponibles. Des méthodes pour éviter cette situation sont implémentés sur ces GPU, mais la documentation n'est pas très explicite.
Sur les GPU NVIDIA, il y a aussi une instruction d'allocation de registre, mais elle fonctionne différemment. Elle permet d'échanger des registres entre ''threads''. Une première différence est que tous les ''threads'' commencent avec une allocation égale des registres. Les ''threads'' démarrent tous avec le même nombre de registres. Un ''thread'' peut libérer des registres, qui sont alors alloués à un autre ''thread'', le ''thread'' en question pouvant être choisit par le ''thread'' qui libère les registres.
===Le banc de registre est multiport de type externe===
Le banc de registre doit permettre de lire deux vecteurs SIMD par opération, soit deux lectures simultanées. Pour cela, le banc de registre contient deux ports de lecture, chacun permettant de lire un opérande dans le banc de registre. Mais plus le nombre de ports augmente, plus la consommation énergétique du banc de registre augmente, sans compter que celui-ci devient plus lent.
Les processeurs peuvent utilisent des bancs de registres ayant réellement deux ports par banc de registre. Un port de lecture est implémenté avec un composant appelé un multiplexeur, connecté à tous les registres.
[[File:Mémoire multiport faite avec des MUX-DEMUX.png|centre|vignette|upright=2|Mémoire multiport faite avec des MUX-DEMUX]]
Les GPU ne peuvent pas se permettre un tel luxe. Leur banc de registre doit alimenter plusieurs unités de calcul en même temps, en parallèle. Le nombre de ports serait plus proche de 4 à 10 ports de lectures. La solution précédente aurait un budget en transistor et un budget thermique trop important. À la place, ils utilisent une autre méthode : ils simulent un banc de registre à plusieurs ports avec un ou plusieurs bancs de registres à un port. On parle alors de '''multiport externe'''.
[[File:Mémoire multiport à multiportage externe.png|centre|vignette|upright=2.5|Mémoire multiport à multiportage externe.]]
Il existe plusieurs méthodes de multiport externe. Mais celle utilisée sur les GPU simule un banc de registre multiport à partir de plusieurs bancs de registres à un port. Le banc de registre est en réalité formé de plusieurs banques, de plusieurs bancs de registre séparés, chacun mono-port. L'idée est que si l'on accède à deux banques en même temps, on peut lire deux opérandes, une par port/banc de registre. Par contre, si les deux opérandes à lire sont la même banque, il y a un '''conflit d'accès aux banques'''.
[[File:Mémoire à multiports par banques.png|centre|vignette|upright=2|Mémoire à multiports par banques.]]
===L'''Operand Collector'' et les caches de ''register reuse''===
Sans conflit d'accès à une banque, les deux opérandes sont disponibles immédiatement. Par contre, en cas de conflit d'accès aux banques, les deux opérandes sont lus l'une après l'autre. En clair, le premier opérande doit être mis en attente quelque part pendant que la seconde est en cours de lecture. Et le problème survient souvent, surtout avec les opérations FMA qui utilisent trois opérandes, encore plus avec les rares opérations qui demandent 4 à 5 opérandes.
Pour gérer les conflits d'accès aux banques, les GPU utilise un circuit dédié, appelé le '''collecteur d'opérandes''' (''operand collector''). Le rôle du collecteur d'opérandes est d'accumuler les opérandes en attente, son nom est assez transparent. Il accumule les opérandes en attente, puis les envoie aux unités de calcul quand elles sont toutes prêtes, toutes lues depuis le banc de registres. Les opérandes sont mis en attente dans des entrées, qui contiennent la donnée, l'identifiant du ''thread''/''warp'' pour ne pas confondre des opérandes entre ''threads'', et deux bits d'occupation. Les deux bits d'occupation indiquent si l'entrée est vide, réservée pour un opérande en cours de lecture ou occupée par un opérande.
Le collecteur d'opérande est souvent accompagné de '''registres temporaires''', qui mémorisent le résultat d'une instruction précédente. Il y a un registre temporaire par ALU, le résultat fournit par une ALU est mémorisé dans le registre temporaire associé. Une instruction peut lire une opérande dans un registre temporaire, ce qui permet de lire le résultat d'une instruction précédente sans passer par le banc de registres. Le collecteur d'opérande est alors configuré pour récupérer les opérandes adéquats dans les registres temporaires adéquats.
: Pour faire une comparaison avec les processeurs modernes, ces registres sont une forme de ''data forwarding'', de contournement, mais qui est rendue explicite pour le logiciel.
Une optimisation des GPU récent vise à réduire les accès aux bancs de registres en utilisant une mémoire cache spécialisée. Il s'agit de l'''operand reuse cache'', aussi appelés '''''register reuse cache'''''. L'idée est que quand un opérande est lu depuis le banc de registres, elle peut être stockée dans ce cache pour des utilisations ultérieures. Les architectures Volta, Pascal et Maxwell disposent de 4 caches de ce type, chacun stockant 8 données/opérandes/résultats
Il s'agit en réalité de pseudo-caches, car ils sont partiellement commandés par le logiciel. Une instruction précise qu'un opérande doit être stocké dans un ''register reuse cache'', pour une utilisation ultérieure. Pour cela, elle incorpore quelques bits pour préciser qu'elle doit être placée dans le cache. Les bits font en quelque sorte partie du mode d'adressage. Si l'instruction immédiatement suivante lit ce registre dans le même ''slot'' d'opérande lira l'opérande dans le cache. La technique est donc assez limitée, mais elle a des résultats pas négligeables.
Les ''register reuse cache'' et le collecteur opérandes sont sans doute fusionnés en un seul circuit, plutôt que d'utiliser deux circuits séparés. La raison est que les deux doivent mémoriser des opérandes les mettre en attente pour une utilisation ultérieure, et sont placés juste après le banc de registre. NVIDIA a publié deux brevets à propos de ces deux techniques, mais rien n'indique que c'est exactement cette technique qui utilisée dans les cartes modernes. Il faut dire que le nombre de banques a changé suivant les cartes graphiques.
* [https://patents.google.com/patent/US7834881B2/en Operand collector architecture ]
* [https://patents.google.com/patent/US20130159628A1/en Methods and apparatus for source operand collector caching ]
==Les processeurs de shaders VLIW==
Une autre forme d'émission multiple est l'usage d'un jeu d'instruction VLIW et/ou d'instructions en ''co-issue'', abordés dans les chapitres précédents. Pour rappel, un processeur VLIW regroupe plusieurs opérations en une seule instruction machine. L'instruction encode les calculs à faire en parallèle, en ''co-issue''. Elle précise les registres, l'opcode de l'instruction, et tout ce qu'il faut pour faire les calculs, et attribue implicitement une unité de calcul à chaque opération.
Les opérations regroupées sont garanties indépendantes par le compilateur, ce qui fait que le décodeur d'instruction envoie chaque opération à l'unité de calcul associée, sans avoir à faire la moindre vérification de dépendances entre instructions. L'unité d'émission est donc grandement simplifiée, elle n'a pas à découvrir les dépendances entre instructions.
Au passage, cela explique pourquoi les premières cartes graphiques étaient de type VLIW, alors que les modernes sont de type SIMD. Les anciennes cartes graphiques préféraient se passer de ''scoreboard''. Mieux valait utiliser le peu de transistors dont elles disposaient pour des unités de calcul. De plus, DirectX 8 et 9 profitaient pas mal de la présence de ''co-issue''. Par contre, il fallait un compilateur performant pour en profiter, ce qui n'était pas vraiment le cas. Les compilateurs n'exploitaient pas beaucoup la ''co-issue'', ce qui fait que les fabricant de GPU ont préféré déléguer cette tâche à un ''scoreboard'' matériel.
===Les architectures VLIW pures, sans unité SIMD===
Un processeur VLIW contient un grand nombre d'unités de calcul. En théorie, il incorpore plusieurs unités de calcul scalaires séparés et n'a pas d'unité de calcul SIMD. Il y a cependant quelques exceptions, mais nous les verrons plus tard. Pour le moment, concentrons-nous sur les processeurs de shaders VLIW sans aucune unité de calcul SIMD.
Un exemple est celui des anciennes cartes graphiques AMD, d'architecture TeraScale/VLIW-5, à savoir les Radeon HD 2000/3000/4000/5000/6000. L'architecture était une architecture VLIW, pas SIMD, d'où la distinction. Il n'y avait pas d'unité de calcul SIMD, mais plusieurs unités de calcul scalaires. Un point important est que les unités de calculs scalaires pouvaient faire des opérations différentes. Par exemple, la première pouvait faire une addition flottante, la seconde une addition entière, la troisième une soustraction, etc.
Elles disposaient de six unités de calcul : cinq unités de calcul scalaires, et une unité de calcul dédiée aux branchements. Sur les 5 unités de calcul, une était une unité de calcul hybride scalaire/transcendentale. Nous allons donc faire la distinction entre unité de calcul spéciale et les 4 unités de calcul basiques.
Toutes les unités de calculs pouvaient faire les opérations suivantes, sur des flottants et entiers sur 32 bits : comparaisons, additions, soustractions, opérations logiques, décalages, opérations bit à bit et instructions CMOV. Les unités de calcul basiques gèrent aussi les multiplications, opérations MAD et produits vectoriels/scalaires, mais seulement pour des opérandes flottantes. L'unité de calcul spéciale gérait des multiplications et division entières sur des opérandes 32 bits, ainsi que des instructions transcendantales entières/flottantes.
Par la suite, avec l'architecture VLIW-4, l'unité de calcul transcendantale a été retirée. Mais les calculs transcendantaux n'ont pas disparus. En effet, il ne resta que 4 ALU flottantes, qui ont été augmentées pour gérer partiellement les opérations transcendantales. Tout se passait comme si l'ALU transcendantale avait été éclatée en morceaux répartis dans chaque ALU flottante/entière. Et c'est globalement ce qui s'est passé : les diverses tables matérielles utilisées pour les calculs transcendantaux ont été dispersés dans les ALU, afin de faire des calculs transcendantaux approchés. En combinant les résultats approchés, on pouvait calculer le résultat exact.
===Les hybrides SIMD/VLIW et les instructions à ''co-issue''===
La gestion des instructions en ''co-issue'' peut aussi utiliser les techniques des processeurs VLIW. Pour rappel, l'exemple typique d'instruction en ''co-issue'' regroupe une opération SIMD avec une opération scalaire, pour profiter de la présence d'une ALU scalaire séparée de l'ALU SIMD. Les cartes graphiques modernes gérent la ''co-issue'' aisni, avec la possibilité de ''co-issue'' une opération scalaire et une opération vectorielle. L'opération scalaire est souvent une opération entière, éventuellement flottante. Les processeurs de shaders qui supportent de telles instructions sont un hybride entre VLIW et SIMD.
Sur les anciennes cartes graphiques disposant d'une unité SIMD, la ''co-issue'' fonctionnait différemment, car les unités de calcul entières n'étaient pas présentes. De plus, le faible budget en transistor faisait que l'on ajoutait pas d'unité flottante scalaire, ce qui ne servait pas à grand-chose. Par contre, l'unité de calcul transcendantale était systématiquement présente, car très utile. Aussi, une forme plus limitée de ''co-issue'' était possible : on pouvait exécuter une opération transcendantale en parallèle d'une instruction SIMD. Le cas le plus simple est le processeur de vertices de la Geforce 3, avec une unité SIMD et une unité transcendantale. Il n'y avait pas d'unité de calcul scalaire entière, ni même flottante.
Un autre exemple est le processeur de ''vertex shader'' de la Geforce 6800, illustré ci-dessous. On voit que le processeur contient une unité d'accès mémoire/textures, avec deux unités de calcul. La première est une unité de calcul scalaire flottante, qui gère les opérations flottantes transcendantales. Elle travaille sur des opérandes de 32 bits. À côté, on trouve une unité de calcul SIMD, qui gère des vecteurs de 4 nombres flottants 32 bits. Elle permet de faire des additions, des multiplications, des opérations MAD, des produits vectoriels, et quelques autres opérations comme le calcul du maximum/minimum de deux nombres. Il n'y a aucune gestion d'instructions flottantes scalaires ou d'opérations entières, la carte graphique est trop ancienne pour cela.
[[File:GeForce 6800 Vertex processor block.png|centre|vignette|upright=2.0|Processeur de shader (vertex shader) d'une GeForce 6800. On voit clairement que celui-ci contient, outre les traditionnelles unités de calcul et registres temporaires, un "cache" d'instructions, des registres d'entrée et de sortie, ainsi que des registres de constante.]]
Le processeur de pixel shader de la même carte graphique était lui aussi un hybride entre VLIW et SIMD. Cependant, il lorgnait plus du côté VLIW que SIMD. Il pouvait faire soit une opération SIMD le vecteur, soit couper le vecteur en deux et effectuer deux opérations différentes sur chaque morceau. Par exemple, il pouvait faire une opération sur 3 pixels, et une opération scalaire sur le quatrième, ou deux opérations vectoriels chacune sur deux pixels. Le processeur travaille sur des blocs de 4 pixels, appelés des ''quads''. Chaque pixel est codé avec 4 flottants 32 bits, cela fait en tout 4 vecteurs de 4 flottants, avec ''co-issue'' à l'intérieur de chaque vecteur. Précisons que les registres temporaires du processeur mémorisent chacun un vecteur de 4 flottants, un pixel, par un ''quad''.
Niveau unités de calcul, le tout était assez complexe. Il contenait tout d'abord une unité de texture, et plusieurs unités VLIW/SIMD. Elles sont appelées "unités SIMD" dans les schémas qui vont suivre, mais elles sont en réalité un mix entre unité SIMD véritable et unités scalaires du VLIW. La documentation NVIDIA elle-même parle d'unité vectorielle, mais le terme est quelque peu trompeur, car elles sont plus flexibles que de simples unités SIMD.
Il y en a deux, la première envoyant son résultat à la seconde. La première est capable de faire des opérations de multiplications/MAD, mais elle peut aussi être utilisée pour la correction de perspective grâce à son support des opérations 1/x. Elle peut aussi normaliser des nombres flottants sur 16 bits. Il faut noter que la première unité SIMD/VLIW ne peut pas être utilisée si un accès mémoire/texture est en cours. La seconde unité SIMD/VLIW est capable de faire des opérations de MAD, et un produit vectoriel/scalaire DOT4.
Le résultat de la seconde ALU est ensuite envoyé à une unité de branchement qui décide s'il faut ré-exécuter une autre passe d'instructions ou non. Il s'agit vraisemblablement d'une unité qui gère la prédication des résultats. Une fois le fragment/pixel final calculé, il est envoyé à une unité de calcul du brouillard, qui est une unité de calcul spécialisée travaillant sur des nombres entiers (en réalité, en virgule fixe, mais c'est pareil).
[[File:Processeur de pixel shader de la Geforce 6800.png|centre|vignette|upright=2.0|Processeur de pixel shader de la Geforce 6800]]
{{NavChapitre | book=Les cartes graphiques
| prev=La répartition du travail sur les unités de shaders
| prevText=La répartition du travail sur les unités de shaders
| next=La hiérarchie mémoire d'un GPU
| netxText=La hiérarchie mémoire d'un GPULa mémoire unifiée et la mémoire vidéo dédiée
}}{{autocat}}
21vfn70geoc7t2ptuinzmts6zygscfw
Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020
0
82108
745331
745251
2025-06-24T23:33:13Z
LodestarChariot2
120009
/* 2020 */
745331
wikitext
text/x-wiki
{{EnTravaux}}
{{Page de garde|image=OSPO cover.png|description=
'''Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020''' est un recueil de réflexions, sous forme de livre, sur des questions pertinentes au mouvement de la science ouverte. Ce tome vise à faciliter la compréhension de la science sociale ouverte à travers le Canada et à l'international, afin de contribuer à influencer et à mettre en œuvre des politiques liées à la mobilisation des connaissances. Ce faisant, il reflète les politiques pertinentes et leur impact sur les communautés de recherche, tout en signalant les tendances et les recherches actuelles; et offre une base large et approfondie pour l'élaboration de recommandations politiques sur des questions importantes, notamment la gestion de l'identité, l'accès ouvert, la gestion des données, la science citoyenne et d'autres domaines connexes.
|avancement=Terminé
|cdu=
* {{CDU item|3/31|316}}
|versions=
{{Moteur}}
}}
''Pour lire la version anglaise de ce texte, voir [[b:en:Foundational Observations: Open Scholarship Policy Observatory, 2017-2020 | Foundational Observations: Open Scholarship Policy Observatory, 2017-2020]].''
''Pour le deuxième volume de cette série, voir [[Extension : Observatoire des politiques d'Érudition ouverte, 2021-2024 | Extension : Observatoire des politiques d'Érudition ouverte, 2021-2024]].
<big><div style="text-align: left;">'''Éditrices / Éditeurs : Alyssa Arbuckle, Ray Siemens, Tanja Niemann, Lynne Siemens'''</div></big>
<big><div style="text-align: left;">'''Auteures / Auteurs : Sarah Milligan, Alyssa Arbuckle, Kim Silk, Caroline Winter'''</div></big>
==Table des matières==
===[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Introduction | Introduction]]===
===2017===
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Politique_des_trois_organismes_sur_le_libre_accès_aux_publications | Politique des trois organismes sur le libre accès aux publications]] <small>(Milligan)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/L'Examen_du_soutien_fédéral_aux_sciences | L'Examen du soutien fédéral aux sciences]] <small>(Milligan)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Access_Copyright_c._Université_York | Access Copyright c. Université York]] <small>(Milligan)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Feuille_de_route_sur_la_communication_savante_de_l'ABRC | Feuille de route sur la communication savante de l'ABRC]] <small>(Milligan)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Recommandations_politiques_pour_le_libre_accès_aux_données_de_recherche_en_Europe_(RECODE) | Recommandations politiques pour le libre accès aux données de recherche en Europe (RECODE)]] <small>(Arbuckle)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Groupe_de_travail_sur_la_science_ouverte_du_G7 | Groupe de travail sur la science ouverte du G7]] <small>(Milligan)</small>
===2018===
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Déclaration_de_principes_des_trois_organismes_sur_la_gestion_des_données_numériques | Déclaration de principes des trois organismes sur la gestion des données numériques]] <small>(Milligan)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Appel_de_Jussieu_pour_la_Science_ouverte_et_la_bibliodiversité | Appel de Jussieu pour la Science ouverte et la bibliodiversité]] <small>(Milligan)</small>
*[[b:en:Foundational_Observations:_Open_Scholarship_Policy_Observatory,_2017-2020/Integrated_Digital_Scholarship_Ecosystem | Integrated Digital Scholarship Ecosystem]] <small>(Milligan)</small>{{ref|Ce texte est uniquement disponible en anglais.|a}}
*[[Observations fondamentales : Observatoire des politiques sur les savoirs ouverts, 2017-2020/ORCID : Connecter la recherche et les chercheurs | ORCID : Connecter la recherche et les chercheurs]] <small>(Silk)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Le_gouvernement_ouvert | Le gouvernement ouvert]] <small>(Silk)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Négociations_de_l'édition_à_libre_accès_en_Europe | Négociations de l'édition à libre accès en Europe]] <small>(Silk)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Comment_le_budget_fédéral_de_2018_affecte_la_recherche_au_Canada | Comment le budget fédéral de 2018 affecte la recherche au Canada]] <small>(Silk)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Ouvrir_les_outils_d'annotation | Ouvrir les outils d'annotation]] <small>(Silk)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Plan_S_et_cOAlition_S | Plan S et cOAlition S]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Le_Canada_célèbre_la_Semaine_du_libre_accès_international_2018 | Le Canada célèbre la Semaine du libre accès international 2018]] <small>(Winter)</small>
{{reflist}}
===2019===
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Conformité_à_la_politique_de_libre_accès_au_Canada | Conformité à la politique de libre accès au Canada]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/L'Analysis_&_Policy_Observatory | L'Analysis & Policy Observatory]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Le Film Paywall : The Business of Scholarship | Le Film Paywall : The Business of Scholarship]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/La_rupture_entre_Elsevier_et_l'University_of_California | La rupture entre Elsevier et l'University of California]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/La_semain_de_l'éducation_ouverte_2019 | La semain de l'éducation ouverte 2019]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/L'éducation_ouverte_en_Colombie_Britannique | L'éducation ouverte en Colombie Britannique]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Le_Projet_Review,_Promotion,_and_Tenure_à_ScholCommLab | Le Projet « Review, Promotion, and Tenure » à ScholCommLab]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Déclaration_électorale_commune_de_CAUL-AOASG | Déclaration électorale commune de CAUL-AOASG]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Le_directive_de_l'Union_Européenne_sur_le_droit_d'auteur_dans_le_marché_unique_numérique | Le directive de l'Union Européenne sur le droit d'auteur dans le marché unique numérique]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Le_Canadian–Australian_Partnership_for_Open_Scholarship_(CAPOS) | Le Canadian–Australian Partnership for Open Scholarship (CAPOS)]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Les_médias_sociaux_pour_la_communauté_savant | Les médias sociaux pour la communauté savant]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Mise à jour ORCID : Intégration des identifiants ORCID dans les workflows de financement de la recherche | Mise à jour ORCID : Intégration des identifiants ORCID dans les workflows de financement de la recherche]] <small>(Winter)</small>
===2020===
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Wikidata_dans_les_bibliothèques_de_recherche | Wikidata dans les bibliothèques de recherche]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/La_déclaration_de_la_Sorbonne_sur_les_droits_relatifs_aux_données_de_recherche | La déclaration de la Sorbonne sur les droits relatifs aux données de recherche]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Le_politique_des_trois_organismes_sur_la_gestion_des_données_de_recherche | Le politique des trois organismes sur la gestion des données de recherche]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/NOIRN_et_la_stratégie_canadienne_d’infrastructure_de_recherche_numérique | NOIRN et la stratégie canadienne d’infrastructure de recherche numérique]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Examen_et_consultation_de_la_politique_de_L’UKRI_sur_libre_accès | Examen et consultation de la politique de L’UKRI sur libre accès]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Plan_Stratégique_2019–2024_du_CRKN–RCDR | Plan Stratégique 2019–2024 du CRKN–RCDR]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Science_Ouverte_et_COVID-19 | Science Ouverte et COVID-19]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Mind_the_Gap_et_POP!_:_En_conversation_avec_John_Maxwell | Mind the Gap et POP! : En conversation avec John Maxwell]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/La_Recommandation_de_l’UNESCO_sur_la_science_ouverte | La Recommandation de l’UNESCO sur la science ouverte]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Le_Persistent_Identifier_(PID)_Consortium_de_Royaume-Uni | Le Persistent Identifier (PID) Consortium de Royaume-Uni]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/L’atelier_et_rapport_Advancing_Open_d’ABRC | L’atelier et rapport Advancing Open d’ABRC]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Feuille_de_route_pour_la_science_ouverte_du_Canada | Feuille de route pour la science ouverte du Canada]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Mise à jour du Plan S : La Stratégie de conservation des droits | Mise à jour du Plan S : La Stratégie de conservation des droits]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Les_principes_TRUST_pour_les_dépôts_numériques | Les principes TRUST pour les dépôts numériques]] <small>(Winter)</small>
[[Catégorie:Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020 (livre)]]
5ni8s76s5p70msdjlkst18el5tfxzag
745332
745331
2025-06-24T23:35:13Z
LodestarChariot2
120009
/* 2018 */
745332
wikitext
text/x-wiki
{{EnTravaux}}
{{Page de garde|image=OSPO cover.png|description=
'''Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020''' est un recueil de réflexions, sous forme de livre, sur des questions pertinentes au mouvement de la science ouverte. Ce tome vise à faciliter la compréhension de la science sociale ouverte à travers le Canada et à l'international, afin de contribuer à influencer et à mettre en œuvre des politiques liées à la mobilisation des connaissances. Ce faisant, il reflète les politiques pertinentes et leur impact sur les communautés de recherche, tout en signalant les tendances et les recherches actuelles; et offre une base large et approfondie pour l'élaboration de recommandations politiques sur des questions importantes, notamment la gestion de l'identité, l'accès ouvert, la gestion des données, la science citoyenne et d'autres domaines connexes.
|avancement=Terminé
|cdu=
* {{CDU item|3/31|316}}
|versions=
{{Moteur}}
}}
''Pour lire la version anglaise de ce texte, voir [[b:en:Foundational Observations: Open Scholarship Policy Observatory, 2017-2020 | Foundational Observations: Open Scholarship Policy Observatory, 2017-2020]].''
''Pour le deuxième volume de cette série, voir [[Extension : Observatoire des politiques d'Érudition ouverte, 2021-2024 | Extension : Observatoire des politiques d'Érudition ouverte, 2021-2024]].
<big><div style="text-align: left;">'''Éditrices / Éditeurs : Alyssa Arbuckle, Ray Siemens, Tanja Niemann, Lynne Siemens'''</div></big>
<big><div style="text-align: left;">'''Auteures / Auteurs : Sarah Milligan, Alyssa Arbuckle, Kim Silk, Caroline Winter'''</div></big>
==Table des matières==
===[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Introduction | Introduction]]===
===2017===
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Politique_des_trois_organismes_sur_le_libre_accès_aux_publications | Politique des trois organismes sur le libre accès aux publications]] <small>(Milligan)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/L'Examen_du_soutien_fédéral_aux_sciences | L'Examen du soutien fédéral aux sciences]] <small>(Milligan)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Access_Copyright_c._Université_York | Access Copyright c. Université York]] <small>(Milligan)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Feuille_de_route_sur_la_communication_savante_de_l'ABRC | Feuille de route sur la communication savante de l'ABRC]] <small>(Milligan)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Recommandations_politiques_pour_le_libre_accès_aux_données_de_recherche_en_Europe_(RECODE) | Recommandations politiques pour le libre accès aux données de recherche en Europe (RECODE)]] <small>(Arbuckle)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Groupe_de_travail_sur_la_science_ouverte_du_G7 | Groupe de travail sur la science ouverte du G7]] <small>(Milligan)</small>
===2018===
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Déclaration_de_principes_des_trois_organismes_sur_la_gestion_des_données_numériques | Déclaration de principes des trois organismes sur la gestion des données numériques]] <small>(Milligan)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Appel_de_Jussieu_pour_la_Science_ouverte_et_la_bibliodiversité | Appel de Jussieu pour la Science ouverte et la bibliodiversité]] <small>(Milligan)</small>
*[[b:en:Foundational_Observations:_Open_Scholarship_Policy_Observatory,_2017-2020/Integrated_Digital_Scholarship_Ecosystem | Integrated Digital Scholarship Ecosystem]] <small>(Milligan)</small>{{ref|Ce texte est uniquement disponible en anglais.|a}}
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/ORCID : Connecter la recherche et les chercheurs | ORCID : Connecter la recherche et les chercheurs]] <small>(Silk)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Le_gouvernement_ouvert | Le gouvernement ouvert]] <small>(Silk)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Négociations_de_l'édition_à_libre_accès_en_Europe | Négociations de l'édition à libre accès en Europe]] <small>(Silk)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Comment_le_budget_fédéral_de_2018_affecte_la_recherche_au_Canada | Comment le budget fédéral de 2018 affecte la recherche au Canada]] <small>(Silk)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Ouvrir_les_outils_d'annotation | Ouvrir les outils d'annotation]] <small>(Silk)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Plan_S_et_cOAlition_S | Plan S et cOAlition S]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Le_Canada_célèbre_la_Semaine_du_libre_accès_international_2018 | Le Canada célèbre la Semaine du libre accès international 2018]] <small>(Winter)</small>
{{reflist}}
===2019===
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Conformité_à_la_politique_de_libre_accès_au_Canada | Conformité à la politique de libre accès au Canada]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/L'Analysis_&_Policy_Observatory | L'Analysis & Policy Observatory]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Le Film Paywall : The Business of Scholarship | Le Film Paywall : The Business of Scholarship]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/La_rupture_entre_Elsevier_et_l'University_of_California | La rupture entre Elsevier et l'University of California]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/La_semain_de_l'éducation_ouverte_2019 | La semain de l'éducation ouverte 2019]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/L'éducation_ouverte_en_Colombie_Britannique | L'éducation ouverte en Colombie Britannique]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Le_Projet_Review,_Promotion,_and_Tenure_à_ScholCommLab | Le Projet « Review, Promotion, and Tenure » à ScholCommLab]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Déclaration_électorale_commune_de_CAUL-AOASG | Déclaration électorale commune de CAUL-AOASG]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Le_directive_de_l'Union_Européenne_sur_le_droit_d'auteur_dans_le_marché_unique_numérique | Le directive de l'Union Européenne sur le droit d'auteur dans le marché unique numérique]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Le_Canadian–Australian_Partnership_for_Open_Scholarship_(CAPOS) | Le Canadian–Australian Partnership for Open Scholarship (CAPOS)]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Les_médias_sociaux_pour_la_communauté_savant | Les médias sociaux pour la communauté savant]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Mise à jour ORCID : Intégration des identifiants ORCID dans les workflows de financement de la recherche | Mise à jour ORCID : Intégration des identifiants ORCID dans les workflows de financement de la recherche]] <small>(Winter)</small>
===2020===
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Wikidata_dans_les_bibliothèques_de_recherche | Wikidata dans les bibliothèques de recherche]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/La_déclaration_de_la_Sorbonne_sur_les_droits_relatifs_aux_données_de_recherche | La déclaration de la Sorbonne sur les droits relatifs aux données de recherche]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Le_politique_des_trois_organismes_sur_la_gestion_des_données_de_recherche | Le politique des trois organismes sur la gestion des données de recherche]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/NOIRN_et_la_stratégie_canadienne_d’infrastructure_de_recherche_numérique | NOIRN et la stratégie canadienne d’infrastructure de recherche numérique]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Examen_et_consultation_de_la_politique_de_L’UKRI_sur_libre_accès | Examen et consultation de la politique de L’UKRI sur libre accès]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Plan_Stratégique_2019–2024_du_CRKN–RCDR | Plan Stratégique 2019–2024 du CRKN–RCDR]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Science_Ouverte_et_COVID-19 | Science Ouverte et COVID-19]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Mind_the_Gap_et_POP!_:_En_conversation_avec_John_Maxwell | Mind the Gap et POP! : En conversation avec John Maxwell]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/La_Recommandation_de_l’UNESCO_sur_la_science_ouverte | La Recommandation de l’UNESCO sur la science ouverte]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Le_Persistent_Identifier_(PID)_Consortium_de_Royaume-Uni | Le Persistent Identifier (PID) Consortium de Royaume-Uni]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/L’atelier_et_rapport_Advancing_Open_d’ABRC | L’atelier et rapport Advancing Open d’ABRC]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Feuille_de_route_pour_la_science_ouverte_du_Canada | Feuille de route pour la science ouverte du Canada]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Mise à jour du Plan S : La Stratégie de conservation des droits | Mise à jour du Plan S : La Stratégie de conservation des droits]] <small>(Winter)</small>
*[[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Les_principes_TRUST_pour_les_dépôts_numériques | Les principes TRUST pour les dépôts numériques]] <small>(Winter)</small>
[[Catégorie:Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020 (livre)]]
by8ts071xj1xzdkkn6wymi5h358hcup
Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/La déclaration de la Sorbonne sur les droits relatifs aux données de recherche
0
82204
745305
745041
2025-06-24T17:05:54Z
LodestarChariot2
120009
Liens mis à jour
745305
wikitext
text/x-wiki
''Cette observation a été écrit par Caroline Winter, pour le Electronic Textual Cultures Laboratory et le partenariat Implementing New Knowledge Environments.''
[https://www.leru.org/files/Sorbonne-declaration.pdf La Déclaration de la Sorbonne sur le droit des données de la recherche] est une déclaration de membres de la communauté internationale de la recherche s'engageant à pratiquer et à promouvoir le partage ouvert des données. Il appelle les gouvernements et les organismes de financement de la recherche à travers le monde à développer les cadres juridiques et les structures de récompense institutionnelles nécessaires pour un partage responsable des données de recherche.
La Déclaration a été signée lors du [https://www.leru.org/news/data-summit-in-paris Sommet international du droit des données de la recherche], qui s'est tenu le 27 janvier 2020 à l'Université de la Sorbonne à Paris (LERU 2020). Les signataires originaux comprennent neuf groupes représentant plus de 160 instituts de recherche :
*[https://www.aau.edu/ Association of American Universities (AAU)]
*[http://arua.org.za/ African Research Universities Alliance (ARUA)]
*[http://www.curif.org/fr/ La Coordination des universités de recherche intensive françaises (CURIF)]
*[https://www.german-u15.de/ Allemand U15]
*[http://www.leru.org/ Ligue européenne des universités de recherche (LEUR)]
*[http://www.ru11.jp/eng/ RU11 Japon]
*[https://russellgroup.ac.uk/ Le Russell Group (Royaume-Uni)]
*[https://go8.edu.au/ Le Groupe des Huit (Go8) (Australie)]
*[http://u15.ca/fr Regroupement des universités de recherche du Canada (U15)]
==La déclaration de la Sorbonne et le partenariat INKE==
L'appel de la Déclaration de la Sorbonne pour un changement infrastructurel et institutionnel afin de promouvoir le partage ouvert des données se rapporte à d'autres politiques et normes liées aux données ouvertes et aux science ouvertes d'intérêt pour le partenariat INKE, telles que [https://www.go-fair.org/fair-principles/ les principes FAIR] et la [http://www.science.gc.ca/eic/site/063.nsf/fra/h_83F7624E.html Déclaration de principes des trois organismes sur la gestion des données numériques] (voir « [[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Déclaration de principes des trois organismes sur la gestion des données numériques|Déclaration de principes des trois organismes sur la gestion des données numériques]] »).
[https://cwrc.ca/fr Le Collaboratoire scientifique des écrits du Canada (CSÉC)] défend bon nombre des mêmes principes identifiés dans la Déclaration et les met en pratique dans un contexte canadien en fournissant aux projets qu'il héberge l'infrastructure nécessaire pour rendre les données de recherche ouvertes et interopérables (CSÉC s.d.). Le projet [https://cwrc.ca/fr/node/215 Linked Infrastructure for Networked Cultural Scholarship (LINCS)], dirigé par la directrice du projet CSÉC, Susan Brown (U Guelph), développera une infrastructure nationale pour les données ouvertes liées à la culture, à partir des projets CSÉC et d'autres ensembles de données de recherche.
==La Déclaration de la Sorbonne dans les médias==
La signature de la Déclaration de la Sorbonne a été largement rapportée dans les médias de la communauté de la recherche, y compris les communiqués de presse de signataires tels que [https://www.german-u15.de/presse/2020/20200130_PM_Sorbonne.html les U15 allemands], [https://www.leru.org/news/data-summit-in-paris LEUR], [https://russellgroup.ac.uk/news/russell-group-signs-sorbonne-declaration-on-research-data-rights/ le Russell Group] et [https://go8.edu.au/go8-joins-paris-talks-to-push-for-greater-global-open-access-to-research-data le Go8], ainsi que de leurs membres, notamment [https://www.uni-bonn.de/news/017-2020 l'Universität Bonn] et [https://www.ucl.ac.uk/global/news/2020/feb/ucl-leads-launch-sorbonne-declaration-data-rights l'University College London] et d'autres parties prenantes telles que [https://www.go-fair.org/2020/01/29/universities-push-for-greater-global-open-access-to-research-data/ Go FAIR].
Dans deux articles pour le ''Times Higher Education'', [https://www.timeshighereducation.com/blog/risks-not-sharing-data-are-greater-costs Paul Ayris] et [https://www.timeshighereducation.com/news/top-universities-ink-data-sharing-pact-virus-spread-shows-need John Ross] citent l'épidémie de COVID-19 comme un contexte important pour la Déclaration de la Sorbonne, qui met en évidence les avantages des données ouvertes ainsi que la nécessité d'un soutien et d'une infrastructure plus importants pour permettre le partage ouverte des données (2020; 2020b).
L'importance du partage des données pour la réponse internationale COVID-19 a fait des données ouvertes et du libre accès un sujet d'intérêt dans les médias en général également. Par exemple, [https://www.cbc.ca/news/health/coronavirus-2019-ncov-science-virus-genome-who-research-collaboration-1.5446948 Kelly Crowe] écrit pour les nouvelles de la CBC qu'une [https://wellcome.ac.uk/press-release/sharing-research-data-and-findings-relevant-novel-coronavirus-covid-19-outbreak?utm_source=twitter&utm_medium=o-wellcome déclaration du Wellcome Trust] appelant à ce que les données et publications relatives à l'épidémie soient immédiatement accessibles au public – et signées par une centaine d'éditeurs et d'organismes de recherche – est une exception notable à pratiques habituelles de recherche et de publication (2020). Crowe cite Vincent Larivière, membre du partenariat INKE, soulignant que la déclaration du Wellcome Trust est une reconnaissance implicite des obstacles posés par ces pratiques habituelles de recherche et de publication aux réponses à d'autres maladies mortelles et à la recherche en général (2020).
==Déclaration de la Sorbonne et science ouverte==
Ayris soutient que le plus grand défi pour les types de changements demandés dans la Déclaration peut être culturel, car la communauté universitaire doit changer son état d'esprit vers l'ouverture et la gestion des données selon les principes FAIR (2020). Ne pas le faire, soutient-il, aura des conséquences financières et entravera notre capacité à faire face aux défis mondiaux, y compris les défis sanitaires tels que COVID-19, mais ces changements nécessitent des investissements importants dans les infrastructures de recherche (2020).
En effet, la Déclaration de la Sorbonne fait écho à d'autres politiques de bourses ouvertes en appelant à l'intégration de la gestion des données dans les flux de recherche standard et appelle au développement d'infrastructures et de financements pour le permettre. Citant des chercheurs des institutions du Go8, Ross note que, en tant que déclaration développée par des organisations de recherche influentes à travers le monde, la Déclaration a le potentiel de conduire à des changements significatifs dans les pays avec des politiques de données ouvertes fragmentées, ainsi que dans ceux avec des politiques plus développées (2020a). Il cite [https://www.ic.gc.ca/eic/site/136.nsf/fra/accueil la stratégie d’infrastructure de recherche numérique] (IRN) du Canada comme exemple de politique bien élaborée et suggère que la déclaration est une politique importante à garder à l’esprit lorsque les détails du financement du IRN du Canada seront décidés. [http://science.gc.ca/eic/site/063.nsf/fra/h_97992.html La Feuille de route pour la science ouverte] publiée récemment par le Canada souligne également la nécessité que les données soient « ouverts par conception et par défaut » et conformes aux principes FAIR (gouvernement du Canada 2020).
Comme indiqué dans « [[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Le Projet Review, Promotion, and Tenure à ScholCommLab|Le project ‘Review, Promotion, and Tenure’ à ScholCommLab]] », les politiques institutionnelles qui ne reconnaissent ni ne récompensent la science ouvertes sont largement reconnues comme un obstacle au changement culturel. En reconnaissance de cela, et comme moyen d'encourager un changement culturel nécessaire, la Déclaration comprend également un engagement à œuvrer pour changer ces politiques.
==Ouvrages Citées==
*Ayris, Paul. 2020. « The Risks of Not Sharing Data are Greater than the Costs ». ''Times Higher Education'', 8 février 2020, [https://www.timeshighereducation.com/blog/risks-not-sharing-data-are-greater-costs https://www.timeshighereducation.com/blog/risks-not-sharing-data-are-greater-costs].
*Crowe, Kelly. 2020. « ‘We’re Opening Everything’: Scientists Share Coronavirus Data in Unprecedented Way to Contain, Treat Disease ». ''CBC News'', 1 février 2020, [https://www.cbc.ca/news/health/coronavirus-2019-ncov-science-virus-genome-who-research-collaboration-1.5446948 https://www.cbc.ca/news/health/coronavirus-2019-ncov-science-virus-genome-who-research-collaboration-1.5446948].
*Gouvernement du Canada. 2020. ''Feuille de route pour la science ouverte''. Février 2020. [http://science.gc.ca/eic/site/063.nsf/fra/h_97992.html http://science.gc.ca/eic/site/063.nsf/fra/h_97992.html]
*LEUR (Ligue européenne des universités de recherche). 2020. « Data Summit in Paris ». [https://www.leru.org/news/data-summit-in-paris https://www.leru.org/news/data-summit-in-paris].
*Ross, John. 2020a. « Open Data ‘tougher’ than Open Access and Needs ‘Mindset Change’ ». ''Times Higher Education'', 31 janvier 2020, [https://www.timeshighereducation.com/news/open-data-tougher-open-access-and-needs-mindset-change https://www.timeshighereducation.com/news/open-data-tougher-open-access-and-needs-mindset-change].
*Ross, John. 2020b. « Top Universities Ink Data-Sharing Pact as Virus Spread Shows Need ». ''Times Higher Education'', 28 janvier 2020, [https://www.timeshighereducation.com/news/top-universities-ink-data-sharing-pact-virus-spread-shows-need https://www.timeshighereducation.com/news/top-universities-ink-data-sharing-pact-virus-spread-shows-need].
[[Catégorie:Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020 (livre)]]
8fs45aj9eq4cpxygzf7xwpibv1ap7y8
Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Le politique des trois organismes sur la gestion des données de recherche
0
82205
745306
745042
2025-06-24T19:30:43Z
LodestarChariot2
120009
Lien mis à jour
745306
wikitext
text/x-wiki
''Cette observation a été écrit par Caroline Winter (avec des remerciements à Rachel Hendery, Janneke Adema, Gary Hall, et Les Kneebone pour ses commentaires et contributions), pour le Electronic Textual Cultures Laboratory et le partenariat Implementing New Knowledge Environments.''
En mai 2018, le gouvernement du Canada a publié une ébauche de [http://www.science.gc.ca/eic/site/063.nsf/fra/h_97610.html Politique de consultation des trois organismes sur la gestion des données de recherche] (politique GDR). Le politique fait partie d'une stratégie tripartite pour encourager et soutenir la gestion des données de recherche, qui comprend également la [http://www.science.gc.ca/eic/site/063.nsf/fra/h_F6765465.html Politique des trois organismes sur le libre accès aux publications] (2015) et la [http://www.science.gc.ca/eic/site/063.nsf/fra/h_83F7624E.html Déclaration de principes des trois organismes sur la gestion des données numériques] (2016) (Pour plus d'informations, voir « [[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Déclaration de principes des trois organismes sur la gestion des données numériques|Déclaration de principes des trois organismes sur la gestion des données numériques]] » et [https://ospolicyobservatory.uvic.ca/partner-response-to-tri-agency-statement-of-principles-on-digital-data-management/ « Partner Response to Tri-Agency Statement of Principles on Digital Data Management »] par Lisa Goddard [UVic Libraries]).
La politique GDR s'applique aux institutions qui administrent le financement des trois agences ainsi qu'aux bénéficiaires de subventions. Il comprend trois points clés :
# Les institutions doivent avoir des stratégies de GDR accessibles au public qui sont examinées et mises à jour régulièrement. Ils doivent s'assurer que leurs chercheurs ont également mis en place des stratégies de GDR.
# Les propositions de subvention devraient inclure des plans de GDR, et certaines possibilités de financement les nécessiteront.
# Les bénéficiaires de subventions doivent déposer toutes les données de recherche pertinentes aux publications financées dans des référentiels, et les données doivent être en libre accès lorsque cela est juridiquement et éthiquement approprié. Ils devraient également établir un lien clair entre les publications et les données de recherche.
La version finale de la politique n'avait pas été publiée au moment de la rédaction, mais Portage [https://portagenetwork.ca/fr/nouvelles/publication-du-resume-de-la-consultation-publique-politique-des-trois-organismes-sur-la-gestion-des-donnees-de-recherche/ rapporte] une date de sortie au printemps 2020.
En décembre 2019, le gouvernement a publié un [http://www.science.gc.ca/eic/site/063.nsf/fra/h_97905.html Résumé de la consultation publique]. Ce résumé montre que, dans l'ensemble, les chercheurs canadiens appuient la politique, mais demandent une clarification de la terminologie et de la portée, et ils soulèvent des préoccupations concernant l'éthique et les coûts de mise en œuvre et de surveillance.
==La politique GDR et le partenariat INKE==
De nombreuses institutions canadiennes disposent de supports gestion des données disponibles pour les chercheurs par le biais de bibliothèques institutionnelles. Il s'agit notamment des membres du partenariat INKE, des [https://libguides.uvic.ca/researchdata/home Bibliothèques de l'Université de Victoria] et des [https://www.lib.sfu.ca/help/publish/research-data-management Bibliothèques de l'Université Simon Fraser]. En Australie, la [https://ecu.au.libguides.com/research-data-management/introduction_to_rdm bibliothèque de l'Université Edith Cowan] répertorie les politiques institutionnelles ainsi que les services de soutien.
En réponse à une prise de conscience croissante de l'importance du GDR, le Portage Network – une initiative de [http://www.carl-abrc.ca/fr/ CARL / ABRC (Canadian Association of Research Libraries / Association des bibliothèques de recherche du Canada)] – a lancé [https://assistant.portagenetwork.ca/ L'assistant PGD] en octobre 2015. L'assistant PGD est un outil en ligne ouvert et bilingue pour la création de plans de gestion des données (PGD). En mars 2018, Portage a lancé le [https://portagenetwork.ca/fr/reseau-dexperts/strategies-institutionnelles-de-gdr/ Modèle de stratégie institutionnelle et le Guide d’accompagnement] en prévision de la publication de la politique de gestion des données. Les deux outils sont recommandés dans le projet de politique.
En janvier 2020, le Portage Groupe d’experts sur la recherche et l’intelligence (GERI) a publié un [https://portagenetwork.ca/wp-content/uploads/2019/11/RIEGStrategySurveySummary_FR.pdf rapport] résumant les résultats d'une enquête auprès des institutions de recherche canadiennes menée en juin 2019 en prévision de la publication d'une version officielle de la politique GDR. L'enquête a recueilli des données sur l'état des stratégies de GDR des institutions et les défis rencontrés pour les développer. Le rapport indique qu'en juin 2019, 80% des répondants ont indiqué que leur établissement de recherche avait entamé le processus d'élaboration d'une stratégie institutionnelle (2019 p. 3). Certaines des recommandations de Portage GERI comprennent la création d'un inventaire des politiques institutionnelles, d'une communauté de pratique et d'un ensemble de meilleures pratiques.
En janvier 2019, Jacqueline M. Quinless et Shahira A. Khair des bibliothèques de l'Université de Victoria ont publié les résultats d'une enquête sur les pratiques de GDR distribuée aux chercheurs de l'UVic. Le rapport, [https://dspace.library.uvic.ca/bitstream/handle/1828/10509/UVic_RDM_Final_Report_Jan2019.pdf?sequence=1&isAllowed=y ''The Enduring Potential of Data : An Assessment of Researcher Data Stewardship Practices at the University of Victoria''], note que la demande de formation et d'orientation sur la gestion des données est claire, mais les variations de terminologie et de pratiques entre les disciplines posent un défi, en particulier en ce qui concerne les partage de données.
==Réponse à la politique de gestion des données dans la communauté universitaire élargie==
Étant donné que la politique finale de gestion des données n'a pas encore été publiée, aucun établissement universitaire canadien n'a de stratégie en place au moment de la rédaction. Cependant, plusieurs institutions ont approuvé l'énoncé de [https://www.rdc-drc.ca/fr/activites/les-principes-de-drc/ Principes de DRC concernant la GDR] de Données de recherche Canada (2016).
En août 2019, le CRSH a annoncé [https://www.sshrc-crsh.gc.ca/funding-financement/programs-programmes/data_management-gestion_des_donnees-fra.aspx L'Initiative visant le renforcement de la capacité de gestion des données de recherche], qui financera 40 projets soutenant la capacité de GDR au Canada dans le cadre du programme de [https://www.sshrc-crsh.gc.ca/funding-financement/programs-programmes/connection_grants-subventions_connexion-fra.aspx Subventions connexion] entre novembre 2019 et août 2020.
La GDR est une préoccupation importante dans la communauté universitaire internationale ainsi qu'au Canada. Au Royaume-Uni, par exemple, le [http://www.dcc.ac.uk/ Digital Curation Center (DCC)] comprend des [http://www.dcc.ac.uk/resources/policy-and-legal aperçus des politiques de gestion des données des bailleurs de fonds institutionnels], ainsi que des ressources pour l'élaboration de plans de gestion des données. Son outil [https://dmponline.dcc.ac.uk/ DMPOnline] comprend des modèles pour de nombreux bailleurs de fonds au Royaume-Uni et dans le monde.
En Australie, [https://apo.org.au/ l'Analysis and Policy Observatory (APO)] recueille des ressources liées à la GDR, que c’est soutenue par une variété d'organisations et d'initiatives. À partir de 2020, l'Australian Research Council (ARC) exige des plans de gestion des données dans le cadre de son programme de financement national. L'Australian National Data Service (ANDS) soutient des projets de données de recherche et gère Research Data Australia, un portail de découverte pour la recherche australienne. L'Australian Research Data Commons (ARDC), fondée en 2018, développe une infrastructure de données de recherche et un commun national de données de recherche.
==La gestion des données et la science ouverte==
La politique GDR est positionnée par rapport à la politique internationale relative à libre accès et aux données ouvertes, notamment la [https://www.opengovpartnership.org/fr/process/joining-ogp/open-government-declaration/ Déclaration du gouvernement ouvert] du Open Government Parnership (2011) et le [http://www.g7.utoronto.ca/science/2017-science-communique.html G7 Science Ministers Communiqué] (2017).
L'ouverture est une valeur directrice de la politique GDR, qui stipule que : « Les organismes subventionnaires sont d’avis que les données recueillies par la recherche grâce aux fonds publics doivent être gérées de manière responsable et efficace » (2018).
==Ouvrages citées==
*GERI (Group d’experts sur la recherche et l’intelligence du réseau Portage). 2019. ''Sondage sur la stratégie institutionnelle de gestion des données de recherche (GDR)''. [https://portagenetwork.ca/wp-content/uploads/2019/11/RIEGStrategySurveySummary_FR.pdf https://portagenetwork.ca/wp-content/uploads/2019/11/RIEGStrategySurveySummary_FR.pdf].
*Gouvernement du Canada. 2018.'' Politique des trois organismes sur la gestion des données de recherche aux fins de consultation''. [http://www.science.gc.ca/eic/site/063.nsf/fra/h_97610.html http://www.science.gc.ca/eic/site/063.nsf/fra/h_97610.html].
*Quinless, Jacqueline M., et Shahira A. Khair. 2019. ''The Enduring Potential of Data: An Assessment of Researcher Data Stewardship Practices at the University of Victoria''. Bibliothèques de l’Université de Victoria. [https://dspace.library.uvic.ca/handle/1828/10509 https://dspace.library.uvic.ca//handle/1828/10509]
*Gouvernement du Canada. 2019. ''Résumé de la consultation publique''. [http://www.science.gc.ca/eic/site/063.nsf/fra/h_97905.html http://www.science.gc.ca/eic/site/063.nsf/fra/h_97905.html].
[[Catégorie:Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020 (livre)]]
cbq7fihuzmnyuxjfnax7splrqst7uva
Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Examen et consultation de la politique de L’UKRI sur libre accès
0
82208
745328
745044
2025-06-24T21:54:54Z
LodestarChariot2
120009
Lien mis à jour
745328
wikitext
text/x-wiki
''Cette observation a été écrit par Caroline Winter (avec des remerciements à Claire Warwick pour ses commentaires et contributions), pour le Electronic Textual Cultures Laboratory et le partenariat Implementing New Knowledge Environments.''
En février 2020, [https://www.ukri.org/ UK Research and Innovation (UKRI)] a publié [https://www.ukri.org/files/funding/oa/open-access-review-consultation/ une nouvelle politique de libre accès proposée pour consultation] afin de remplacer [https://www.ukri.org/files/legacy/documents/rcukopenaccesspolicy-pdf/ la politique de RCUK sur le libre accès] publiée en 2018.
Les objectifs de la politique proposée comprennent la promotion du libre accès et de nouveaux modèles de publication et l'alignement des politiques libre accès à travers l'UKRI, qui comprend [https://www.ukri.org/about-us/our-councils/ les sept conseils de recherche du Royaume-Uni], [https://www.gov.uk/government/organisations/innovate-uk Innovate UK] et [https://re.ukri.org/ Research England] (UKRI 2020).
Les points clés de la politique proposée sont les suivants :
* La politique s'appliquera à tous les résultats de recherche publiés financés par l'UKRI, y compris les publications dans des revues (articles, actes du colloque, revues) et sous forme de livres.
* Les publications de revues dans le champ d'application acceptées pour publication le 1er janvier 2022 ou après, doivent être en libre accès lors de la publication (version officielle ou préimpression) et sous licence CC BY.
* Les monographies, chapitres de livres et collections éditées dans le champ d'application publiés le 1er janvier 2024 ou après, doivent être en libre accès dans les 12 mois suivant la publication (version officielle ou préimpression) et sous licence CC BY (préféré) ou CC BY-ND.
La politique comprend également des dispositions pour le droit d'auteur et la conservation des droits, l'octroi de licences et des normes techniques spécifiques relatives aux identificateurs d'objets numériques persistants, aux normes de métadonnées et à la préservation.
Le processus de consultation est ouvert, avec [https://www.surveymonkey.co.uk/r/open_access_2020 la rétroaction invitée] de toute personne ou organisation intéressée, à l'intérieur ou à l'extérieur du Royaume-Uni. La période de consultation se termine en mai et l'UKRI publiera un résumé des commentaires avec la politique finale, qu'elle prévoit de publier plus tard dans l'année.
==La politique proposée par UKRI sur l'OA et le partenariat INKE==
Bien que cette politique affecte principalement les membres de la communauté INKE au Royaume-Uni, elle s'appliquera probablement à toute recherche financée en tout ou en partie par l'UKRI, y compris les travaux réalisés dans le cadre d'une collaboration internationale (RCUK 2019).
Martin Paul Eve a partagé [https://eve.gd/2020/02/13/key-points-from-the-ukri-open-access-review-consultation-document/ des notes sur la politique proposée] ainsi que son projet de [https://eve.gd/2020/02/27/my-draft-response-to-the-ukri-oa-consultation/ réponse à l'appel à commentaires] de son point de vue en tant que chercheur et en tant que PDG de [https://www.openlibhums.org/ l'Open Library of Humanities]. Eve soutient la politique proposée dans son ensemble, mais appelle à plus de spécificité sur plusieurs points, à un plus grand alignement avec le Plan S en incluant les licences CC BY-SA et CC0 comme alternatives à CC BY, et à ce que le droit d'auteur reste avec l'auteur ou son institution, plutôt qu'avec éditeurs (Eve 2020).
==La politique proposée dans les médias==
Les réactions à la politique proposée au sein de la communauté de recherche britannique ont été globalement positives (Research Information 2020). De nombreuses universités au Royaume-Uni ont publié des avis et des déclarations sur l'examen des politiques, notamment [http://openaccess.ox.ac.uk/2020/02/17/consultation-on-ukris-open-access-policy/ l’Université d’Oxford] et [https://www.ucl.ac.uk/library/open-access/open-access-and-uk-research-councils-ukri l’University College London]. [https://aberdeenunilib.com/2020/02/14/ukri-open-access-review/ L'Université d'Aberdeen] et [https://www.ed.ac.uk/information-services/research-support/publish-research/open-access/plan-s/ukri-open-access-policy-consultation l'Université d'Edimbourg] sollicitent les contributions de leurs communautés afin de préparer des réponses institutionnelles.
Les articles de Jack Grove dans ''Times Higher Education'' mettent en évidence l'effet de la politique proposée sur les livres, un problème qui affecte particulièrement les sciences humaines et sociales. [https://www.timeshighereducation.com/news/ukri-wants-monographs-be-open-access-by2024 Le premier] souligne que les institutions ne seront probablement pas en mesure de couvrir les frais élevés à cinq chiffres que les éditeurs facturent pour la publication des monographies en tant que libre accès, notant que l'UKRI reconnaît ce défi et accueille favorablement les discussions sur la manière dont la politique peut être mise en œuvre avec succès (Grove 2020a). [https://www.timeshighereducation.com/news/humanities-scholars-warn-over-ukris-plan-open-access-books La seconde] exprime des inquiétudes quant au potentiel de la politique de limiter les opportunités pour les chercheurs en sciences humaines émergents qui manquent de fonds pour publier leurs premières monographies en tant que libre accès, notant l'effet néfaste sur leur carrière (Grove 2020c). Comme Eve le souligne dans ses notes, cependant, les livres commerciaux, les éditions savantes, les catalogues d'exposition, les manuels et les écrits créatifs sont tous en dehors du champ d'application de la politique (Eve 2020).
[https://www.timeshighereducation.com/news/licence-remix-research-alarms-humanities-scholars Un autre article] de Grove dans ''THE'' note que la licence CC-BY requise par la politique proposée est particulièrement préoccupante pour les chercheurs en sciences humaines, car les façons dont les faits et les idées sont exprimés font partie intégrante de leur travail. La licence CC-BY rendrait leur travail vulnérable à la réutilisation, à l'exploitation ou à la fausse représentation (Grove 2020b).
==La politique proposée et la science ouverte==
La politique proposée par l'UKRI reconnaît libre accès comme faisant partie d'un écosystème de recherche plus large et note que certaines formes de science ouverte – en particulier, les données ouvertes – dépassent sa portée.
La politique proposée note que la mise en œuvre réussie de libre accès est un effort mondial nécessitant une coopération internationale. L'UKRI est membre de [https://www.coalition-s.org/ cOAlition S] et met l'accent sur l'alignement avec [https://www.coalition-s.org/addendum-to-the-coalition-s-guidance-on-the-implementation-of-plan-s/principles-and-implementation/ Plan S] (voir « [[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Plan S et cOAlition S|Plan S et cOAlition S]] »). Il note également que, afin d'assurer la cohérence entre les politiques du Royaume-Uni en matière de libre accès, le retour d'information de cette consultation éclairera la politique de libre accès pour le deuxième exercice du [https://www.ref.ac.uk/ Research Excellence Framework (REF)] (REF-après-REF 2021), qui doit être effectué après le REF exercice 2021 est terminé.
==Ouvrages cités==
*Eve, Martin Paul. 2020a. « My Draft Response to the UKRI OA Consultation ». 27 février 2020. [https://eve.gd/2020/02/27/my-draft-response-to-the-ukri-oa-consultation/ https://eve.gd/2020/02/27/my-draft-response-to-the-ukri-oa-consultation/].
*Grove, Jack. 2020a. « Humanities Scholars Warn Over UKRI’s Plan for Open-Access Books ». 17 février 2020. [https://www.timeshighereducation.com/news/humanities-scholars-warn-over-ukris-plan-open-access-books https://www.timeshighereducation.com/news/humanities-scholars-warn-over-ukris-plan-open-access-books].
*Grove, Jack. 2020b. « Licence to ‘Remix’ Research Alarms Humanities Scholars ». 20 mars 2020. [https://www.timeshighereducation.com/news/licence-remix-research-alarms-humanities-scholars https://www.timeshighereducation.com/news/licence-remix-research-alarms-humanities-scholars].
*Grove, Jack. 2020c. « UKRI Wants Monographs to be Open Access by 2024 ». ''Times Higher Education''. 13 février 2020. [https://www.timeshighereducation.com/news/ukri-wants-monographs-be-open-access-by2024 https://www.timeshighereducation.com/news/ukri-wants-monographs-be-open-access-by2024].
*RCUK (Research Councils UK). 2019. « RCUK Policy on Open Access Frequently Asked Questions ». 1 mai 2019. [https://www.ukri.org/files/funding/oa/oa-faqs-pdf/ https://www.ukri.org/files/funding/oa/oa-faqs-pdf/].
*Research Information. « Reactions to UKRI Consultation Announcement ». 14 février 2020. [https://www.researchinformation.info/news/reactions-ukri-consultation-announcement https://www.researchinformation.info/news/reactions-ukri-consultation-announcement].
*UKRI (UK Research and Innovation). 2020. ''UKRI Open Access Review: Consultation''. 13 février 2020. [https://www.ukri.org/files/funding/oa/open-access-review-consultation/ https://www.ukri.org/files/funding/oa/open-access-review-consultation/].
[[Catégorie:Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020 (livre)]]
p4rlkmoc27n7x8131v4rcaoj7ldnfjh
Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Le Persistent Identifier (PID) Consortium de Royaume-Uni
0
82215
745329
745049
2025-06-24T22:19:52Z
LodestarChariot2
120009
Lien mis à jour
745329
wikitext
text/x-wiki
''Cette observation a été écrit par Caroline Winter, pour le Electronic Textual Cultures Laboratory et le partenariat Implementing New Knowledge Environments.''
Dans un article pour [https://scholarlykitchen.sspnet.org/2020/06/29/the-uk-national-pid-consortium-a-pathway-to-increased-adoption/?informz=1 The Scholarly Kitchen] en juin 2020, Alice Meadows soutient que maintenant, alors que la pandémie COVID-19 a conduit à des niveaux sans précédent d'ouverture et de collaboration entre les chercheurs du monde entier, la construction d'une infrastructure de recherche solide et stable est plus importante que jamais (Meadows 2020; voir aussi « [[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Science Ouverte et COVID-19|Science Ouverte et COVID-19]] »). Meadows annonce que, dans le cadre de ses efforts pour soutenir et étendre le libre accès au Royaume-Uni, [https://www.jisc.ac.uk/ Jisc] travaille à la création d'un « Persistent Identifier (PID) Consortium » pour le Royaume-Uni.
Jisc est une organisation à but non lucratif qui soutient les besoins en technologie numérique des secteurs de l'enseignement supérieur, de la formation continue et des compétences au Royaume-Uni. Il met en place le PID Consortium du Royaume-Uni en réponse à une recommandation du rapport d'Adam Tickell, [https://assets.publishing.service.gov.uk/government/uploads/system/uploads/attachment_data/file/774956/Open-access-to-research-publications-2018.pdf ''Open Access to Research Publications - 2018''], qui résume l'état de libre accès au Royaume-Uni et propose des recommandations pour le soutenir davantage. Tickell recommande que Jisc prenne l'initiative d’etablisser et promouvoir une selection d’identificateurs permanents, y compris ORCID, en collaboration et consultation avec les parties prenantes (Tickell 2018). Il formule cette recommandation dans le contexte de la réduction du fardeau administratif de libre accès, notamment en rendant les informations sur les options de publication en libre accès facilement accessibles aux chercheurs et en partageant efficacement les publications et les informations pertinentes entre les éditeurs, les chercheurs et les institutions (Tickell 2018).
Dans un rapport ultérieur, [https://repository.jisc.ac.uk/7840/ ''Developing a Persistent Identifier Roadmap for Open Access to UK'' Research (2020)], Josh Brown note que le PID Consortium du Royaume-Uni espère s'appuyer sur le succès d[https://www.jisc.ac.uk/orcid ’UK ORCID Consortium] et appliquer son modèle pour sensibiliser et utiliser d'autres identficateurs permanents. Brown décrit des identificateurs permanents comme des structures essentielles dans l'écosystème de la communication savante qui relient ses divers éléments entre eux, et note que bien que les politiques qui imposent leur l'utilisation soient utiles, leurs plus grands avantages ne peuvent être réalisés que lorsqu’ils sont largement utilisés, et en combinaison (2020).
Certains identificateurs permanents sont propriétaires, tels que le [https://service.elsevier.com/app/answers/detail/a_id/11212/supporthub/scopus/kw/%26quot%3Bauthor+identifier%26quot%3B/ Scopus Author Identifier] d’Elsevier et le [http://researcherid.com/?utm_source=blog&utm_medium=backlink&utm_campaign=RID-comes-to-publons#rid-for-researchers ResearcherID] du Web of Science. Les identificateurs permanents ouverts sont généralement enregistrés et maintenus par des organismes de recherche, et comme ils ne sont pas propriétaires, ils sont complètement interopérables et résolvables (transformables en URL menant à un document lisible par l'humain). Certains identificateurs permanents ouverts largement utilisés incluent
* Les iD ORCID, qui identifient les chercheurs et autres personnes, enregistrés par ORCID (voir « [[Observations fondamentales : Observatoire des politiques sur les savoirs ouverts, 2017-2020/ORCID : Connecter la recherche et les chercheurs|ORCID : Connecter la recherche et les chercheurs]] » et « [[Observations fondamentales : Observatoire des politiques sur les savoirs ouverts, 2017-2020/Mise à jour ORCID : Intégration des identifiants ORCID dans les workflows de financement de la recherche|Mise à jour ORCID : Intégration des identifiants ORCID dans les workflows de financement de la recherche]] »)
* Identifiants [https://ror.org/ ROR], qui identifient les organismes de recherche, enregistrés par le Research Organism Registry (ROR)
* Les [https://www.doi.org/ DOI], qui identifient les publications, les données de recherche et d'autres objets numériques, enregistrés par une fédération d'agences, y compris [https://www.crossref.org/ Crossref] et [https://datacite.org/ DataCite]
* Les [https://www.raid.org.au/ RAiD], qui identifient les projets de recherche, enregistrés par [https://ardc.edu.au/ l’Australian Research Data Commons].
Le développement du PID Consortium du Royaume-Uni est actuellement au stade de la recherche, ce qui comprend la consultation des parties prenantes, notamment des chercheurs, des éditeurs et des bailleurs de fonds par le biais de groupes de discussion et d'une enquête de rétroaction.
==Membres du partenariat INKE et les identificateurs permanents==
Plusieurs membres du Partenariat INKE travaillent à soutenir l'utilisation des identificateurs permanents grâce à leur implication avec ORCID, y compris [https://www.carl-abrc.ca/fr/ l'Association canadienne des bibliothèques de recherche (CARL-ABRC)], [https://www.computecanada.ca/?lang=fr Calcul Canada], le [https://www.crkn-rcdr.ca/ Réseau canadien de documentation pour la recherche (CRKN-RCDR)] et le [https://pkp.sfu.ca/ Public Knowledge Project] avec [https://orcid-ca.org/fr ORCID-CA], et [https://www.ecu.edu.au/ l’Edith Cowan Université] avec [https://aaf.edu.au/orcid/consortium.html l’Australian ORCID Consortium].
Le Canadian Humanities and Social Sciences Commons du Partenariat INKE, actuellement en développement, s’efforce d’intégrer ORCID dans le cadre de ses capacités de la signature unique et de profil de membre.
L'ABRC reconnaît également l'importance des métadonnées interopérables pour les référentiels et a annoncé en mai 2020 un nouveau module d’extension (plug-in) pour DSpace, une plate-forme de dépôts de documents ouverts. Cet module d’extension se conformer aux directives d'OpenAIRE les plus récentes pour les métadonnées en dépôts, qui incluent l'utilisation d'iD ORCID pour identifier les auteurs (Brin 2020).
==Le UK PID Consortium et la science ouverte==
Dans [https://orcid.org/blog/2020/08/03/orcid-and-uk-national-pid-consortium un article pour le blog ORCID-UK], Meadows note que plusieurs autres organisations développent déjà des stratégies conjointes pour l'utilisation et le développement des identificateurs permanents pour les infrastructures de la science ouvertes, notamment l'Australian Research Data Commons, le Portugal's [https://www.fct.pt/ FCT (Fundação para a Ciência ea Tecnologia)], et [https://www.capes.gov.br/ CAPES du Brésil (Coordenação de Aperfeiçoamentao de Pessoal de Nivel Superior (Meadows 2020)].
Parce qu'ils identifient et connectent des éléments de l'écosystème de la communication savante, les identificateurs permanents sont essentiels pour rendre la science ouverte [https://www.nature.com/articles/sdata201618 FAIR (Findable, Accessible, Interoperable, Reusable)] (Wilkinson et al. 2016). Le PID Consortium du Royaume-Uni est l'un des nombreux groupes dans le monde à s'engager dans la recherche et la sensibilisation liées aux identificateurs permanents, ainsi qu'à établir et soutenir leur utilisation.
==Ouvrages Citées==
*Brin, Lise. 2020. « Collaborating to Support Greater Visibility and Discoverability of Open Scholarship – DSpace 5 & 6 Extension Now Available to Support ORCID and New OpenAIRE Guidelines ». ''L’Association des bibliothèques de recherche du Canada'' (blog). 20 mai 2020. [https://www.carl-abrc.ca/news/release-of-dspace-5-6-extension-openaire/ https://www.carl-abrc.ca/news/release-of-dspace-5-6-extension-openaire/].
*Brown, Josh. 2020. « Developing a Persistent Identifier Roadmap for Open Access to UK Research ». 8 avril 2020. [https://repository.jisc.ac.uk/7840/ https://repository.jisc.ac.uk/7840/].
*Meadows, Alice. 2020. « ORCID and the UK National PID Consortium ». 3 août 2020. [https://orcid.org/blog/2020/08/03/orcid-and-uk-national-pid-consortium https://orcid.org/blog/2020/08/03/orcid-and-uk-national-pid-consortium].
*Tickell, Adam. 2018. « Open Access to Research Publications – 2018 ». [https://assets.publishing.service.gov.uk/government/uploads/system/uploads/attachment_data/file/774956/Open-access-to-research-publications-2018.pdf https://assets.publishing.service.gov.uk/government/uploads/system/uploads/attachment_data/file/774956/Open-access-to-research-publications-2018.pdf].
*Wilkinson, Mark D., Michel Dumontier, IJsbrand Jan Aalbersberg, Gabrielle Appleton, Myles Axton, Arie Baak, Niklas Blomberg, et al. 2016. « The FAIR Guiding Principles for Scientific Data Management and Stewardship ». ''Scientific Data'' 3 (1): 160018. [https://doi.org/10.1038/sdata.2016.18 https://doi.org/10.1038/sdata.2016.18].
[[Catégorie:Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020 (livre)]]
qaw8kwee0mcfxnfa7n940c81ufoet4o
Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Feuille de route pour la science ouverte du Canada
0
82217
745330
745051
2025-06-24T23:26:15Z
LodestarChariot2
120009
Section de notes ajoutée
745330
wikitext
text/x-wiki
''Cette observation a été écrit par Caroline Winter, pour le Electronic Textual Cultures Laboratory et le partenariat Implementing New Knowledge Environments.''
En février 2020, le gouvernement du Canada a publié la [https://www.ic.gc.ca/eic/site/063.nsf/fra/h_97992.html ''Feuille de route pour la science ouverte''], un ensemble de principes et de recommandations pour guider la recherche scientifique fédérale au Canada. Plus précisément, les recommandations de la Feuille de route s'appliquent à la recherche des chercheurs employés par le gouvernement fédéral, ainsi qu'à la recherche contractée par les ministères et organismes fédéraux (gouvernement du Canada 7).
La ''Feuille de route'' définit la science ouverte comme « la pratique qui consiste à rendre les intrants, et les processus scientifiques librement accessibles à tous avec un minimum de restrictions », y compris des articles évalués par des pairs, des données de recherche et des communications destinées au public (11), et note que la recherche fédérale doit être « Ouvert par conception et par défaut » (7).
La ''Feuille de route'' a été élaborée dans le cadre des engagements à l'égard de la science ouverte énoncés dans le [https://ouvert.canada.ca/fr/contenu/plan-daction-national-du-canada-pour-un-gouvernement-ouvert-de-2018-2020 Plan d'action national du Canada pour un gouvernement ouvert de 2018-2020] (Gouvernement du Canada 2018). Il recoupe également d'autres politiques liées à la science ouverte, y compris la [https://www.tbs-sct.gc.ca/pol/doc-fra.aspx?id=28108 Directive sur le gouvernement ouvert], la [http://science.gc.ca/eic/site/063.nsf/fra/h_F6765465.html Politique des trois organismes sur le libre accès aux publications] et la [http://science.gc.ca/eic/site/063.nsf/fra/h_83F7624E.html Déclaration de principes des trois organismes sur la gestion des données numériques].
Reconnaissant leur importance pour la science ouverte, la ''Feuille de route'' est guidée par cinq principes : personnes, transparence, inclusivité, collaboration et durabilité (6).
En outre, la ''Feuille de route'' comprend 10 recommandations, résumées ici :
# La recherche scientifique financée par le gouvernement fédéral au Canada devrait être guidée par une approche scientifique ouverte (7).
# Les politiques et plans d'action devraient être élaborés en consultation avec la communauté scientifique (7).
# Les ministères et organismes fédéraux devraient avoir mis en place des plans d'action pour la science ouverte d'ici octobre 2020. Tous les résultats de la recherche devraient être « ouverts par conception et par défaut, » et les plans devraient aborder le partage des données existantes. Les éléments centralisés et communs des plans devraient être élaborés en collaboration, par exemple en ce qui concerne les délais, l'accessibilité, les langues officielles et les considérations informatiques (7).
# À compter de janvier 2022, tous les articles publiés dans des revues savantes devraient être mis en libre accès dès leur publication, et à compter de janvier 2023, toutes les publications fédérales aussi, éventuellement au moyen d'outils et d'une infrastructure partagés dans la communauté scientifique fédérale (8).
# Les stratégies de mise en œuvre des principes [https://www.go-fair.org/fair-principles/ FAIR (facile à trouver, accessible, interopérable et réutilisables)] pour les données de recherche et les métadonnées devraient être en place d'ici janvier 2023 et pleinement mises en œuvre d'ici janvier 2025. Cela nécessite des pratiques solides de gestion des données pour les données de recherche et les métadonnées et devrait être accompli en utilisant une approche commune au sein de la communauté scientifique fédérale (8).
# Le conseillére scientifique en chef devrait travailler avec la communauté scientifique fédérale pour élaborer un cadre permettant d'identifier les recherches qui ne devraient pas être rendues publiques en raison de problèmes d'éthique, de confidentialité ou de sécurité (9).
# Les stratégies élaborées en réponse à la feuille de route de [https://www.canada.ca/fr/conseil-prive/organisation/greffier/publications/strategie-donnees.html la Stratégie de données] de 2018, les plans d'action pour la science ouverte (recommandation 3) et aux directives du gouvernement ouvert devraient être bien alignées, sous la direction d'un principal des données scientifiques (9).
# Afin de parvenir à une stratégie nationale de science ouverte qui comprend des recherches financées par le gouvernement fédéral menées à l'extérieur des ministères et organismes fédéraux, un comité directeur de la science ouverte devrait être créé pour consulter les organismes de financement fédéraux, provinciaux et territoriaux et les sociétés savantes pour guider cette stratégie. (9).
# D'ici décembre 2021, la conseillére scientifique en chef devrait mener des consultations afin d'élaborer une stratégie scientifique ouverte pour la recherche financée par les organismes subventionnaires fédéraux, les bailleurs de fonds provinciaux et territoriaux et les sociétés savantes (9).
# La feuille de route et les stratégies et plans relatifs à la science ouverte doivent rester adaptés à l'évolution du contexte international de la science ouverte, qui devrait être surveillée par le conseiller scientifique en chef (10).
==Réponses à la feuille de route==
En mettant l’accent sur le fait de rendre la recherche scientifique canadienne « ouverte par conception et par défaut », la Feuille de route pour la science ouverte est bien alignée avec le mandat d’INKE de rechercher et de soutenir la science sociale ouverte. Il s’inscrit également dans le cadre de nombreux mandats de libre accès de nos partenaires et de nos membres.
Lors de la publication de la feuille de route, elle a été approuvée par les partenaires INKE sur Twitter. [https://twitter.com/carlabrc/status/1234873413145153537 L'ABRC–CARL (@carlabrc) a tweeté]<ref>Texte du tweet : "CARL welcomes the recent launch by @ChiefSciCan and @ISED_CA of a Roadmap for Open Science, which calls for Canada to adopt 'an #openscience approach to federally funded scientific and research outputs.' http://science.gc.ca/eic/site/063.nsf/eng/h_97992.html" </ref> à l'appui de la vision directrice de la Feuille de route, ajoutant que ils sont encouragés de voir incluse une recommandation qu’une stratégie de science ouverte doivent également être développés pour la recherche financée par le gouvernement fédéral menée en dehors des agences gouvernementales fédérales et les départements.
Gabriel Miller, président de la Fédération des sciences humaines et sociales, [https://twitter.com/gabrielbmiller/status/1233202618425651201 a tweeté ses félicitations pour la publication de la feuille de route],<ref>Texte du tweet : "Congrats @ChiefSciCan - we need integrated OS policies across gvt and this points that way. Good to see rec #9 calling for an OS plan for fed funded research - and even better to see learned societies named as key partners in that project. @ideas_idees"</ref> et a de même ajoutant le louange pour l’inclusion dans recommandation neuf à un plan de science ouvert pour la recherche financée par la gouvernement fédérale avec les sociétés savantes nommées partenaires de ce projet.
L'American Institute of Physics a noté la publication de la feuille de route dans son [https://www.aip.org/fyi/fyi-this-week/week-march-2-2020 bulletin hebdomadaire], soulignant que sa recommandation pour un accès libre immédiat est une dérogation à la politique de libre accèes des trois agences qui autorise un embargo de 12 mois (American Institute of Physics 2020).
En juillet, un article du [https://www.nationalobserver.com/2020/07/13/opinion/canadas-investments-science-are-more-important-ever ''National Observer du Canada''] a cité la Feuille de route comme exemple de l'investissement du Canada dans la science, un plus important que jamais face à la pandémie du COVID-19, au changement climatique et à d'autres défis complexes auxquels le monde est confronté ensemble (Bains 2020).
==La feuille de route et la science ouverte==
Dans le cadre de la stratégie nationale de science ouverte du Canada, la Feuille de route a également des implications pour la science ouvertes à l’échelle mondiale. Dans son annonce du déploiement de [https://cognit.ca/fr/ cognit.ca], un outil de recherche-découverte [https://francecanadaculture.org/fr/lancement-de-la-base-de-donnees-de-recherche-cognit-ca/ développé] par [http://u15.ca/fr le U15 Regroupement des universités de recherche du Canada], [https://www.univcan.ca/fr/ Universités Canada], [https://www.innovation.ca/fr la Fondation canadienne pour l'innovation (FCI)], les trois agences et [https://www.mitacs.ca/fr Mitacs], l'ambassade de France au Canada souligne l'importance de la Feuille de route et de la stratégie nationale de science ouverte du Canada pour faciliter la collaboration internationale (Ambassade de France au Canada 2020).
Bien que la ''Feuille de route pour la science ouverte'' ne s'applique qu'aux publications scientifiques, qu'elle définit comme incluant « les sciences fondamentales et appliquées, naturelles, physiques, biomédicales et sociales ainsi que de l’ingénierie et des mathématiques » (11), son soutien ferme à libre accès complète et immédiate suggère un changement dans la politique nationale qui pourrait également affecter d'autres disciplines à l'avenir.
==Notes==
{{reflist}}
==Ouvrages citées==
*Ambassade de France au Canada. 2020. « Lancement de la base de donnés de recherche Cognit.ca ». 31 mars 2020. https://francecanadaculture.org/fr/lancement-de-la-base-de-donnees-de-recherche-cognit-ca/.
*American Institute of Physics. 2020. ''FYI'', 2 mars 2020. [https://www.aip.org/fyi/fyi-this-week/week-march-2-2020 https://www.aip.org/fyi/fyi-this-week/week-march-2-2020].
*Bains, Navdeep. 2020. « Canada’s Investments in Science are More Important than Ever ». ''Canada’s National Observer''. 13 juillet 2020. [https://www.nationalobserver.com/2020/07/13/opinion/canadas-investments-science-are-more-important-ever https://www.nationalobserver.com/2020/07/13/opinion/canadas-investments-science-are-more-important-ever].
*Gouvernement du Canada. 2018. Plan d’action national du Canada pour un gouvernement ouvert de 2018–2020. 6 decembre 2018. https://ouvert.canada.ca/fr/contenu/plan-daction-national-du-canada-pour-un-gouvernement-ouvert-de-2018-2020.
*Gouvernement du Canada. 2020. Feuille de route pour la science ouverte. février 2020. https://www.ic.gc.ca/eic/site/063.nsf/fra/h_97992.html.
[[Catégorie:Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020 (livre)]]
jol8abrhgp538v4ve22w8979r1m7bgl
Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Mise à jour du Plan S : La Stratégie de conservation des droits
0
82218
745333
745052
2025-06-24T23:58:37Z
LodestarChariot2
120009
Liens mis à jour
745333
wikitext
text/x-wiki
''Cette observation a été écrit par Caroline Winter, pour le Electronic Textual Cultures Laboratory et le partenariat Implementing New Knowledge Environments.''
''Cette observation fait partie d'une série consacrée aux développements liés à la cOAlition S et au Plan S depuis leur lancement en septembre 2018.''
[https://www.coalition-s.org/ Plan S] est une initiative de la cOAlition S, un consortium international d'organisations de recherche et de financement. Il appelle à libre accès complet et immédiat de toutes les publications de recherche financées par des fonds publics à partir de janvier 2021 (voir « [[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Plan S et cOAlition S|Plan S et cOAlition S]] »).
En juillet 2020, cOAlition S a publié sa [https://www.coalition-s.org/coalition-s-develops-rights-retention-strategy/ Stratégie de conservation des droits] (« Rights Retention Strategy »). Avec cette stratégie, les organismes de financement demanderont aux chercheurs d'appliquer une licence Creative Commons Attribution 4.0 International (CC BY 4.0) à leur recherche avant qu'elle ne soit soumise pour publication (cOAlition S 2020a). Cela permettra aux chercheurs de conserver les droits intellectuels nécessaires pour partager les manuscrits acceptés par l'auteur ou les versions l’éditeur dans un dépôt libre accès lors de la publication, même lors de la publication dans une revue par abonnement ou hybride (voir Rooryck 2020).
Une [https://creativecommons.org/licenses/by/4.0/ licence CC BY] permet à quiconque d'utiliser et de modifier le matériel sous licence de la manière qui lui convient, y compris en le republiant commercialement, à condition de fournir une attribution, d'indiquer si des modifications ont été apportées et de n'appliquer aucune restriction technologique ou légale supplémentaire sur l'œuvre (Creative Commons sd-a; Creative Commons sd-b).
Bien que la décision de la cOAlition S puisse être considérée comme un échec et mat pour le mouvement libre accès (Lem 2020), les éditeurs ne sont pas obligés de publier les soumissions de la recherche financée par le Plan S. Ils pourraient simplement choisir de refuser les soumissions auxquelles la licence CC BY a été appliquée (Lem 2020; Van Noorden 2020).
==Réponse au RRS de la communauté INKE==
L'Association des bibliothèques de recherche du Canada (ABRC–CARL) a publié une [https://www.carl-abrc.ca/fr/nouvelles/reponse-annonce-de-strategie-de-conservation-des-droits-plan-s/?cn-reloaded=1 déclaration en faveur de la Stratégie], soulignant sa position selon laquelle les auteurs devraient conserver le droit d'auteur sur leur travail (CARL 2020), comme indiqué dans [https://www.carl-abrc.ca/wp-content/uploads/2019/02/ABRC_Re%CC%81ponse_au_PlanS_FR.pdf la réponse de l'ABRC aux lignes directrices révisées de mise en œuvre du Plan S] publiées en mai 2019. La déclaration note que le Stratégie libère les auteurs individuels de l'obligation de négocier leurs droits au niveau individuel et appelle cette stratégie mandatée par les bailleurs de fonds un « piste de solution importante et positive » (CARL 2020).
La déclaration fait également référence à [https://www.carl-abrc.ca/wp-content/uploads/2019/05/Addenda_auteur_can_FR_2019.pdf l'Addenda de l’auteur canadien à l'entente de publication] (adapté de [https://sparcopen.org/our-work/author-rights/#addendum l'addendum de l’auteurs SPARC]) et au [https://www.carl-abrc.ca/wp-content/uploads/2019/05/Guide_ABRC_droits_auteurs_FR_2019.pdf Guide des droits d'auteur de l'ABRC], qui ont tous deux été révisés en 2019 pour refléter les modifications apportées au droit d'auteur canadien et pour permettre aux auteurs de se conformer à la [http://www.science.gc.ca/eic/site/063.nsf/fra/h_F6765465.html Politique des trois organismes sur le libre accès aux publications] et politiques de libre accès des autres bailleurs de fonds.
Le Council of Australian University Librarians (CAUL) et l’Australian Open Access Strategy Group (AOASG) ont aussi publié [https://www.caul.edu.au/news/caul-and-aoasg-welcome-coalition-s-rights-retention-strategy une déclaration en faveur de la Stratégie]. Il note que’il s'aligne bien sur [https://www.caul.edu.au/sites/default/files/documents/fair-access/caul2019repositories-requirments.pdf l'approche a libre accès basée sur les dépôts] soutenue par le CAUL, l'AOASG, les universités ils représentées et [https://www.arc.gov.au/ l'Australian Research Council (ARC)], bien aussi [https://www.nhmrc.gov.au/ le National Health and Medical Research Council (NHMRC)] (CAUL et AOASG 2020).
La déclaration du CAUL / AOASG note également que le Stratégie s'aligne sur les conclusions du récent [https://www.caul.edu.au/sites/default/files/documents/fair-access/caul2020retaining-rights.pdf rapport de CAUL] sur la conservation des droits de propriété intellectuelle dans les œuvres savantes dans les universités australiennes (2020), qui propose des stratégies pour les universités afin de garantir que les chercheurs conservent des droits de réutilisation non exclusifs sur leurs œuvres.
==Réponse au RRS de la communauté de recherche==
De nombreuses autres organisations et parties prenantes ont également exprimé leur soutien au la Stratégie de conservation des droits, notamment
* [https://libereurope.eu/blog/2020/07/16/liber-supports-coalitions-rights-retention-strategy-to-ensure-open-access-to-publicly-funded-research/ l’Association of Research Libraries (ARL)]
* [https://www.cesaer.org/news/cesaer-welcomes-rights-retention-strategy-for-researchers-from-coalition-s-587/ CESAER]
* [https://www.eifl.net/news/eifl-welcomes-rights-retention-strategy-researchers l’Electronic Information for Libraries (EIFL)]
* [https://eua.eu/partners-news/539-coalition-s-presents-a-new-%E2%80%9Crights-retention-strategy%E2%80%9D-to-safeguard-researchers%E2%80%99-intellectual-ownership-rights.html l'European University Association (EUA)]
* [https://iarla.org/2020/07/iarla-supports-coalition-s-rights-retention-strategy/ l’International Alliance of Research Library Associations (IARLA)]
* [https://libereurope.eu/blog/2020/07/16/liber-supports-coalitions-rights-retention-strategy-to-ensure-open-access-to-publicly-funded-research/ le Ligue des Bibliothèques Européennes de Recherche (LIBER)]
* [http://doi.org/10.5281/zenodo.3945884 Le Marie Curie Alumni Association]
* [https://www.openaire.eu/openaire-supports-newly-announced-rights-retention-strategy-for-researchers l’OpenAIRE]
L'une des raisons invoquées dans ces déclarations pour soutenir le Statégie est qu'il habilite les auteurs. Par exemple, la déclaration de Creative Commons note que la publication sous un modèle libre accès entre en conflit avec le transfert des droits de l’auteur, et conserver leurs droits permet aux auteurs de partager leur résultats de recherche de mainière á bénéficier à la commuauté universitaire et à la société dans son ensemble (Vézina 2020).
À l'instar de l'ABRC, l’Association of Research Libraries (ARL) américain félicite le Stratégie pour avoir habilité les auteurs à conserver les droits sur leur travail sans avoir à négocier avec les éditeurs au niveau individuel (CARL 2020; Aiwuyor 2020; voir aussi EUA 2020 et Shawrav 2020).
Une autre raison de soutenir le Stratégie citée par l’ABRC, l’ARL et d'autres organisations est qu'il soutient particulièrement libre accès vert, ou le doumission d'œuvres dans des dépôts (CARL 2020; Aiwuyor 2020). Les déclarations [https://www.openaire.eu/openaire-supports-newly-announced-rights-retention-strategy-for-researchers d'OpenAIRE] et [https://www.eifl.net/news/eifl-welcomes-rights-retention-strategy-researchers d'EIFL], par exemple, notent que, car les licences Creative Commons étant déjà largement utilisées et reconnues, ainsi que lisibles par l'homme et la machine, la licence CC BY 4.0 rendra la recherche largement disponible, permettra une plus grande interopérabilité entre les dépôts, et permettre l'exploration de texte et de données (OpenAIRE sd; EIFL 2020).
La déclaration du [https://libereurope.eu/blog/2020/07/16/liber-supports-coalitions-rights-retention-strategy-to-ensure-open-access-to-publicly-funded-research/ LIBER] a souligné l'affirmation de la cOAlition S selon laquelle la pandémie de COVID-19 a démontré la nécessité urgente de la recherche en libre accès et relie le Stratégie à [https://libereurope.eu/blog/2020/07/10/update-on-call-for-urgent-copyright-action-to-support-distance-learning-research/ son propre appel à des changements de droits d'auteur] pour répondre au besoin de ressources d'apprentissage accessibles pendant la pandémie (LIBER 2020) (voir « [[Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020/Science Ouverte et COVID-19|Science ouverte et COVID-19]] »).
Dans l'ensemble, ces déclarations félicitent le Stratégie pour son audace. Par exemple, l’International Alliance of Research Library Associations (IARLA) – dont les membres comprennent l’ABRC, l'ARL, CAUL, LIBER et Research Libraries UK (RLUK) – déclare qu'avec le Stratégie, les bailleurs de fonds de la cOAlition S veulent effectuer un changement critique dans le paysage des licences qui garantirait que les auteurs soient en mesure de publier dans la revue de leur choix tout en rendant leur travail ouvertement accessible (IARLA 2020).
Cependant, certaines organisations parties prenantes expriment des inquiétudes au sujet du Stratégie et de sa mise en œuvre, même lorsqu'elles soutiennent le Plan S et / ou le libre accès en principe. Ces groupes comprennent :
* [https://www.alpsp.org/Reports-Publications/coalition-s-rights-retention-strategy-october-2020 Association of Learned and Professional Society Publishers (ALPSP)]
* [https://www.osa-opn.org/home/newsroom/2020/october/open_access_and_author_rights/ l'Optical Society (OSA)]
* [https://www.cambridge.org/core/blog/2020/10/19/openresearch-cups-response-to-the-rights-retention-strategy-from-coalition-s/ le Cambridge University Press]
Une question soulevée dans les déclarations organisationnelles ci-dessus concerne la manière dont le Stratégie affectera la viabilité financière des sociétés savantes et d'autres petits éditeurs s'ils ne sont pas en mesure de récupérer les coûts de production des manuscrits acceptés par l'auteur. Une autre est la préoccupation connexe que le Stratégie limitera le choix du lieu de publication des auteurs (ALPSP 2020; Taylor 2020).
Un autre problème soulevé est que les auteurs peuvent se trouver dans des situations juridiques complexes, liés par les politiques contradictoires de leurs bailleurs de fonds et de leurs éditeurs (ALPSP 2020; Day 2020).
Un troisième problème soulevé dans ces déclarations est la crainte que, en facilitant le libre accès vert, le Stratégie puisse avoir pour effet involontaire de saper d'autres routes, en particulier la libre accès d’or (ALPSP 2020; Day 2020).
Dans un article qui a suscité une discussion animée sur [https://scholarlykitchen.sspnet.org/2020/07/20/coalition-ss-rights-confiscation-strategy-continues/ ''The Scholarly Kitchen''], Rick Anderson fait part de ses préoccupations au sujet de la licence CC BY elle-même. Le décrivant le Stratégie come une confiscation des droits, il fait valoir que, bien que la licence CC BY permette techniquement à l'auteur de conserver le droit d'auteur sur son œuvre, cette œuvre est effectivement dans le domaine public et l'auteur ne peut pas l'empêcher d'être modifiée ou reproduit. Il note, par exemple, que les auteurs peuvent ne pas souhaiter que leur travail soit traduit sans leur autorisation, et cite des exemples de travaux d'un auteur [https://scholarlykitchen.sspnet.org/2014/03/31/cc-by-copyright-and-stolen-advocacy/ publiés sans leur consentement dans des publications haineuses] et [http://rrresearch.fieldofscience.com/2013/07/apple-academic-press-predatory.html republiés dans une publication commerciale] (Anderson 2020).
D'autres parties prenantes ont exprimé des préoccupations liées à la pertinence de la licence CC BY dans différents contextes disciplinaires. Par exemple, dans sa [https://www.carl-abrc.ca/fr/nouvelles/reponse-a-plan-s/ réponse aux directives de mise en œuvre originales du Plan S] (qui ont été [https://www.coalition-s.org/revised-implementation-guidance/ révisées en mai 2019]), l'ABRC convient que les auteurs devraient conserver le droit d'auteur sur leur travail, mais recommande que les auteurs soient autorisés à choisir la licence CC qui correspond le mieux à leurs besoins, plutôt que de se limiter à la licence CC BY 4.0 (CARL 2019).
Dans l'ensemble, ces préoccupations font ressortir l'idée qu'une stratégie unique peut ne pas convenir à une communauté de recherche mondiale avec différents niveaux de ressources et de financement, ainsi que des pratiques disciplinaires variées (Taylor 2020; Day 2020).
==Implications pour la science ouverte==
Comme la cOAlition S devient de plus en plus internationale, les développements du Plan S et de ses stratégies connexes – y compris le Stratégie de conservation des droits – intéressent la communauté mondiale de la recherche. De plus, la discussion sur les avantages et les inconvénients de la licence CC BY 4.0 et le rôle des bailleurs de fonds dans la détermination de la licence qui s'applique à leurs œuvres est pertinente pour la communauté de la science ouverte plus largement.
==Ouvrages citées==
*ABRC (Association des bibliothèques de recherche du Canada). 2019. « Réponse de l’ABRC à Plan S ». 8 février 2019. [https://www.carl-abrc.ca/fr/nouvelles/reponse-a-plan-s/ https://www.carl-abrc.ca/fr/nouvelles/reponse-a-plan-s/].
*ABRC (Association des bibliothèques de recherche du Canada). 2020. « L’ABRC se réjouit de l’annonce de la Stratégie de conservation des droits adoptée par la cOAlition S ». 16 juillet 2020. [https://www.carl-abrc.ca/fr/nouvelles/reponse-annonce-de-strategie-de-conservation-des-droits-plan-s/ https://www.carl-abrc.ca/fr/nouvelles/reponse-annonce-de-strategie-de-conservation-des-droits-plan-s/].
*Aiwuyor, Jessica. 2020. « ARL Welcomes cOAlition S Rights Retention Strategy Calling for Open Access to Results of Funded Research ». Association of Research Libraries, 15 juillet 2020. [https://www.arl.org/news/arl-welcomes-coalitions-rights-retention-strategy-calling-for-open-access-to-results-of-funded-research/ https://www.arl.org/news/arl-welcomes-coalitions-rights-retention-strategy-calling-for-open-access-to-results-of-funded-research/].
*ALPSP (Association of Learned and Professional Society Publishers). 2020. « cOAlition S and the Rights Retention Strategy – Response from ALPSP ». 7 octobre 2020. [https://www.alpsp.org/Reports-Publications/coalition-s-rights-retention-strategy-october-2020 https://www.alpsp.org/Reports-Publications/coalition-s-rights-retention-strategy-october-2020].
*Anderson, Rick. 2020 « cOAlition S’s Rights Confiscation Strategy Continues ». The Scholarly Kitchen. 20 juillet 2020. [https://scholarlykitchen.sspnet.org/2020/07/20/coalition-ss-rights-confiscation-strategy-continues/ https://scholarlykitchen.sspnet.org/2020/07/20/coalition-ss-rights-confiscation-strategy-continues/].
*CAUL (Council of Australian University Librarians) et AOASG (Australasian Open Access Strategy Group). 2020. « CAUL and AOASG Welcome cOAlition S Rights Retention Strategy ». [https://www.caul.edu.au/news/caul-and-aoasg-welcome-coalition-s-rights-retention-strategy https://www.caul.edu.au/news/caul-and-aoasg-welcome-coalition-s-rights-retention-strategy].
*cOAlition S. 2020a. « Plan S: Rights Retention Strategy ». ''Analysis & Policy Observatory'', 15 juillet 2020. [https://apo.org.au/node/307168 https://apo.org.au/node/307168].
*CAUL (Council of Australian University Librarians). 2020. ''Intellectual Property Rights Retention in Scholarly Works at Australian Universities''. [https://www.caul.edu.au/sites/default/files/documents/fair-access/caul2020retaining-rights.pdf https://www.caul.edu.au/sites/default/files/documents/fair-access/caul2020retaining-rights.pdf].
*Creative Commons. sd-a « Attribution 4.0 International (CC BY 4.0) ». Creative Commons. [https://creativecommons.org/licenses/by/4.0/ https://creativecommons.org/licenses/by/4.0/].
*Creative Commons. sd-b « About the Licenses ». Creative Commons, [https://creativecommons.org/licenses/ https://creativecommons.org/licenses/].
*Day, Matt. « CUP’s Response to the Rights Retention Strategy from cOAlition S ». Cambridge Core Blog. 19 octobre 2020. [https://www.cambridge.org/core/blog/2020/10/19/openresearch-cups-response-to-the-rights-retention-strategy-from-coalition-s/ https://www.cambridge.org/core/blog/2020/10/19/openresearch-cups-response-to-the-rights-retention-strategy-from-coalition-s/.]
*EIFL (Electronic Information for Libraries). 2020. « EIFL Welcomes Rights Retention Strategy for Researchers ». 15 juillet 2020. [https://www.eifl.net/news/eifl-welcomes-rights-retention-strategy-researchers https://www.eifl.net/news/eifl-welcomes-rights-retention-strategy-researchers].
*EUA (European University Association). 2020. « cOAlition S Presents a New ‘Rights Retention Strategy’ to Safeguard Researchers’ Intellectual Ownership Rights ». 16 juillet 2020. [https://eua.eu/partners-news/539-coalition-s-presents-a-new-%E2%80%9Crights-retention-strategy%E2%80%9D-to-safeguard-researchers%E2%80%99-intellectual-ownership-rights.html https://eua.eu/partners-news/539-coalition-s-presents-a-new-%E2%80%9Crights-retention-strategy%E2%80%9D-to-safeguard-researchers%E2%80%99-intellectual-ownership-rights.html].
*IARLA (International Alliance of Research Library Associations). 2020. « IARLA supports cOAlition S Rights Retention Strategy ». 23 juillet 2020. [https://iarla.org/2020/07/iarla-supports-coalition-s-rights-retention-strategy/. https://iarla.org/2020/07/iarla-supports-coalition-s-rights-retention-strategy/.]
*Lem, Pola. 2020. « Plan S Funders Present ‘Bold’ Rights-Retention Strategy ». ''Research Professional News''. 15 juillet 2020. [https://www.researchprofessionalnews.com/rr-news-europe-infrastructure-2020-7-plan-s-funders-present-bold-rights-retention-strategy/ https://www.researchprofessionalnews.com/rr-news-europe-infrastructure-2020-7-plan-s-funders-present-bold-rights-retention-strategy/].
*LIBER (Association of European Research Libraries). 2020. « LIBER Supports cOAlitionS ‘Rights Retention Strategy’ to Ensure Open Access to Publicly-Funded Research ». 16 juillet 2020. [https://libereurope.eu/blog/2020/07/16/liber-supports-coalitions-rights-retention-strategy-to-ensure-open-access-to-publicly-funded-research/ https://libereurope.eu/blog/2020/07/16/liber-supports-coalitions-rights-retention-strategy-to-ensure-open-access-to-publicly-funded-research/].
*OpenAIRE. sd. « OpenAIRE Supports Newly Announced Rights Retention Strategy for Researchers ». [https://www.openaire.eu/openaire-supports-newly-announced-rights-retention-strategy-for-researchers https://www.openaire.eu/openaire-supports-newly-announced-rights-retention-strategy-for-researchers].
*Rooryck, Johan. 2020. « Opinion: The cOAlition S Rights Retention Strategy ». cOAlition S, juillet 2020. [https://www.coalition-s.org/opinion-rights-retention-strategy/ https://www.coalition-s.org/opinion-rights-retention-strategy/].
*Shawrav, Mostata Moonir. 2020. « Marie Curie Alumni Association Welcomes Rights Retention for Researchers ». 15 juillet 2020. [http://doi.org/10.5281/zenodo.3945884 http://doi.org/10.5281/zenodo.3945884].
*Taylor, Alision. 2020. « Open Access and Author Rights ». ''Optics & Photonics News''. 5 octobre 2020. [https://www.osa-opn.org/home/newsroom/2020/october/open_access_and_author_rights/ https://www.osa-opn.org/home/newsroom/2020/october/open_access_and_author_rights]/.
*Vézina, Brigitte. 2020. « Why cOAlition S’ Rights Retention Strategy Protects Researchers ». Creative Commons, 19 août 2020. [https://creativecommons.org/2020/08/19/why-coalition-s-rights-retention-strategy-protects-researchers/ https://creativecommons.org/2020/08/19/why-coalition-s-rights-retention-strategy-protects-researchers/].
*Van Noorden, Richard. 2020. « Open-Access Plan S to Allow Publishing in Any Journal ». ''Nature'', 16 juillet 2020. [https://www.nature.com/articles/d41586-020-02134-6 https://www.nature.com/articles/d41586-020-02134-6].
[[Catégorie:Observations préliminaires : Observatoire des politiques d'Érudition ouverte, 2017-2020 (livre)]]
8u6a5spkop95ze667ohdcgpt986ca7q
Mathc initiation/001K
0
82531
745344
2025-06-25T09:29:55Z
Xhungab
23827
news
745344
wikitext
text/x-wiki
__NOTOC__
[[Catégorie:Mathc initiation (livre)]]
:
[[Mathc initiation/a08| Sommaire]]
:
{{Partie{{{type|}}}|Dérivation logarithmique}}
:
La dérivée d'une fonction formé de produits et de quotients peut être difficile à calculer.
La dérivation logarithmique peut en 4 étapes simplifier le travail.
* 1) Prendre le logarithme de l'équation :
y = f(x) donne ln(y) = ln(f(x))
* 2) Utiliser les propriétés de la fonction ln() sur ln(f(x))
* 3) Dériver l'équation :
ln(y) = ln(fx) donne 1/y dy/dx = ln(f(x))'
* 4) Donner la solution
1/y dy/dx = ln(f(x))' donne dy/dx = y ln(f(x))'
:
.
:
Copier la bibliothèque dans votre répertoire de travail :
* [[Mathc initiation/Fichiers h : c76a1|x_hfile.h ............. Déclaration des fichiers h]]
* [[Mathc initiation/Fichiers h : c30a2|x_def.h .............. Déclaration des utilitaires]]
* [[Mathc initiation/Fichiers h : c23a3|x_fx.h ................ Calculer la dérivé première et seconde]]
Examples :
* [[Mathc initiation/001G| fa.h ]] ..... ..... ..... [[Mathc initiation/001H| c00a.c ]]
* [[Mathc initiation/001I| fb.h ]] ..... ..... ..... [[Mathc initiation/001J| c00b.c ]]
{{AutoCat}}
2ii0im1pkimoiswpddn1l3p7ga3ji8a
Mathc initiation/001H
0
82532
745346
2025-06-25T09:42:54Z
Xhungab
23827
news
745346
wikitext
text/x-wiki
[[Catégorie:Mathc initiation (livre)]]
[[Mathc initiation/001K| Sommaire]]
Installer et compiler ces fichiers dans votre répertoire de travail.
{{Fichier|c00a.c|largeur=70%|info=|icon=Crystal128-source-c.svg}}
<syntaxhighlight lang="c">
/* --------------------------------- */
/* save as c00a.c */
/* --------------------------------- */
#include "x_hfile.h"
#include "fa.h"
/* --------------------------------- */
int main(void)
{
double c = 2.5;
clrscrn();
printf(" f : x-> %s\n\n"
" Df : x-> %s\n\n\n", feq, Dfeq);
printf(" Compute the derivative of f when x = %0.3f\n\n", c);
printf(" with Df(%0.3f) = %0.8f \n",c, Df(c));
printf(" with fx_x(%0.3f) = %0.8f\n\n\n",c, fx_x(f,c,H));
stop();
return 0;
}
/* ---------------------------------- */
/* ---------------------------------- */
</syntaxhighlight>
Calculons la dérivé de la fonction f :
'''Exemple de sortie écran :'''
<syntaxhighlight lang="C">
f : x-> (2x**2+1)**sin(x)
Df : x-> [(2x**2+1)**sin(x)] [cos(x) ln(2x**2+1) + sin(x) (4x)/(2x**2+1)]
Compute the derivative of f when x = 2.500
with Df(2.500) = -7.79466818
with fx_x(2.500) = -7.79466809
Press return to continue.
</syntaxhighlight>
'''Calculons la dérivé :'''
<syntaxhighlight lang="C">
Utilisons les logarithmes pour dériver y :
y = (2x**2+1)**sin(x)
ln(y) = ln((2x**2+1)**sin(x))
ln(y) = sin(x) ln( 2x**2+1)
1/y dy/dx = [cos(x) ln(2x**2+1) + sin(x) (4x)/(2x**2+1)]
dy/dx = y [cos(x) ln(2x**2+1) + sin(x) (4x)/(2x**2+1)]
dy/dx = [(2x**2+1)**sin(x)] [cos(x) ln(2x**2+1) + sin(x) (4x)/(2x**2+1)]
</syntaxhighlight>
{{AutoCat}}
852ah2qa5s91zy5lqomqebc5u0jmmcd
745350
745346
2025-06-25T10:11:11Z
Xhungab
23827
745350
wikitext
text/x-wiki
[[Catégorie:Mathc initiation (livre)]]
[[Mathc initiation/001K| Sommaire]]
Installer et compiler ces fichiers dans votre répertoire de travail.
{{Fichier|c00a.c|largeur=70%|info=|icon=Crystal128-source-c.svg}}
<syntaxhighlight lang="c">
/* --------------------------------- */
/* save as c00a.c */
/* --------------------------------- */
#include "x_hfile.h"
#include "fa.h"
/* --------------------------------- */
int main(void)
{
double c = 5.;
clrscrn();
printf(" f : x-> %s\n\n"
" Df : x-> %s\n\n\n", feq, Dfeq);
printf(" Compute the derivative of f when x = %0.3f\n\n", c);
printf(" with Df(%0.3f) = %0.8f \n",c, Df(c));
printf(" with fx_x(%0.3f) = %0.8f\n\n\n",c, fx_x(f,c,H));
stop();
return 0;
}
/* ---------------------------------- */
/* ---------------------------------- */
</syntaxhighlight>
Calculons la dérivé de la fonction f :
'''Exemple de sortie écran :'''
<syntaxhighlight lang="C">
f : x-> (2x**2+1)**sin(x)
Df : x-> [(2x**2+1)**sin(x)] [cos(x) ln(2x**2+1) + sin(x) (4x)/(2x**2+1)]
Compute the derivative of f when x = 5.
with Df(5.000) = 0.01703600
with fx_x(5.000) = 0.01703600
Press return to continue.
</syntaxhighlight>
'''Calculons la dérivé :'''
<syntaxhighlight lang="C">
Utilisons les logarithmes pour dériver y :
y = (2x**2+1)**sin(x)
ln(y) = ln((2x**2+1)**sin(x))
ln(y) = sin(x) ln( 2x**2+1)
1/y dy/dx = [cos(x) ln(2x**2+1) + sin(x) (4x)/(2x**2+1)]
dy/dx = y [cos(x) ln(2x**2+1) + sin(x) (4x)/(2x**2+1)]
dy/dx = [(2x**2+1)**sin(x)] [cos(x) ln(2x**2+1) + sin(x) (4x)/(2x**2+1)]
</syntaxhighlight>
{{AutoCat}}
6g2y2s0hm5uhm266xnfxg52l8ow3wse
Mathc initiation/001J
0
82533
745347
2025-06-25T09:55:53Z
Xhungab
23827
news
745347
wikitext
text/x-wiki
[[Catégorie:Mathc initiation (livre)]]
[[Mathc initiation/001K| Sommaire]]
Installer et compiler ces fichiers dans votre répertoire de travail.
{{Fichier|c00bc|largeur=70%|info=|icon=Crystal128-source-c.svg}}
<syntaxhighlight lang="c">
/* --------------------------------- */
/* save as c00b.c */
/* --------------------------------- */
#include "x_hfile.h"
#include "fb.h"
/* --------------------------------- */
int main(void)
{
double c = PI;
clrscrn();
printf(" f : x-> %s\n\n"
" Df : x-> %s\n\n\n", feq, Dfeq);
printf(" Compute the derivative of f when x = %0.3f\n\n", c);
printf(" with Df(%0.3f) = %0.8f \n",c, Df(c));
printf(" with fx_x(%0.3f) = %0.8f\n\n\n",c, fx_x(f,c,H));
stop();
return 0;
}
/* ---------------------------------- */
/* ---------------------------------- */
</syntaxhighlight>
Calculons la dérivé de la fonction f :
'''Exemple de sortie écran :'''
<syntaxhighlight lang="C">
f : x-> [x sqrt(2x+1)]/[exp(x) cos(x)]
Df : x-> [[x sqrt(2x+1)]/[exp(x) cos(x)]] [1/x + 1/(2x+1)-1+(sin(x)/cos(x))]
Compute the derivative of f when x = 3.142
with Df(3.142) = 0.19945383
with fx_x(3.142) = 0.19945383
Press return to continue.
</syntaxhighlight>
'''Calculons la dérivé :'''
<syntaxhighlight lang="C">
Utilisons les logarithmes pour dériver y :
y = [ x sqrt(2x+1)] / [exp(x) cos(x)]
ln(y) = ln([ x sqrt(2x+1)] / [exp(x) cos(x)])
ln(y) = ln(x) + 1/2 ln(2x+1) - ( x + ln(cos(x)))
1/y dy/dx = [1/x + 1/(2x+1) - 1 - (-sin(x)/cos(x))]
dy/dx = y [1/x + 1/(2x+1) - 1 + sin(x)/cos(x) ]
dy/dx = [[x sqrt(2x+1)] / [exp(x) cos(x)]] [1/x + 1/(2x+1) - 1 + sin(x)/cos(x) ]
</syntaxhighlight>
{{AutoCat}}
jybvu6nbcw336bo2rjhizfjt23peytw
745351
745347
2025-06-25T10:16:40Z
Xhungab
23827
745351
wikitext
text/x-wiki
[[Catégorie:Mathc initiation (livre)]]
[[Mathc initiation/001K| Sommaire]]
Installer et compiler ces fichiers dans votre répertoire de travail.
{{Fichier|c00bc|largeur=70%|info=|icon=Crystal128-source-c.svg}}
<syntaxhighlight lang="c">
/* --------------------------------- */
/* save as c00b.c */
/* --------------------------------- */
#include "x_hfile.h"
#include "fb.h"
/* --------------------------------- */
int main(void)
{
double c = 4.;
clrscrn();
printf(" f : x-> %s\n\n"
" Df : x-> %s\n\n\n", feq, Dfeq);
printf(" Compute the derivative of f when x = %0.3f\n\n", c);
printf(" with Df(%0.3f) = %0.8f \n",c, Df(c));
printf(" with fx_x(%0.3f) = %0.8f\n\n\n",c, fx_x(f,c,H));
stop();
return 0;
}
/* ---------------------------------- */
/* ---------------------------------- */
</syntaxhighlight>
Calculons la dérivé de la fonction f :
'''Exemple de sortie écran :'''
<syntaxhighlight lang="C">
f : x-> [x sqrt(2x+1)]/[exp(x) cos(x)]
Df : x-> [[x sqrt(2x+1)]/[exp(x) cos(x)]] [1/x + 1/(2x+1)-1+(sin(x)/cos(x))]
Compute the derivative of f when x = 4
with Df(4.000) = -0.17449102
with fx_x(4.000) = -0.17449102
Press return to continue.
</syntaxhighlight>
'''Calculons la dérivé :'''
<syntaxhighlight lang="C">
Utilisons les logarithmes pour dériver y :
y = [ x sqrt(2x+1)] / [exp(x) cos(x)]
ln(y) = ln([ x sqrt(2x+1)] / [exp(x) cos(x)])
ln(y) = ln(x) + 1/2 ln(2x+1) - ( x + ln(cos(x)))
1/y dy/dx = [1/x + 1/(2x+1) - 1 - (-sin(x)/cos(x))]
dy/dx = y [1/x + 1/(2x+1) - 1 + sin(x)/cos(x) ]
dy/dx = [[x sqrt(2x+1)] / [exp(x) cos(x)]] [1/x + 1/(2x+1) - 1 + sin(x)/cos(x) ]
</syntaxhighlight>
{{AutoCat}}
jy9mrs8uxdb5ueeccxpz4vq4zbvjcyg
Mathc initiation/001G
0
82534
745348
2025-06-25T10:00:30Z
Xhungab
23827
news
745348
wikitext
text/x-wiki
[[Catégorie:Mathc initiation (livre)]]
[[Mathc initiation/001K| Sommaire]]
Installer ce fichier dans votre répertoire de travail.
{{Fichier|fa.h|largeur=70%|info=|icon=Crystal Clear mimetype source h.png}}
<syntaxhighlight lang="c">
/* --------------------------------- */
/* save as fa.h */
/* --------------------------------- */
/* --------------------------------- */
double f(
double x)
{
return(pow((2*x*x+1),sin(x)));
}
char feq[] = "(2x**2+1)**sin(x)";
/* --------------------------------- */
double Df(
double x)
{
return( f(x)*(cos(x)*log(2*x*x+1) + sin(x)*(4*x)/(2*x*x+1)) );
}
char Dfeq[] = "[(2x**2+1)**sin(x)] [cos(x) ln(2x**2+1)"
" + sin(x) (4x)/(2x**2+1)]";
/* --------------------------------- */
/* --------------------------------- */
</syntaxhighlight>
{{AutoCat}}
559n24f9bo8u2gxbycbo0xjtgranteb
Mathc initiation/001I
0
82535
745349
2025-06-25T10:02:36Z
Xhungab
23827
news
745349
wikitext
text/x-wiki
[[Catégorie:Mathc initiation (livre)]]
[[Mathc initiation/001K| Sommaire]]
Installer ce fichier dans votre répertoire de travail.
{{Fichier|fb.h|largeur=70%|info=|icon=Crystal Clear mimetype source h.png}}
<syntaxhighlight lang="c">
/* --------------------------------- */
/* save as fb.h */
/* --------------------------------- */
/* --------------------------------- */
double f(
double x)
{
return((x*sqrt(2*x+1)) / (exp(x)*cos(x)));
}
char feq[] = "[x sqrt(2x+1)]/[exp(x) cos(x)]";
/* --------------------------------- */
double Df(
double x)
{
return( f(x)*(1/x + 1/(2*x+1) - 1 + (sin(x)/cos(x))) );
}
char Dfeq[] = "[[x sqrt(2x+1)]/[exp(x) cos(x)]]"
" [1/x + 1/(2x+1)-1+(sin(x)/cos(x))] ";
/* --------------------------------- */
/* --------------------------------- */
</syntaxhighlight>
{{AutoCat}}
2pp3o5xx8mplqvqhtomjtibpsrktlij